How to implement multiple should clauses or match on the basis of array of ObjectIds in Atlas Search

Hi all,

Is there any way to have the equivalent of multiple groups of logical OR in Atlas Search? I have a query case where a result must satisfy at least one condition of one group of criteria and at least one condition of another group of criteria. Having two “should” clauses would not work. Also it is very limiting that there cannot be a $match before the $search stage. One of the groups of OR requires that a document field is in an array of ObjectIds, but we cannot use $equals in Atlas Search as it accepts only one ObjectId, not an array.

Many thanks for help.

Kind regards,
Gueorgui

Can you share a sample document?

Thank you Elle.

A sample document looks like this:

{
  "_id": {
    "$oid": "636bf48947c869baecdd19f6"
  },
  "title": "Fairy Tale",
  "authors": [
    "Stephen King"
  ],
  "price": {
    "$numberInt": "100"
  },
  "genres": [
    "Fiction"
  ],
  "identifiers": [
    "1668002175",
    "9781668002179"
  ],
  "averageRating": {
    "$numberDouble": "3.5"
  },
  "imageLinks": {
    "smallThumbnail": "http://books.google.com/books/content?id=jPzjzgEACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
    "thumbnail": "http://books.google.com/books/content?id=jPzjzgEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
  },
  "quantityAvailableForReservation": {
    "$numberInt": "1"
  },
  "totalQuantityListed": {
    "$numberInt": "1"
  },
  "quantityReserved": {
    "$numberInt": "0"
  },
  "description": "Legendary storyteller ...",
  "shopSuppliedIdentifier": "9781668002179",
  "infoLink": "http://books.google.co.uk/books?id=jPzjzgEACAAJ&dq=isbn:9781668002179&hl=&source=gbs_api",
  "bookLibraryId": {
    "$oid": "636bf45047c869baecdd19ee"
  },
  "dateListedForSale": {
    "$date": {
      "$numberLong": "1668019337373"
    }
  },
  "shopInfo": {
    "shopId": {
      "$oid": "635b93aaa86789aa76c2bc8a"
    },
    "charityName": "PDSA",
    "addressLine1": "307 High Street",
    "addressLine2": "",
    "city": "Orpington",
    "postcode": "BR6 0NN",
    "phone": "01689 871243",
    "geolocation": {
      "type": "Point",
      "coordinates": [
        {
          "$numberDouble": "0.098295"
        },
        {
          "$numberDouble": "51.373795"
        }
      ]
    },
    "email": "orpington@pdsa.org"
  },
  "bestseller": [
    {
      "bestsellerSource": "New York Times",
      "reviewLinks": []
    }
  ]
}

A user needs to be able to search for documents (books) that are in several shops by supplied array of shopIds (any of them should match) and also for books that fall in any one of the three categories (bestsellers, recommended - essentially 4 star or over rating, and new arrivals - essentially listed in the last 30 days) - the user may select one, two or all three categories and any should match (a book may be in all three). So both searches use logical OR. I can include a separate $match stage after $search, which seems the only option available given the constraints of Atlas Search. Is there any other way to do this in the $search stage?

Many thanks.

Kind regards,
Gueorgui

Could you use filter ?

So to clarify your specific ask is:

  1. match on a shopid
  "shopInfo": {
    "shopId": {
      "$oid": "635b93aaa86789aa76c2bc8a"
    }

Is there ever more oid’s listed?

  1. search across books that fall in any one of the three categories (bestsellers, recommended - essentially 4 star or over rating, and new arrivals - essentially listed in the last 30 days)

The front-end supplies an array of shopIds. So my original solution was:

if (shopIds) {
                for (let shopId of shopIds) {
                    searchStage.$search.compound.should.push(
                        {
                            equals: {
                                path: "shopInfo.shopId",
                                value: ObjectId(shopId)
                            }
                        }
                    )
                }
                searchStage.$search.compound.minimumShouldMatch = 1;
            }

I am not sure how I can do it with filter for arrays of values (any should match)? The problem seems to me is that “equals” does not accept an array of values (similar to $in).

For categories, I have used “exists” + “range”.

if (category && category !== 'all') {
                if (category === "bestsellers") {
                    searchStage.$search.compound.filter.push({
                        exists: {
                            path: "bestseller"
                        }
                    });
                } else if (category === "recommended") {
                    searchStage.$search.compound.filter.push({
                        exists: {
                            path: "recommended"
                        }
                    });
                } else if (category === "new arrivals") {
                    // new arrivals - 30 days before todays date; use 31 to account for time
                    afterDate.setDate(todaysDate.getDate() - 31);
                    searchStage.$search.compound.filter.push({
                        range: {
                            path: "dateListedForSale",
                            gt: afterDate
                        }
                    });
                }
            }

But now I need to use “should” too because I need mutiple select of several categories at the same time.

Many thanks.

Kind regards,
Gueorgui