Search code examples
arraysnode.jsmongodbembedded-documents

How to query a single embedded document in an array in MongoDB?


I am trying to query a single embedded document in an array in MongoDB. I don't know what I am doing wrong. Programmatically, I will query this document and insert new embedded documents into the currently empty trips arrays.

{
    "_id" : ObjectId("564b3300953d9d51429163c3"),
    "agency_key" : "DDOT",
    "routes" : [
        {
            "route_id" : "6165",
            "route_type" : "3",
            "trips" : [ ]
        },
        {
            "route_id" : "6170",
            "route_type" : "3",
            "trips" : [ ]
        },
...

    ]
}

Following queries -I run in mongo shell- return empty:

db.tm_routes.find( { routes :  {$elemMatch: { route_id:6165 } } } ).pretty();

db.tm_routes.find( { routes :  {$elemMatch: { route_id:6165,route_type:3 } } } ).pretty();

db.tm_routes.find({'routes.route_id':6165}).pretty()

also db.tm_routes.find({'routes.route_id':6165}).count() is 0.

The following query returns every document in the array

db.tm_routes.find({'routes.route_id':'6165'}).pretty();

{
    "_id" : ObjectId("564b3300953d9d51429163c3"),
    "agency_key" : "DDOT",
    "routes" : [
        {
            "route_id" : "6165",
            "route_type" : "3",
            "trips" : [ ]
        },
        {
            "route_id" : "6170",
            "route_type" : "3",
            "trips" : [ ]
        },
        ...
        ]}

but db.tm_routes.find({'routes.route_id':'6165'}).count() returns 1.

And finally, here is how I inserted data in the first place -in Node.JS-:

async.waterfall([
...
//RETRIEVE ALL ROUTEIDS FOR EVERY AGENCY
        function(agencyKeys, callback) {
          var routeIds = [];
          var routesArr = [];
          var routes = db.collection('routes'); 
//CALL GETROUTES FUNCTION FOR EVERY AGENCY
          async.map(agencyKeys, getRoutes, function(err, results){
            if (err) throw err;
            else {
              callback(null, results);
            }
          });
//GET ROUTE IDS
          function getRoutes(agencyKey, callback){
            var cursor = routes.find({agency_key:agencyKey});
            cursor.toArray(function(err, docs){
              if(err) throw err;
              for(i in docs){
                routeIds.push(docs[i].route_id);
                var routeObj = {
                  route_id:docs[i].route_id,
                  route_type:docs[i].route_type,
                  trips:[]
                };
                routesArr.push(routeObj);
    /* I TRIED 3 DIFFERENT WAYS TO PUSH DATA
//1->


        collection.update({agency_key:agencyKey}, {$push:{"routes":{
                  'route_id':docs[i].route_id,
                  'route_type':docs[i].route_type,
                  'trips':[]
                }}});
    //2->

collection.update({agency_key:agencyKey}, {$push:{"routes":routeObj}});

    */
                  }
    // 3->
                  collection.update({agency_key:agencyKey}, {$push:{routes:{$each:routesArr}}});
                  callback(null, routeIds);
                });
              };
            },
...

    var collection = newCollection(db, 'tm_routes',[]);
    function newCollection(db, name, options){
      var collection = db.collection(name);
      if (collection){
        collection.drop();
      }
      db.createCollection(name, options);
      return db.collection(name);
    }

Note: I am not using Mongoose and don't want to use if possible.


Solution

  • Melis,

    I see what you are asking for, and what you need is help understanding how things are stored in mongodb. Things to understand:

    • A document is the basic unit of data for MongoDB and can be roughly compared to a row in a relational database.
    • A collection can be thought of as a table with a dynamic schema

    So documents are stored in collections.Every document has a special _id, that is unique within a collection. What you showed us above in the following format is One document.

    {
        "_id" : ObjectId("564b3300953d9d51429163c3"),
        "agency_key" : "DDOT",
        "routes" : [
            {
                "route_id" : "6165",
                "route_type" : "3",
                "trips" : [ ]
            },
            {
                "route_id" : "6170",
                "route_type" : "3",
                "trips" : [ ]
            },
            ...
    ]}
    

    If you run a query in your tm_routes collection. The find() will return each document in the collection that matches that query. Therefore when you run the query db.tm_routes.find({'routes.route_id':'6165'}).pretty(); it is returning the entire document that matches the query. Therefore this statement is wrong:

    The following query returns every document in the array

    If you need to find a specific route in that document, and only return that route, depending on your use, because its an array, you may have to use the $-Positional Operator or the aggregation framework.