How to update a document using the Realm Android SDK?

Hello,
I would like to perform the following functionality but from Realm using Android/Kotlin as frontend.

Here it is:

db.getCollection('sessions').update(
{'_id' : ObjectId("5f5a3661d00ba84ba0247878")},
    {
        $set:
        {
            "rates.native.stars": NumberInt(3),
            "rates.native.details": "abcde"
        }
    }
)

So I tried the following:

realm = Realm.getDefaultInstance()
        realm.executeTransaction {
            val item = it.where<session>().equalTo("_id", ObjectId("5f5a3661d00ba84ba0247878")).findFirst()
            item?.rates?.native?.stars = 3
        }
        realm.close()

This didn’t work because the executeTransaction seems to work only on existing fields in the document and doesn’t insert/update new ones. In my case “stars” does not exist and I want to insert it.

Exactly as I am doing in the $set method. But I want something for Kotlin/Realm

Then I tried this one:

val user: User? = taskApp.currentUser()
        val mongoClient : MongoClient? = user?.getMongoClient("myservicename")
        val mongoDatabase : MongoDatabase? = mongoClient?.getDatabase("mydbname")
        val mongoCollection : MongoCollection<Document>? = mongoDatabase?.getCollection("sessions")

        val queryFilter : Document = Document("_id", ObjectId("5f5a3661d00ba84ba0247878"))
        val updateDocument : Document = Document("rates.native.stars",  3)
            .append("rates.native.details", "abcde")

        mongoCollection?.updateOne(queryFilter, updateDocument)?.addOnCompleteListener {
            if (it.isSuccessful) {
                val count : Long = it.result.modifiedCount
                if (count == 1L) {
                    Log.v("EXAMPLE", "successfully updated a document.")
                } else {
                    Log.v("EXAMPLE", "did not update a document.")
                }
            } else {
                Log.e("EXAMPLE", "failed to update a document with: ${it.exception}")
            }
        }

This one also didn’t work. I am getting that the document failed to update and that the update is not permitted. I did enable read and write from the Sync UI so that users can read and write but I am still getting this error.

Note that in the documentation, it shows:

mongoCollection?.updateOne(queryFilter, updateDocument)?.getAsync …

but this function is not loading in Android Studio. so I am use onCompleteListener instead.

Does anybody know how to convert the below function into a function that can be called from Android/kotlin using the Realm/Sync.

db.getCollection('sessions').update(
{'_id' : ObjectId("5f5a3661d00ba84ba0247878")},
    {
        $set:
        {
            "rates.native.stars": NumberInt(3),
            "rates.native.details": "abcde"
        }
    }
)

@Maz So the Realm Schema on the mobile app is generally static so you won’t be able to use sync to insert new fields that replicate to the other side. You can iterate on the schema and extend it as you develop and out in product - but generally that is a new build of the app with a new RealmObject class definition which include the new field.

The second method of using the MongoDB APIs is how I would recommend solving this - can you share more logs you are getting on the client side and the corresponding logs on the serverside?

Hi Ian,

It is well noted that the ‘realm.executeTransaction’ is static. So it won’t work in this case.

But what about the second one. This one should work, right?

mongoCollection?.updateOne(queryFilter, updateDocument)?.addOnCompleteListener …

I am not really getting too much information from the logs.

From the client side, I am just getting

failed to update a document with: SERVICE_UNKNOWN(realm::app::ServiceError:-1): update not permitted

and from the Realm UI logs, I am getting the following error:

SchemaValidationFailedWrite Error
update not permitted for document with _id: ObjectID(“5f5a3661d00ba84ba0247878”) : could not validate document: (root): _partition is required

The _partition is already there in the document. The proof is that I can already access this document from the realm.executeTransaction but as it is static, I am not able to insert new fields. And as realm.executeTransaction is a Sync related, _partition is a must for the Sync to even work.

I also tried to run the Schema validation again and there was no error:
Logs:

[
  "Performing schema validation",
  "Namespace: test.sessions",
  "Limit: 1000",
  "Examined 823 document(s)",
  "0 of 823 documents failed schema validation",
  "Completed app request"
]

Also, let me add that the users are logged in using custom login function. I don’t think this should be an issue.

I also run those functions on the user

user = taskApp.currentUser()!!
val islogged = user.isLoggedIn
val apiAuth = user.apiKeyAuth
val token = user.accessToken

And all those functions gave valid results.

I tried this function because I saw that the user may not be authenticated.

{“error”:“must authenticate first”,“link”:“https://realm.mongodb.com/groups/5d778478f2a30b915a98779f/apps/5f4f90f0d94945435390d6a4/logs?co_id=5f5c994affaf8bc725173eac”}

I am getting this from the POST request on the client side. Though the user should be authenticated from both the client side as well as the Realm UI.

What do you think?

Let me add that one thing. I believe the user not permitted reason is not valid. Possibly because I was clicking on the POST link, I am considered to be an external user and this is creating ‘authentication error’. So don’t bother about this reason.
I think the others should give clear directions.
Thank you.

Thanks this is helpful. Let me investigate the behavior on the cloud side and get back to you

As a workaround, you can create a Realm Cloud function that takes a document as user input and then inserts it into the collection on the backend. I believe this should work.

Hi Ian,

Yes indeed. The custom functions in the UI can solve the problem.
But please check this error as it not really convenient to keep creating custom functions and link them.
Thank you

@Maz So this appears to be working as expected. When enabling sync - sync permissions take precedence. This also means that any mutations made from clients logged into that realm cloud app need to follow the syncing schema.

If you are trying to use rules on a separate collection or allow that is not part of sync or use different schemas what you can do is create a separate sever-side Realm App which is just for your web or other API traffic but connect it to the the same MongoDB Atlas cluster as the Realm Sync app. You can apply your rules there. You can see an example of this here -

We realize this is a workaround and are looking to unify the permissions system in the near future.