19

I am aware it would be very difficult to query by a value that does not exist in an array but is there a way to do this without doing exactly that?

Here is my scenario - I have a subscription based service where people can opt in and "follow" a specific artist. In my backend, this creates a subscription doc with their followerId, the id of the artist they want to follow (called artistId), and an array called pushed. The artist can add new releases, and then send each follower a notification of a specific song in the future. I would like to keep track of which follower has been pushed which release, and this done in the aforementioned pushed array. I need a way to find which followers have already been pushed a specific release and so...

I was thinking of combining two queries but I am not sure if it is possible. Something like:

  1. db.collection('subscriptions').where('artistId', '==', artistId)
  2. db.collection('subscriptions').where('artistId', '==', artistId).where('pushed', 'array-contains', releaseId)

And then take the intersection of both query results and subtract from the 1st query to get the followers that have not been pushed a specific release.

Is this possible? Or is there a better way?

imjared
  • 19,492
  • 4
  • 49
  • 72
nolbuzanis
  • 225
  • 2
  • 9

4 Answers4

5

There is no way to query Firestore for documents that don't have a certain field or value. It's not "very difficult", but simply not possible. To learn more on why that is, see:

Your workaround is possible, and technically not even very complex. The only thing to keep in mind is that you'll need to load all artists. So the performance will be linear to the number of artists you have. This may be fine for your app at the moment, but it's something to definitely do some measurements on.

Typically my workaround is to track not what releases were pushed to a user, but when the last push was sent to a user. Say that a release has a "notifications sent timestamp" and each user has a "last received notifications timestamp". By comparing the two you can query for users who haven't received a notification about a specific release yet.

The exact data model for this will depend on your exact use-case, and you might need to track multiple timestamps for each user (e.g. for each artist they follow). But I find that in general I can come up with a reasonable solution based on this model.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I found this: https://firebase.google.com/docs/reference/rules/rules.List which might be a viable solution to this problem. Could the X in Y operator be used in a query though or would the doc have to be read from the db first? – nolbuzanis Jan 06 '20 at 03:23
  • That link describes an operation in security rules to detect if a list *does* contain a certain value. The equivalent in the SDKs is the `array-contains` operator. – Frank van Puffelen Jan 06 '20 at 03:34
1

For Elasticsearch case you need to sync with your database and elasticsearch server. And also need to make firewall rules at your Google Cloud Platform, you need keep away the arbitrarily request to your server, since it may cause bandwith cost.

Tahir Kizir
  • 83
  • 2
  • 10
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/low-quality-posts/27266221) – Kamil Naja Sep 28 '20 at 20:11
  • 1
    @KamilNaja This is technically an answer. It may be a low quality answer, but it shouldn't be deleted, at least in the LQP review queue. – 10 Rep Sep 28 '20 at 20:32
1

The not-in operator is now available in Firestore!

citiesRef.where('country', 'not-in', ['USA', 'Japan']);

See the docs for a full list of examples:

https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any

1
citiesRef.where('country', 'not-in', [['USA']]);

Notice the double array around [['USA']]. You need this to filter out any docs that have 'USA' in the 'country' array.

Single array ['USA'] assumes that 'country' is a string.

BrianG7
  • 155
  • 1
  • 4
  • 4
    It works if array only contains 'USA'. Not useful because arrays generally contains multiple values. – LacOniC Jun 28 '21 at 05:26
  • Yes in most cases it would not be useful. I did however use this functionality with getting docs that did not contain the user's Id in the array of userId's. Luckily for me, this was at a point where the user is looking to join another users game(and there is only one user in the array) and I didn't want to include docs where the current user might have started a game trying to find a partner. – BrianG7 Jun 29 '21 at 15:48