1

In a mongodb aggregation pipeline, I want to $project the indices of an embedded array (a sub-document) that matches a previous $match stage.

Say, I have the following docs.

{_id: '1', tags: ['aaa', 'bbb', 'ccc']},
{_id: '2', tags: ['baa', 'aaa', 'aaa']},
{_id: '3', tags: ['aac', 'cbb', 'aca']},

Now, if I query with {tags: 'aaa'}, I want to output something similar to

{_id: '1', tags: [0]},
{_id: '2', tags: [1, 2]}

3 Answers 3

1
db.inventory.aggregate([
  { $match : {tags : 'aaa' }},
  { $unwind : { path: "$tags", includeArrayIndex: "arrayIndex"}},
  { $match : {tags : 'aaa' }},
  { $group : {
      _id : '$_id',
      tags : { $push : '$arrayIndex'}
    }
  }
 ])

Output :

{ "_id" : "2", "tags" : [ NumberLong(1), NumberLong(2) ] }
{ "_id" : "1", "tags" : [ NumberLong(0) ] }

Another way :

db.inventory.aggregate([
 { $match : {tags : 'aaa' }},
 { $project  : {
    tags: {
      $filter: {
        input: {
          $zip: {
            inputs: [ "$tags", { $range: [0, { $size: "$tags" }] } ]
          }
        },
        as: "tagWithIndex",
        cond: {
          $let: {
            vars: {
              tag : { $arrayElemAt: [ "$$tagWithIndex", 0 ] }
            },
            in: { $eq: [ "$$tag", 'aaa' ] }
          }
        }
      }
    }
 }},
 { $unwind  : '$tags'},
 { $group : {
       _id : '$_id',
       tags  : {
          $push : { $arrayElemAt: [ "$tags", 1]}
       }
   }
 }
])

Output :

{ "_id" : "2", "tags" : [ 1, 2 ] }
{ "_id" : "1", "tags" : [ 0 ] }

hope this helps.

Sign up to request clarification or add additional context in comments.

Comments

1

You need to $map over the $size of the $tags array to include index of the each element inside the tags array and then you can easily use $filter aggregation to exclude the elements which do contain letter aaa

db.collection.aggregate([
  { "$match": { "tags": "aaa" }},
  { "$project": {
    "tags": {
      "$filter": {
        "input": {
          "$map": {
            "input": { "$range": [0, { "$size": "$tags" }] },
            "in": {
              "string": { "$arrayElemAt": ["$tags", "$$this"] },
              "index": "$$this"
            }
          }
        },
        "cond": { "$eq": ["$$this.string", "aaa"] }
      }
    }
  }},
  { "$project": { "tags": "$tags.index" }}
])

Output

[
  {
    "_id": "1",
    "tags": [0]
  },
  {
    "_id": "2",
    "tags": [1, 2]
  }
]

Comments

0

If you're searching for an array, you should use $in.

db.inventory.find( { tags: { $in: [ 'aaa' ] } } )

You can also write the same in the match. spelling is the same.

Will help for detail. That's what you're looking for.

Source : https://docs.mongodb.com/manual/reference/operator/query/in/


db.inventory.find( { "tags": { $in: 'aaa' } },
                  { "tags.$": 1 } )

This is probably what you want.

3 Comments

db.inventory.find( { tags: { $in: [ 'aaa' ] } } ) will give me the first and second doc (from the docs in the question) as a whole. However, I need the indices of tags array that match the query.
db.inventory.find( { "tags": { $in: 'aaa' } }, { "tags.$": 1 } ) This gives a list of elements of the array that matches. Not their indices. However, I need their indices.
Why do you say these one by one, my beautiful brother, exactly what you want

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.