72

I want select from Firestore collection just articles written NOT by me.
Is it really so hard?

Every article has field "owner_uid".

Thats it:
I JUST want to write equivalent to "select * from articles where uid<>request.auth.uid"

TL;DR: solution found already: usages for languages/platforms: https://firebase.google.com/docs/firestore/query-data/queries#kotlin+ktx_5

android51130
  • 1,077
  • 2
  • 9
  • 14
  • Firestore now supports "where in" operator. Check my answer and update accepted answer – TSR Sep 13 '20 at 15:09

7 Answers7

95

EDIT Sep 18 2020

The Firebase release notes suggest there are now not-in and != queries. (Proper documentation is now available.)

  • not-in finds documents where a specified field’s value is not in a specified array.
  • != finds documents where a specified field's value does not equal the specified value.

Neither query operator will match documents where the specified field is not present. Be sure the see the documentation for the syntax for your language.

ORIGINAL ANSWER

Firestore doesn't provide inequality checks. According to the documentation:

The where() method takes three parameters: a field to filter on, a comparison operation, and a value. The comparison can be <, <=, ==, >, or >=.

Inequality operations don't scale like other operations that use an index. Firestore indexes are good for range queries. With this type of index, for an inequality query, the backend would still have to scan every document in the collection in order to come up with results, and that's extremely bad for performance when the number of documents grows large.

If you need to filter your results to remove particular items, you can still do that locally.

