1

I need to fetch sub-set of documents in Firestore collection modified after some moment. I tried going theses ways:

  • It seems that native filtering can work only with some real fields in stored document - i.e. nevertheless Firestore API internally has DocumentSnapshot.getUpdateTime() I cannot use this information in my query.

  • I tried adding my _lastModifiedAt 'service field' via server-side firestore cloud function, but ... that updating of _lastModifiedAt causes recursive invocation of the onWrite() function. I.e. is does also not work as needed (recursion finally stops with Error: quota exceeded (Function invocations : per 100 seconds)).

Are there other ideas how to filter collection by 'lastModifiedTime'?


Here is my 'cloud function' for reference

  • It would work if I could identify who is modifying the document, i.e. ignore own updates of _lastModified field, but I see no way to check for this
  • _lastModifiedBy is set to null because of current inability of Firestore to provide auth information (see here)

exports.updateLastModifiedBy = functions.firestore.document('/{collId}/{documentId}').onWrite(event => {
        console.log(event.data.data());
        var lastModified = {
            _lastModifiedBy: null,
            _lastModifiedAt: now
        }
        return event.data.ref.set(lastModified, {merge: true});
    });
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Xtra Coder
  • 3,389
  • 4
  • 40
  • 59

3 Answers3

1

I've found the way to prevent recursion while updating '_lastModifiedAt'.

Note: this will not work reliably if client can also update '_lastModifiedAt'. It does not matter much in my environment, but in general case I think writing to '_lastModifiedAt' should be allowed only to service accounts.

exports.updateLastModifiedBy = functions.firestore.document('/{collId}/{documentId}').onWrite(event => {
    var doc = event.data.data();
    var prevDoc = event.data.previous.data();

    if( doc && prevDoc && (doc._lastModifiedAt != prevDoc._lastModifiedAt) )
        // this is my own change
        return 0;

    var lastModified = getLastModified(event);

    return event.data.ref.set(lastModified, {merge: true});
});

Update: Warning - updating lastModified in onWrite() event causes infinite recursion when trying to delete all documents in Firebase console. This happens because onWrite() is also triggered for delete and writing lastModified into deleted document actually resurrects it. That document propagates back into console and is tried to be deleted once again, indefinitely (until WEB page is closed).

To fix that issue above mentioned code has to be specified individually for onCreate() and onUpdate().

Xtra Coder
  • 3,389
  • 4
  • 40
  • 59
0

How about letting the client write the timestamp with FieldValue.serverTimestamp() and then validate that the value written is equal to time in security rules?

Also see Mike's answer here for an example: Firestore Security Rules: If timestamp (FieldValue.serverTimestamp) equals now

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

You could try the following function, which will not update the _lastModifiedAt if it has been marked as modified within the last 5 seconds. This should ensure that this function only runs once, per update (as long as you don't update more than once in 5 seconds).

exports.updateLastModifiedBy = functions.firestore.document('/{collId}/{documentId}').onWrite(event => {
        console.log(event.data.data());
        if ((Date.now() - 5000) < event.data.data()._lastModifiedAt) {return null};
        var lastModified = {
            _lastModifiedBy: null,
            _lastModifiedAt: now
        }
        return event.data.ref.set(lastModified, {merge: true});
    });
Jason Berryman
  • 4,760
  • 1
  • 26
  • 42