Rudy
Rudy

Reputation: 39

Autocomplete filtered with mongodb

I would like to perform autocompletion on the name but filtered on a specific city with mongoose and nodejs. I have a mongodb collection like this :

{
    "_id" : ObjectId("6007fd9d984e2507ad452cf3"),
    "name" : "John",
    "city" : "A",
},
{
    "_id" : ObjectId("6007ff6844d9e517e1ec0976"),
    "name" : "Jack",
    "city" : "B",
}

What i have done so far :

I have setup MongoDB Atlas with a Search Index (with the help of search doc) And set up the autocomplete like that :

router.get('/search', async (request, response) => {
  try {
    let result = await Client.aggregate([
      {
        "$search": {
          "autocomplete": {
            "query": `${request.query.term}`,
            "path": "name",
            "fuzzy": {
              "maxEdits": 2,
              "prefixLength": 3,
            },
          },
        },
      },
      {
        $limit: 3
      },
      {
        $project: {
          "_id": 0,
        }
      }
    ]);
    response.send(result);
  } catch (e) {
    response.status(500).send({message: e.message});
  }
});

In front-end, with autocompleteJs :

  const autoCompleteJS = new autoComplete({
    data: {
      src: async () => {
        const query = document.querySelector("#autoComplete").value;
        const source = await fetch(`${window.location.origin}/search?term=${query}`);
        const data = await source.json();
        return data;
      },
      key: ["name"],
    },
    trigger: {
      event: ["input", "focus"],
    },
    searchEngine: "strict",
    highlight: true,
  });

So far it is working well. But I don't know how to make the autocomplete result filtered based on city. It seems that the documentation does not mention this. Do you have any leads.

Upvotes: 1

Views: 2351

Answers (2)

Paul Savala
Paul Savala

Reputation: 71

Use the $where pipeline stage from the aggregation pipeline after performing your search to filter out unwanted documents. So for example,

Client.aggregate([
  {
    "$search": {
      "autocomplete": {
        "query": `${request.query.term}`,
        "path": "name",
        "fuzzy": {
          "maxEdits": 2,
          "prefixLength": 3,
        },
      },
    },
  },
  { 
    $match: { city: 'city-name' } 
  },
  {
    $limit: 3
  },
  {
    $project: {
      "_id": 0,
    }
  }
]);

Upvotes: 1

Doug
Doug

Reputation: 15515

Use a compound operator like so which lets you have more control over your results in a performant fashion:

"$search": {
  "compound" : {
    "filter" : [{ 
       "text" : { path: "city", query: "New York" } 
     }],
     "must": [{ 
          "autocomplete": {
            "query": `${request.query.term}`,
            "path": "name",
            "fuzzy": {
              "maxEdits": 2,
              "prefixLength": 3,
            },
          },}
        }] }

using filter will filter your results, without impacting the score. must will require that the name field will also match. Also take a look at should and mustNot in the compound docs for more options.

Upvotes: 0

Related Questions