You also have the option of using multiple queries to exclude a distinct value. Something like this, if you want everything except 12. Query for value < 12, then query for value > 12, then merge the results in the client.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • 13
    locally but how? If collection "articles" has 10 thousands articles readed by me it means I have to query this collection with pagination crazy amount of times before i will reach UNreaded articles. – android51130 Nov 12 '17 at 19:30
  • 1
    for example, how to get only articles i DONT read already?
    storing readed_article_ids in my user Firestore Document will enlarge my user document and every firestore doc has limit in 1mb - some day readed_article_ids Firestore Array within my user document will exceed this threshold.
    So confused :(((
    – android51130 Nov 12 '17 at 19:34
  • If you have a more specific question about data modeling, that sounds like a different question. – Doug Stevenson Nov 12 '17 at 20:19
  • hm, do I need to create new question to get answer? )) allright, I will right now, cuz' I read all the docs about data modeling and still have questions about it – android51130 Nov 13 '17 at 08:36
  • 198
    Such annoying lacking of basic functionality makes me regret that I've picked Google Firebase as BaaS – Andrey Gordeev Nov 22 '17 at 13:19
  • 11
    There's no minefields here. Like any nosql database, you have to learn how to model your data in a way that suits your anticipated queries. – Doug Stevenson Nov 23 '17 at 18:17
  • 6
    So I've made an app for creating articles. I've been surprised, when I haven't found any solution of how to show a list of articles created by other users. local filtering is bullshit in million(just one!) articles case. ^( – android51130 Nov 28 '17 at 19:29
  • I edited the answer to include an option that may not be immediately obvious - using queries to exclude a single value from a range. – Doug Stevenson Nov 28 '17 at 19:33
  • 1
    And what about if it's not a number but an id (alphanumeric)? This is quite annoying. – b-fg Apr 05 '18 at 18:52
  • @b-fg You can use the same strategy. – Doug Stevenson Apr 06 '18 at 00:07
  • Range queries for alphanumeric values? Please show me an example. – b-fg Apr 06 '18 at 10:25
  • @b-fg It's just like numbers, except you use strings. There is nothing special going on here - it's standard lexigographic comparison of strings. – Doug Stevenson Apr 06 '18 at 10:28
  • 1
    @DougStevenson String comparison doesn't work for me. I get an empty response when I use two where clauses with ">" and "<". – Ari Sep 20 '18 at 17:58
  • For new comers, this answer is now out of date. Check my answer below for up to date answer . – TSR Sep 13 '20 at 15:13
  • @TSR this answer is not out of date. What you're recommending in your answer isn't an inequality filter that gives everything but a specific value, as the OP requests. It's implementing a query for a set of specific values. – Doug Stevenson Sep 13 '20 at 20:10
  • Can you explain that further @DougStevenson I tried to implement != query, but weirdly it's JUST returning elements that logically are == to the requested attribute/value – David Seek Sep 21 '20 at 22:40
  • @DavidSeek If you have a specific query that's not working the way you expect, please post a new question that illustrates the issue and that anyone can reproduce. – Doug Stevenson Sep 21 '20 at 23:18
  • @DougStevenson https://stackoverflow.com/questions/64001492/how-to-use-firebases-new-firestore-inequality-require-feature – David Seek Sep 22 '20 at 00:26
  • Never-mind. I'm an idiot. Been getting from the wrong Node. Been storing the status update to the proper Node. That's why I was getting the same element over and over again. Well. That's a day I'm not getting back... – David Seek Sep 22 '20 at 00:43
  • Now inequality filter `!=` is available in firebase sdk but seems not to work. – Midou Sep 22 '20 at 21:21
  • @Midou If you have a question, post it separately along with the code that isn't working the way you expect, and a description of what it's supposed to do instead. – Doug Stevenson Sep 22 '20 at 21:53
  • @DougStevenson Do you know if/when the official docs are expected to be updated to include these new filters? The [WereFilterOp Ref](https://firebase.google.com/docs/reference/js/firebase.firestore#wherefilterop) doesn't mention them. Also, the [Queries Guide](https://firebase.google.com/docs/firestore/query-data/queries#query_limitations) still says `!=` is not supported. I'm specifically interested in knowing more about the indexing and security implications related to these new filters. Seems like and awfully big feature to drop and then only leave a couple hints in the release notes! – CDoe Sep 25 '20 at 17:50
  • @CDoe I suggest reaching out to Firebase support if you have a question that can only be addressed by the Firebase team. https://support.google.com/firebase/contact/support – Doug Stevenson Sep 25 '20 at 17:52
  • edit your answer, it is real, NE queries are alive https://firebase.googleblog.com/2020/09/cloud-firestore-not-equal-queries.html – Ruli Oct 02 '20 at 19:48
6

For android it should be easy implement with Task Api. Newbie example:

    FirebaseFirestore db = FirebaseFirestore.getInstance();
    Query lessQuery = db.collection("users").whereLessThan("uid", currentUid);
    Query greaterQuery = db.collection("users").whereGreaterThan("uid", currentUid);
    Task lessQuery Task = firstQuery.get();
    Task greaterQuery = secondQuery.get();

    Task combinedTask = Tasks.whenAllSuccess(lessQuery , greaterQuery)
                             .addOnSuccessListener(new OnSuccessListener<List<Object>>() {
        @Override
        public void onSuccess(List<Object> list) {

            //This is the list of "users" collection without user with currentUid
        }
    });

Also, with this you can combine any set of queries.

For web there is rxfire

Jurij Pitulja
  • 5,546
  • 4
  • 19
  • 25
4

This is an example of how I solved the problem in JavaScript:

let articlesToDisplay = await db
  .collection('articles')
  .get()
  .then((snapshot) => {
    let notMyArticles = snapshot.docs.filter( (article) => 
      article.data().owner_uid !== request.auth.uid
    )
    return notMyArticles
  })

It fetches all documents and uses Array.prototype.filter() to filter out the ones you don't want. This can be run server-side or client-side.

Darren G
  • 780
  • 8
  • 16
  • 4
    Did you count reads from your db in Firebase console? I think in this way you always read all the 'atricles' db graph, and only then you filter items - which is too expensive by your server memory+cpu usage in the case of huge graph size – android51130 Mar 01 '20 at 12:10
  • Yes, as stated this solution reads all documents from the collection. Inappropriate for large collections. You could filter on date to get articles from, say, the last week only to reduce the size, but depends on the use case. – Darren G Mar 02 '20 at 19:24
0

Updating the answer of Darren G, which caused "TypeError: Converting circular structure to JSON". When we perform the filter operation, the whole firebase object was added back to the array instead of just the data. We can solve this by chaining the filter method with the map method.

let articles = []
let articlesRefs = await db.collection('articles').get();

articles = articlesRefs.docs
           .filter((article) => article.data.uid !== request.auth.uid) //Get Filtered Docs
           .map((article) => article.data()); //Process Docs to Data

return articles

FYI: This is an expensive operation because you will fetching all the articles from database and then filtering them locallly.

Ashfaq nisar
  • 2,500
  • 1
  • 12
  • 22
0
  1. Track all user id in a single document (or two)

  2. filter unwanted id out

  3. Use "where in"


var mylistofidwherenotme =  // code to fetch the single document where you tracked all user id, then filter yourself out


database.collection("articles").where("blogId", "in", mylistofidwherenotme)

TSR
  • 17,242
  • 27
  • 93
  • 197
  • you can't filter unwanted id's out from the document and you also can't fetch every time all the ids to perform filtering locally - we speak about huge database – android51130 Sep 16 '20 at 17:16
-1
let query = docRef.where('role','>',user_role).where('role','<',user_role).get()

This is not functioning as the "not equal" operation in firestore with string values

BLasan
  • 57
  • 4
-1

You can filter the array of objects within the javascript code.

var data=[Object,Object,Object] // this is your object array
var newArray = data.filter(function(el) {
   return el.gender != 'Male';
});
Kanishka
  • 267
  • 4
  • 21