How to be notifed that docuemnt is changed but liveQuery's query isn't catching it anymore?

Hi everyone,
I need some assitance.
Let’s say that I have a liveQuery running with query ‘Q’.
Let’s say that a document ‘D’ is inserted to the DB that answers query ‘Q’ - so change() is called.
Let’s say document ‘D’ is updated and now it’s not answering query ‘Q’ - how can I be notifed that document ‘D’ is updated ? change() isn’t called anymore…

Thanks

What is your Query ? Any time the database is updated (inserts, deletes, updates) that affects the results on your query, the change listener should be called.

You can’t be notified in that situation via query listener. It sounds like you should add a document change listener to the database instead. Query change listeners will only inform about changes to the query.

That is not quite true, if the query’s where expression is something like Expression.property(“name”).equalsTo(“doc”)
and I save a document with doc.setValue(“name”,“doc”)
and then I pull this document from the db and save it again with setValue(“name”, “doc2”) the live query won’t know it.
Let’s say that I save all the documents’ Ids that were saved to the DB and the liveQuery catched them, can we implement some kind of pattern to know that the document was changed but this liveQuery isn’t relevant anymore?

How does documentChangeListener can be helpful? Will I be able to know that the document isn’t relevant to my liveQuery anymore?
Is there a way to check if document answers to where expression?

This was your question. I didn’t see anything about “irrelevant to the query” anymore. You can tell that it is not relevant anymore by its absence from the query results in the next change listener callback. If it’s not there it is not relevant.

Beyond that if you want to know if the document changed again then you need to implement a document (or database) change listener. You won’t get specifically notified of documents leaving the result set.

Well, what about my statement isn’t true ? I said that if your document update affects the results of your query , that you will be notified. That’s why I wanted to know what your query was so I had a better sense of what you were attempting.

So in your case,

The query listener should be notified because the query result was affected by this change.
Isn’t that what you are observing?

Hopefully Jim’s follow on responses have clarified further.

The query listenr won’t be notified because the updated document is not answering to the query.
What I’m trying to say is that the change() won’t be called so I won’t know that the document was changed.
Assume that after 10 minutes of this update there will be another insert, a document that its “name” is “doc” then the change() will be called and then is the first time I will notice that the earlier document is not relevant anymore.

Here is a unit test that I put together on iOS which succeeds (i.e. the listener does get called when the document was updated such that it no longer meets criteria). Should be translatable to Android as well.
Does this capture what you are trying to do ?

   func testLiveQuery() throws {
        var count = 0;

       // Add a document 
        let doc = MutableDocument(id: "doc1")
        doc.setValue(5, forKey: "number1")
        try self.db.saveDocument(doc)
          
        // Create Query
        let q = QueryBuilder
            .select(SelectResult.expression(Meta.id), SelectResult.property("number1"))
            .from(DataSource.database(db))
            .where(Expression.property("number1").lessThan(Expression.int(10)))
            .orderBy(Ordering.property("number1"))
        
        let x = expectation(description: "changes")
        
        let token = q.addChangeListener { (change) in
           count = count + 1
            XCTAssertNotNil(change.query)
            XCTAssertNil(change.error)
            let rows =  Array(change.results!)
            print("----")
            for row in rows {
                print(row.toDictionary())
            }
            
            if count == 1 {
                print("Doc1 in db")
                XCTAssertEqual(rows.count, 1)
               
            } else {
                // This is invoked when doc1 is no longer matching criteria
                print("Doc1 was removed ")
                XCTAssertEqual(rows.count, 0)
                x.fulfill()
            }
        }
        
        
       // Asynchronously, update the document so it no longer meets the query where criteria
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
              let doc = self?.db.document(withID: "doc1")?.toMutable()
              doc?.setValue(15, forKey: "number1")
            
             try! self?.saveDocument(doc!)
        }
        waitForExpectations(timeout: 2.0) { (error) in }
        
        q.removeChangeListener(withToken: token)
    }

Apparently that is not Case in Android.
The live query isn’t called. I’m using @hideki 's example from here

https://github.com/couchbase/couchbase-lite-android/issues/1628

And after db.save(updateddoc) That used to answer the live query and now not, the live query isn’t called.

Hi @itai.shalom2,

Thank you for trying to use CBL 2.0.

Following unit test, which is Java implementation of @priya.rajagopal posted above, proves that LiveQuery send notification in case of query result is changed. From 1 document match to 0 document match.

If you know the Document ID, as @borrrden wrote previously, using DocumentChangeListener is efficient.

Thank you for supporting Couchbase Lite!

Thanks again for replying.
So I saw the unit test you wrote and the code is not much different than what you and me wrote earlier.
Few questions ahead:

  1. One difference i see, you use “executor” which i do not. May it be related?

  2. Why would the changed() be called? As far as I understood, on every db change the query q will run and changed() will be called if there are results from query q.
    Don’t get me wrong, I wish to get the all documents that used to answer the query q and now not.
    Also, you unit test doesn’t change the query params like I do -This causes the changed() to be call again and this time no results ( regardless the update)

  3. Regarding DocumentChangeListener, we have hundreds of documents on db, if each of them will have changeListener, it will kill the system (I assume that each listener is a thread, am I wrong?)

  4. Bottom line, if I use the code from
    https://github.com/couchbase/couchbase-lite-android/issues/1628
    but the where expression has Expression.and(Expression.property(“number1”).lessThan(Expression.int(10)))
    how can I make this changed() function called with the documents that are no longer answers that query?

Thanks

Yes it will. The LiveQuery listener gets called whenever the query results change. If a document used to be in the query results, and now isn’t anymore, that changes the results of the query.

Not quite. A database change starts a timer, and when the timer fires (in a fraction of a second) the query is re-run on the database. If the new results are different from the last results, the listener is called.

(The timer is to keep the query from being run hundreds of times if many documents are changing at once, as can happen during a pull replication.)