Find inside an array in a collection

Hi everyone :smiley:
I’m new to mongo, I’m currently building my very first API with this new technology

I have a collection that looks like this

[
  {
    "_id": "5f7a1b6a3aedab1df33574bb",
    "name": "myFirstGame",
    "players": [
      {
        "_id": "5f7a1be79acaf41e058ccd9f",
        "name": "Albert",
        "points": 10
      },
      {
        "_id": "5f7a20de87f0fc1ea309145c",
        "name": "Adam",
        "points": 10
      },
      {
        "_id": "5f7a25fdcd01211fc84afd0e",
        "name": "John",
        "points": 10
      }
    ],
    "description": "This is a fun game to watch",
    "__v": 0
  }
]

And I’m trying to get the player which name is JOHN

try{
        const cond = {
          _id: '5f7a1b6a3aedab1df33574bb', //the id of the game
          'players.name': { $eq: 'John' }, // the condition on the player
        }
        player = await Game.find(cond, 'players');
        return player;
}catch (e) {
    console.error(e);
}

What I’m expecting to get is an object like this

      {
        "_id": "5f7a25fdcd01211fc84afd0e",
        "name": "John",
        "points": 10
      }

But instead I’m getting the full collection with no filter applied

Is there anyway to get an object inside a collection array?

In SQL it would be something like thise

SELECT p.name, p.points FROM players as p
where p.game_id = '5f7a1b6a3aedab1df33574bb' AND p.name = 'John'
1 Like

Hello @Julian_Mendez, welcome to the community!

In MongoDB there are various operators to work with arrays. In your case you want to project an array element for a matching condition, with the positional $ operator.

This projects the first element in an array that matches the query condition.

The query:

db.test.find( { _id: "5f....", "players.name": "Adam" }, { "players.$": 1 } )

1 Like

Thank you @Prasad_Saya,
This is more or less what I was looking for…
Now, I have another question…

Is no other way to get instead of a Collection/Array, to get the plain object?

I mean, I did what you told me and I get this…

[
  {
    "_id": "5f7a1b6a3aedab1df33574bb",
    "players": [
      {
        "_id": "5f7a1be79acaf41e058ccd9f",
        "name": "Adam",
        "points": 101
      }
    ]
  }
]

And i thought the result would be like this

      {
        "_id": "5f7a1be79acaf41e058ccd9f",
        "name": "Adam",
        "points": 101
      }

I’m asking this because I’m looking for the players, not the whole game, I already “filter” the game when I’m using the _id of a game

You need to use projection to remove the fields (or keep the fields you want) in the output. for example, { "players.$": 1, _id: 0 }.

The output is always a document. You can control what fields you want in the output with a find query.

Hi @Julian_Mendez,

I tested the following query in Mongo Shell:

db.game.find(
   {"players.name": "John"},
   {"players.$": 1, _id:0}
).next().players[0]

And I got the following result:

{ "_id" : "5f7a25fdcd01211fc84afd0e", "name" : "John", "points" : 10 }

NB: next() iterates over the cursor (returned by find()) and returns the next value in the cursor.

This (overkill) aggregation pipeline would also do the job and offer a little more flexibility:

db.game.aggregate([
  {
    '$unwind': {
      'path': '$players'
    }
  }, {
    '$match': {
      'players.name': 'John'
    }
  }, {
    '$project': {
      '_id': '$players._id', 
      'name': '$players.name', 
      'points': '$players.points'
    }
  }
])

The main difference between the 2 here: the aggregation pipeline will return 2 Johns if you had 2 “John” in the same game while the first find() query will only return the first “John” found.

Enjoy,
Maxime.