skip to Main Content

I am trying to get a number of documents depending on what query you type in.

For example I have a bunch of documents:

[
  {name: 'jon', city: 'la', country: 'us'},
  {name: 'foo', city: 'ca', country: 'us'},
  {name: 'alf', city: 'ca', country: 'us'},
  {name: 'olla', city: 'la', country: 'us'},
  {name: 'prol', city: 'la', country: 'us'},
  {name: 'jon', city: 'ca', country: 'us'}
]

And if I query jon la I want {name: 'jon', city: 'la'} as a result.

I use Mongoose and the find() method.

I’ve tried both with regex and the text search index.

Using text:

.find({
  '$text': {
       $search: 'jon la'
   },
})

Using regex:

.find({
  '$or': [
    { name: { $regex: '^jon|^la' } },
    { city: { $regex: '^jon|^la' } },
    { country: { $regex: '^jon|^la' } }
],
})

I get these results from both approaches:

  {name: 'jon', city: 'la', country: 'us'},
  {name: 'olla', city: 'la', country: 'us'},
  {name: 'prol', city: 'la', country: 'us'},
  {name: 'jon', city: 'ca', country: 'us'}

The problem here is I never know if it should find on name or city or any other key in the object. For example the query can be jon us then I want to get all jon in us and not all jon and all us.

How can I achieve this?

I am thinking about a option to for example

2

Answers


  1. Chosen as BEST ANSWER

    I solved it by nesting $and with $or's with a split.

    Live(almost) example:

    // Get search query and split by space
    const query = 'jon la'.split(' ')
    
    // Choose which fields to be searched for
    const searchFields = ['customer.contact.name', 'city', 'age']
    
    // Data for find() method
    const data = {}
    
    // Map through search words and wrap in '$or'
    data['$and'] = query.map((q: string) => {
      return { $or: searchFields.map((v) => ({ [v]: { $regex: q, $options: 'i' } })) };
    });
    
    await Model.find(data)
    

  2. If you explicitly want documents where name=jon and city=la (or the regex of those) you just don’t use a $or

    .find({
      name: { $regex: '^jon' },
      city: { $regex: '^la' }
    })
    

    If the problem is you don’t know what is being searched for (e.g., you need to find results where >2 words are matched) things are more complicated, you’d need to use an aggregation pipeline I think.

    If what you care about is finding the "best n" matches you can use the full text search scoring

    .find(
      '$text': {
           $search: 'jon la'
       },
       { score: { $meta: "textScore" } }
    ).sort( { score: { $meta: "textScore" } } )
    

    More info on textScore is here: https://www.mongodb.com/docs/manual/reference/operator/query/text/

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search