mongodb atlas search embedded document and project only the matched array elements

131 views Asked by At

I have a mongo db collection structured something like below.

{
_id,
name,
companies: [{
name,
rating
address
}]

}

I have created a atlas index of the type embedded on the field companies.name, when I query using this index, I get the whole document in the results including all the elements of the array that were not a match. How can I project or even find out which array element was the match. I cannot use highlights, since its not supported with embedded document datatype. I can $unwind the results from my aggregation pipeline, but then I have to do the matching myself on the name fields. Other option for me is to restructure the document, but that has other implications. What are my options here ?, all I need is to findout which element was the match.

Here is my query.

let query1 = {
      '$search': {
          'index': 'businessname',
          'embeddedDocument': {
            'path': 'companies', 
            'operator': {
              'queryString': {
                'defaultPath': 'companies.name', 
                'query': `${tags} ~ 2`
              }
            }
          }
        }
      }
 let project = {
      '$project': {
        '_id': 1,
        'displayName': 1,
        'email': 1,
        'companies.$': 1
      }

    }  

I get the below error when using companies.$: 1 MongoServerError: Invalid $project :: caused by :: Cannot use positional projection in aggregation projection

1

There are 1 answers

3
Buzz Moschetti On

Doing a $match for data in an array always returns the whole array. To return only the matched element, add another stage to $filter:

db.foo.aggregate([
    {$match: {"companies.name":"X5"}},
    {$project: {
        company: {$filter: {input: '$companies', cond: {$eq:['$$this.name','X5']}}}
    }}  
]);

If you know that your query should yield only 1 item (e.g. in this case, companies.name is unique in the array), then you can make it slightly easier by returning a single object instead of an array of 1:

    {$project: {
        X: {$arrayElemAt:[{$filter: {input: '$companies', cond: {$eq:['$$this.name','X5']}}\
},0]}
    }}