Search code examples
arraysmongodbmeteorsubdocument

Find & return first matching subdocument from array (Meteor / Mongo)


how do I find and return the first subdocument in the 'tasks' array that matches completed: true?

using findOne returns the entire document.. is there another function for returning a subdocument?

{
  title: 'awebsite.com'
  company: 'a company'
  companyID: Random.id()
  category: 'website'
  starred: false
  timeline: {
    tasks: [
      {
        name: 'task1'
        completed: true
        todos: [
          {todo: 'something', completed: false, todoID: Random.id()}
          {todo: 'something', completed: false, todoID: Random.id()}
          {todo: 'something', completed: false, todoID: Random.id()}
        ]
      }
      {
        name: 'task2'
        completed: false
        todos: [
          {todo: 'something', completed: false, todoID: Random.id()}
          {todo: 'something', completed: false, todoID: Random.id()}
          {todo: 'something', completed: false, todoID: Random.id()}
        ]
      }
    ]
  }
}

Solution

  • You can do this by aggregation where you can take advantage of an index and limit with the $match pipeline. Use the $unwind operator to deconstruct your tasks array into a stream of documents that can then be matched. since you would want to return only "the first subdocument in the 'tasks' array that matches completed: true", you could use the $limit operator in the last pipeline stage to return only one subdocument:

    db.collection.aggregate([
      { 
          $match: {
              "timeline.tasks.completed": true
          }
      },
      { 
          $unwind: "$timeline.tasks" 
      },
      { 
          $match: {
              "timeline.tasks.completed": true
          }
      },
      { 
          $group: {
               _id: {
                    "tasks": "$timeline.tasks"
               }
          }
      },
      {
          $project: {
              _id: 0,
              tasks: "$_id.tasks"
          }
      },
      {
          $limit: 1
      }
    ])
    

    Results in:

    {
        "result" : [ 
            {
                "tasks" : {
                    "name" : "task1",
                    "completed" : true,
                    "todos" : [ 
                        {
                            "todo" : "something",
                            "completed" : false,
                            "todoID" : "jfoe84jn"
                        }, 
                        {
                            "todo" : "something",
                            "completed" : false,
                            "todoID" : "yr934hjs"
                        }, 
                        {
                            "todo" : "something",
                            "completed" : false,
                            "todoID" : "84hdkl0t"
                        }
                    ]
                }
            }
        ],
        "ok" : 1
    }