3

This is an issue I came across while trying to mix geo location with Firestore. Long story short - I need restaurants around user's location. In order to get geo search done I use Algolia. When I do the request to Algolia it returns an array of unique restaurant IDs which correspond to Firestore document ids. This works just fine.

What makes things complicated is that I need two more conditions - I need to restrict the query to restaurants with average rating >= 8. And also I want to limit the count of the returned documents (2, 5, 20 etc).

So this is how it should look like in pseudo code:

db.restaurantsCollection
    .documentIds([111, 222, 333, 444, 555, 666, 777, 888, 999])
    .whereField("averageRating", isGreaterThanOrEqualTo: 8)
    .order(by: "averageRating", descending: true)
    .limit(to: 2)
    .getDocuments()

I know as of today Firestore doesn't support queries with multiple document ids. So what is the most optimized way to perform such a query?

If I set the documentId as an id field in my document and then iterate through all of the returned ids from Algolia and do something like this (then I can do the ordering and limiting in pure Swift):

for id in ids {
    db.restaurantsCollection
        .whereField("averageRating", isGreaterThanOrEqualTo: 8)
        .whereField("id", isEqualTo: id)
        .getDocuments()
}

But still this means a lot of requests. Any other ideas?

Vasil Garov
  • 4,851
  • 1
  • 26
  • 37
  • There is no API in the client-side SDKs to retrieve a set of document IDs (yet). If the IDs are in a single range (which is unlikely), you can use a range query (`where ID >= 111 && ID <= 999`). But otherwise this will require a separate request for each document. See https://stackoverflow.com/questions/46721517/google-firestore-how-to-get-document-by-multiple-ids-in-one-round-trip – Frank van Puffelen Nov 27 '18 at 14:55
  • Possible duplicate of [Google Firestore - how to get document by multiple ids in one round trip?](https://stackoverflow.com/questions/46721517/google-firestore-how-to-get-document-by-multiple-ids-in-one-round-trip) – Frank van Puffelen Nov 27 '18 at 14:56
  • Thanks Puf, yeah that was my first resource. I am asking because my case is a bit more complicated. The ids should actually be unique autogenerated IDs, sorry for the confusing example. So say I am getting 100 IDs from Algolia and I only need the first 2 top rated restaurants. Does it mean I have to make 100 requests to Firestore? – Vasil Garov Nov 27 '18 at 16:27
  • Did you find a solution to your problem? – Leze Mar 08 '19 at 22:58

2 Answers2

6

Here is a bit more efficient way to do it:

    firestoreDB
        .collection("someCollection")
        .whereField(FieldPath.documentID(), in: [
            "05C0632C-601B-4D98-BD2B-3E809D0496B1", //up to 10 ids
            "087686CA-6B21-4268-9E4C-CF833FCA92DE"
        ]).getDocuments { snap, error in

            if let snap = snap {
                snap.documents.forEach { print($0.data()) }

            } else if let error = error {
                print("Error: \(error)")
            }
        }

Getting them in batches of 10 will allow for sorting (using .order(by: ) etc.) It is not perfect, but looks like an improvement on what you have. You could have multiple such calls and merge the results in one, then sort those.

Adrian
  • 415
  • 4
  • 18
1

In case this is helpful to anyone I am posting the options you might consider if you are implementing geo queries the way I implemented them - with Algolia. So the way you do it with Algolia is to keep coordinates with your search indices.

For example:

{
  "_geoloc": {
    "lng": 8.507785799999999,
    "lat": 47.17652589999999
  },
  "objectID": "1234abcd"
}

Where objectID corresponds to a document ID of a restaurant or whatever kind of venue you want to geo query.

Then you create a query through Algolia API. If you want to get all items around a CLLocation you should set it up - use query.aroundLatLng property for that. You can also set a distance property in meters. The result will be a JSON with your IDs sorted by their distance starting with the closest.

So far so good - we solved our biggest issue - querying by geo location.

Now what if I want to get the top rated from the all N IDs I got from Algolia? The sad thing is that so far there is no API to query by multiple ids. So this means if I want to get all restaurants with score >= 8, I need to make N Firestore document reads and then get the top rated. Far from ideal.

Option 1.

Save restaurant averages with Algolia data.

I know this means extra networking but this is a good way to go. Drawbacks - every time you update the restaurant average in Firestore you need to update it in Algolia also. And if you calculate averages with Cloud Functions as I do you need to set them in Algolia too. Yes, this means you need to upgrade to a paid Firebase plan since this is an external Cloud Functions call.

Now that you have averages in your Algolia payload you can easily get all restaurant IDs for your area and then sort the data by their Algolia rating client side. Once you have the ids sorted you can then request the documents from Firestore.

Option 2.

Save city name and country

You can save city and country names with your location data in Firestore. This way you can use them for your queries. Pseudo Firestore query code:

db.restaurants.where("city" == "current_city").where("country" == "current_country").where("averageRating" >= 8).getDocuments()

However this approach won't be very accurate and will mostly work for big cities. It also won't count the proximity to your location.

I hope this helps.

Vasil Garov
  • 4,851
  • 1
  • 26
  • 37