realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.45k stars 1.75k forks source link

Wrong result when using x == NONE {y} #7862

Closed jd565 closed 4 months ago

jd565 commented 6 months ago

How frequently does the bug occur?

Always

Description

Am writing queries using the RQL syntax, and a couple of our queries are filtering out items that don't exist in a list. However, if we put in a list of a single item then the wrong item is selected.

fun cleanup(ids: List<Int>) {
    val queryIds = ids.joinToString(prefix = "{", postfix = "}", separator = ",")
    realm.where<RealmItem>()
        .rawPredicate("memberId == NONE $queryIds")
        .findAll()
        .deleteAllFromRealm()
}

If I have ids 100, 101 and 102 in realm, and run this passing in listOf(100, 101), then it correctly deletes 100 and 101 leaving 102, but if I pass in listOf(100), then it does not delete the item with id 100. Have also tried hardcoding the ids in my test "memberId == NONE {100}" and see the same behaviour.

I am new to RQL so could be doing something wrong but this seems like incorrect behaviour to me.

Stacktrace & log output

No response

Can you reproduce the bug?

Always

Reproduction Steps

No response

Version

10.17.0

What Atlas App Services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

Android all versions

Build environment

Android Studio version: Iguana Canary 18 Android Build Tools version: 8.1.2 Gradle version: 8.4

kneth commented 4 months ago

@jd565 Thank you for taking the time to report.

@ironage Do you have any ideas?

ironage commented 4 months ago

Hi @jd565 Assuming 3 objects with member_ids of 100, 101, 102 then we'd expect the following matches for these queries:

query result objects by member_id
member_id == NONE {100, 101, 102} no results
member_id == NONE {100, 101} 102
member_id == NONE {100} 101, 102
member_id == ANY {100, 101, 102} 100, 101, 102
member_id == ANY {100, 101} 100, 101
member_id == ANY {100} 100

We can read member_id == NONE {100, 101, 102} as "match all members who have a member_id that equals none of {100, 101, 102}". From your expectations, I think you are looking to use the ANY query instead of NONE. Let me know if that works for you.

jd565 commented 4 months ago

@ironage Thanks for taking a look. The table above is not the behaviour I am seeing. I am inserting 100, 101 and 102 ids into the table, then have this code in a test:

        val nonMatching = realm.where<RealmProfile>()
            .rawPredicate("${RealmProfile.MEMBER_ID} == NONE {100}")
            .findAll()
            .map { it.memberID }
        assertEquals(listOf(101, 102), nonMatching)

that is, I want to find all the profiles without member id 100, expecting this to return just the members with id 101 and 102, but my test fails with the following:

java.lang.AssertionError: expected:<[101, 102]> but was:<[100]>

Suggesting that the raw predicate is not doing what you have outlined in your message above.

If I have multiple items inside the NONE, like NONE {100, 101}, then the query correctly returns only member 102, so it seems to only be a problem when a single element is inside the NONE query

ironage commented 4 months ago

@jd565 thanks for the clarification. I wasn't able to reproduce, until I add an index to test property (this applies to you if member_id is a primary key or if you have added an index manually). I submitted a fix in https://github.com/realm/realm-core/pull/7333, but until that lands, I'd suggest that you change your query to be member_id != ALL {...} which should produce equivalent results for the time being. Thanks for reporting this bug to us!