2

I hope someone can shed some light on this issue that I have, it's driving me crazy to a point that I have been spending the past three days, learning more and more about mongoDB but still can't figure out this simple query.

What I need to do is to get the object containing the "carId" = "3C".

In other words the object that I want the query to return is:

            {
                "carId" : "3C", 
                "_id" : ObjectId("51273329b64f07a40ef1c15e")
            }

Here is the dataset (cars):

   { 
        "_id" : ObjectId("56223329b64f07a40ef1c15c"), 
        "username" : "john", 
        "email" : "[email protected]", 
        "accounts" : [
            {
                "_id" : ObjectId("56322329b61f07a40ef1c15d"), 
                "cars" : [
                    {
                        "carId" : "6A", 
                        "_id" : ObjectId("56323329b64f07a40ef1c15e")
                    },
                    {
                        "carId" : "6B", 
                        "_id" : ObjectId("56323329b64f07a40ef1c15e")
                    }
                ]
            }
        ]
    },
    { 
        "_id" : ObjectId("56223125b64f07a40ef1c15c"), 
        "username" : "paul", 
        "email" : "[email protected]", 
        "accounts" : [
            {
                "_id" : ObjectId("5154729b61f07a40ef1c15d"), 
                "cars" : [
                    {
                        "carId" : "5B", 
                        "_id" : ObjectId("56323329854f07a40ef1c15e")
                    }
                ]
            },
            {
                "_id" : ObjectId("56322117b61f07a40ef1c15d"), 
                "cars" : [
                    {
                        "carId" : "6G", 
                        "_id" : ObjectId("51212929b64f07a40ef1c15e")
                    },
                    {
                        "carId" : "3C", 
                        "_id" : ObjectId("51273329b64f07a40ef1c15e")
                    },
                    {
                        "carId" : "4N", 
                        "_id" : ObjectId("51241279b64f07a40ef1c15e")
                    }
                ]
            }
        ]
    }

Please note that I have two nested arrays, and apparently MongoDb lacks when it comes to dealing with Projections with deep arrays. The $ operator can only be used once in a projection; leaving with no clues as how to to achieve this simple task.

So again I want to find --only-- the document that has "carId" : "3C" and only return the immediate obj containing the "carId" : "3C". but not the parent objects.

Any help would be so much appreciated. Possibly using either direct MongoDb or Mongoose. Mongoose would be preferred.

As for reference, I have already covered these other related issues wasn't able to figure it out.

Updating a deep record in MongoDb

How to Update Multiple Array Elements in mongodb

Hope in the future, this question and your solutions will help others.

2 Answers 2

8

Amir,

You must use the Aggregation Framework. You can build a pipeline that processes a stream documents through several building blocks: filtering, projecting,grouping,sorting,etc.

When dealing with nested arrays you will have to use the $unwind command. You can get what you want by doing the following.

db.cars.aggregate(
    //De-normalized the nested array of accounts
    {"$unwind": "$accounts"},
    //De-normalized the nested array of cars
    {"$unwind": "$accounts.cars"},
    //match carId to 3C
    {"$match": {"accounts.cars.carId" : "3C"}},
    //Project the accoutns.cars object only
    {"$project" : {"accounts.cars" : 1}},
    //Group and return only the car object
    {"$group":{"_id":"$accounts.cars"}}
).pretty();

You can use the aggregation framework for "array filtering" by using $unwind .

You can delete each line from the bottom of each command in the aggregation pipeline in the above code to observe the pipelines behavior.

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

6 Comments

Thank you so much, this sounds great; is there a way to achieve the same with Mongoose?
Yes. You can use it with mongoose. I'll add to this answer as soon as I get to my laptop.
Just tried this aggregation and it's working like a charm except for one small point. It returns this: { "_id" : ObjectId("56223125b64f07a40ef1c15c"), "accounts" : { "cars" : { "carId" : "3C", "_id" : ObjectId("51273329b64f07a40ef1c15e") } } } instead of the expected object: {"cars" : { "carId" : "3C", "_id" : ObjectId("51273329b64f07a40ef1c15e") } . So from this point, the carId obj is easily accessible with doc.accounts.cars how should this be changed to get the desired result. ...? thanks
Once again I would like to thank you so much. You are a hero. -- knowing the Mongoose syntax will help very much also as I'm using mongoose in my code. thanks
@AmirMog I just changed the answer to only return the one object. Please let me know if it works.
|
1

Here's an example without the aggregation framework. I don't think there's a way purely from querying that you'll be able to get just the individual nested object you're looking for so you have to do a little post processing work. something like Mongoose may provide a way to do this but I'm not really up on what the Mongoose API's look like currently.

var doc = db.cars.findOne({"accounts.cars" : {$elemMatch: {"carId" : "3C"}}}, {"accounts.cars.$": 1, _id: 0})
var car = doc.accounts[0].cars[0]

7 Comments

Would this work without knowing the index of the obj containing "carId" : "3C"? I believe var car = doc.accounts[0].cars[0] would not work, or am I missing something?
I tested using the example docs you have. It did work because the projection is only going to return the first element that matches the query. You should probably check for nulls.
I just modified the example docs so that the { "carId" : "3C", "_id" : ObjectId("51273329b64f07a40ef1c15e") }, would not be at index 0 in the array. Would this still work?
I just checked this and the projection actually returns all three objects inside "cars" array. It doesn't return only one. So trying to access it with index won't help. How can make the projection return the array of "cars" with only one item that being the { "carId" : "3C", "_id" : ObjectId("51273329b64f07a40ef1c15e") } ? thanks
Sorry. Thought the $elemMatch operator would work here but it appears its not going to apply to the cars array, only the accounts array. without restructuring data, Aggregation seems like the way to achieve this. If you mark the other answer as the accepted one ill probably delete this since I don't think this approach will work.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.