How to match multiple attributes in nested object with numericFilters

Really loving Algolia, but hitting a snag. I’m migrating from Mongo to Firebase with Algolia on top to provide the search. But hitting a snag coming up with a comparable way to search in individual elements of a record.

I have an object that stores when a room is available: from and to. Each record can have many individual from/to combos (see the sample below with 2). I want to be able to run a search something like:
roomavailable.from <= 1522195200 AND roomavailable.to >=1524268799

But only have the query search a match within each element. Just like in Mongo. If I run that query on the record listed below, it will return the record, because the two available objects satisfy the .from and .to query. I think.

Is there a way to ensure the search is looking only at matching a pair of .from and .to in an individual object/element?

Below is the pertinent part of the record stored in Algolia so you can see the structure.

"roomavailable": [
        {
          "_id": "rJbdWvY9M",
          "from": 1522195200,
          "to": 1522799999
        },
        {
          "_id": "r1H_-vKqz",
          "from": 1523923200,
          "to": 1524268799
        }
      ],

And here is the Mongo (mongoose) equivalent where its searching inside individual elements (this works):

    $elemMatch: {
                        from: {
                            $lte: moment(dateArray[0]).utc().startOf('day').format()
                        },
                        to: {
                            $gte: moment(dateArray[1]).utc().endOf('day').format()
                        }
                    }

I have also tried this query but it seems to still match either the .from OR .to in the elements:

index.search("", {
 "hitsPerPage": "10",
 "page": "0",
 "analytics": "false",
 "attributesToRetrieve": "*",
 "facets": "[\"roomavailable.from\",\"roomavailable.to\",\"amentities\",\"services\"]",
 "facetFilters": "[[\"roomavailable.to:1524268799\"],[\"roomavailable.from:1522195200\"]]"
});

Thanks for any guidance you can provide!
Kane

EDIT: I thought I had this working so I marked this as solved, but its not working. The single brackets didn’t do the trick. Thanks for your help!

I’ve also tried something like this:

{
	"filters": "objectID: -L8huXqIhDGpXOtJJsdW",
  	"numericFilters": "(roomavailable.from <= 1522195200 AND roomavailable.to >= 1524268799)"
}

This one still returns the object if the .from and .to match any of the roomavailable objects.

Hello,
To my knowledge, Algolia does not support a numericFilter in a specific nested object in a Array. Maybe you can create another index with a dedicaced object for each room, and link theses rooms to your “parent object” by refering a field parentId and manage the relation on your front?

On my company, we use Elasticsearch to manage Nested search, and Algolia for everything else (And we make a “relation” between both in our front).

Thanks for the reply! Disappointing though. I feel like I’m missing something simple though as it seems like a fairly common use case to match multiple attributes in a nested object. Maybe with straight filters or facets?

I’ll think about your solution, too, it might work. Thanks again!

With facet, you can maybe do a “term” nested filter, but not a “range” (numeric, from to). It’s one of the biggest “lack” of Algolia for our usage too :slight_smile: .

Ughh. Thank you so much for sharing your insight. I’ll be able to spend time looking at other solutions instead of trying every different possible way to make this one query work.

If the Algolia team has any additional insight that would be great, for now I guess I need to look at a different solution.

Hey @kanec :wave:

If I understand correctly you want to filter records using a time range, right?

If this is the case you can definitely try something like this:

index.search("", {
 "hitsPerPage": "10",
 "page": "0",
 "analytics": "false",
 "attributesToRetrieve": "*",
 "filters": "from >= 152200000 AND to <= 1524268799",
 "facets": "[]"
});

You can find more documentation about filters and relative comparison operators on the following page:

You can also use numericFilters but we recommend to always use filters whenever possible because it provides a more natural SQL-like syntax.

Let me know if this helps you out!

@gianluca.bargelli Thank you so much for reviewing this post and supplying a response. Unfortunately this doesn’t quite solve my issue. Let me try to rephrase it because I’m certain I’m just missing something silly.

I have a list of rooms and each room can contain multiple blocks of dates the room is available. Say 4/1 - 4/7 and 4/12 - 4/22. They are stored as nested objects in “roomavailable”. I need to be able to execute a search that checks the .from and .to of each object not any combination.

Right now if I execute your suggestion . “filters”: “roomavailable.from >= 1522195200 AND roomavailable.to <= 1524268799” , it will return a result. This is happening because the .from is satisfied and the .to is satisfied, but its only satisfied by using the result of both availability objects.

What I’m hoping is there is a way to ensure the .from AND .to are searching and trying to match each object individually. In Mongo this would be done with elemMatch. I’m really hoping this is possible with Algolia.

Thanks again for taking the time to review. Knowing one way or another is huge for us.

Hi @kanec, thanks for clarifying your question!

Indeed what @Alefort suggested (using roomavailable in a separate index) would be the easiest option since the query I mentioned above will definitely return the results you want. This will mean that you’ll have to query the room availability index separately in order to get which IDs are available, so you’ll have to use multiple-queries:

That said, I asked our core API team to see if there’s a more reasonable way to approach this issue, but I fear that this is a filter limit due to performance reasons with arrays. You could transform your data structure in the following and index your rooms as an object instead:

[
  {
    "roomavailable": {
      "0": {
        "_id": "rJbdWvY9M",
        "from": 1522195200,
        "to": 1522799999
      },
      "1": {
        "_id": "r1H_-vKqz",
        "from": 1523923200,
        "to": 1524268799
      }
    }
  }
]

So you can apply the following filter:

{
  "filters": "roomavailable.0.from <= 1522195200 AND roomavailable.0.to >= 1522799999 AND roomavailable.1.from <= 1522195200 AND roomavailable.1.to >=1522900799"
}

The downside of this is that you’ll need to know the length of roomavailable in order to build the search query on the front-end (you can do so at indexing time by adding a roomavailable_count property) and also this will probably will be less performant with a considerable number of rooms per item; in this case, switching to a dedicated index makes totally sense for the following reasons:

  1. If in your backend you frequently update available rooms you won’t impact the other indices’ build time
  2. Filters will perform better (as explained above)
  3. Indexing strategy will be simpler to handle

Let me know what you think about this and if it helps you out.

@gianluca.bargelli and @Alefort thank you guys both for your help, I really appreciate it. I’m going to go down the suggested path of creating a separate index and running multiple queries.

Thanks again guys!

1 Like