1
votes

I have got some troubles with a MongoDB request that I would like to execute. I am using MongoDB 3.2 with Mongoose in a node.js context. Here is the document:

{
  _id: ObjectId('12345'),
  name: "The name",
  components: [
    {
      name: "Component 1",
      description: "The description",
      container: {
        parameters: [
          {
            name: "Parameter 1",
            value: "1"
          },
          // other parameters
        ],
        // other information...
      }
    },
    // other components
  ]
}

And I would like to list all the parameters name for a specific component (using component name) in the specific document (using _id) with this output:

['Parameter 1', 'Parameter 2', ...]

I have got a mongoose Schema to handle the find or distinct methods in my application. I tried many operations using $ positioning operator. Here is my attempt but return all parameters form all components:

listParameters(req, res) {
  DocumentModel.distinct(
    "components.container.parameters.name", 
    {_id: req.params.modelId, "components.name": req.params.compId},
    (err, doc) => {            
      if (err) {
        return res.status(500).send(err);
      }
      res.status(200).json(doc);
    }
  );
}

but the output is the list of parameter name but without the filter of the specific component. Can you help me to find the right request? (if possible in mongoose JS but if it is a Mongo command line, it will be very good :))

2
Did you check the official docs ?kayess
Of course I did :) My problem is that the combination of the filter and the projection does not work as expected...CloudCompany
SO, you want to first filter by _id, then component name and project only parameters?jpgrassi
Yes, absolutely :)CloudCompany
Please add your attempts of using position operator to the question. The ones you think should work. The find command is limited to a single operator, so if you have nested arrays, you will need either aggregate, or filter on the client.Alex Blex

2 Answers

2
votes

You would need to run an aggregation pipeline that uses the $arrayElemAt and $filter operators to get the desired result.

The $filter operator will filter the components array to return the element satisfying the given condition whilst the $arrayElemAt returns the document from the array at a given index position. With that document you can then project the nested parameters array elements to another array.

Combining the above you ideally want to have the following aggregate operation:

DocumentModel.aggregate([
    { "$match": { "_id": mongoose.Types.ObjectId(req.params.modelId) } },
    { "$project": {
        "component": {
            "$arrayElemAt": [
                {
                    "$filter": {
                        "input": "$components",
                        "as": "el",
                        "cond": { "$eq": ["$$el.name", req.params.compId] }
                    }
                }, 0
            ]
        }
    } },
    { "$project": {
        "parameters": "$component.container.parameters.name"
    } }
], (err, doc) => {            
  if (err) {
    return res.status(500).send(err);
  }
  const result = doc.length >= 1 ? doc[0].parameters : [];
  res.status(200).json(result);
})
0
votes

i don`t know how to do it with mongoose but this will work for you with mongo

db.getCollection('your collaction').aggregate([ // change to youe collaction
    {$match: {_id: ObjectId("5a97ff4cf832104b76d29af7")}}, //change to you id
    {$unwind: '$components'},
    {$match: {'components.name': 'Component 1'}}, // change to the name you want 
    {$unwind: '$components.container.parameters'},
    {
        $group: {
            _id: '$_id',
            params: {$push: '$components.container.parameters.name'}
        }
    }


]);