Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose Query: Find an element inside an array

Mongoose/Mongo noob here:

My Data

Here is my simplified data, each user has his own document

{ "__v" : 1,
  "_id" : ObjectId( "53440e94c02b3cae81eb0065" ),
  "email" : "[email protected]",
  "firstName" : "testFirstName",
  "inventories" : [ 
    { "_id" : "active",
      "tags" : [ 
        "inventory", 
        "active", 
        "vehicles" ],
      "title" : "activeInventory",
      "vehicles" : [ 
        { "_id" : ObjectId( "53440e94c02b3cae81eb0069" ),
          "tags" : [ 
            "vehicle" ],
          "details" : [ 
            { "_id" : ObjectId( "53440e94c02b3cae81eb0066" ),
              "year" : 2007,
              "transmission" : "Manual",
              "price" : 1000,
              "model" : "Firecar",
              "mileageReading" : 50000,
              "make" : "Bentley",
              "interiorColor" : "blue",
              "history" : "CarProof",
              "exteriorColor" : "blue",
              "driveTrain" : "SWD",
              "description" : "test vehicle",
              "cylinders" : 4,
              "mileageType" : "kms" } ] } ] }, 
    { "title" : "soldInventory",
      "_id" : "sold",
      "vehicles" : [],
      "tags" : [ 
        "inventory", 
        "sold", 
        "vehicles" ] }, 
    { "title" : "deletedInventory",
      "_id" : "deleted",
      "vehicles" : [],
      "tags" : [ 
        "inventory", 
        "sold", 
        "vehicles" ] } ] }

As you can see, each user has an inventories property that is an array that contains 3 inventories (activeInventory, soldInventory and deletedInventory)

My Query

Given an user's email a a vehicle ID, i would like my query to go through find the user's activeInventory and return just the vehicle that matches the ID. Here is what I have so far:

user = api.mongodb.userModel;
ObjectId = require('mongoose').Types.ObjectId;
return user
    .findOne({email : params.username})
    .select('inventories')
    .find({'title': 'activeInventory'})
    //also tried
    //.where('title')
    //.equals('activeInventory')
    .exec(function(err, result){
        console.log(err);
        console.log(result);
    });

With this, result comes out as an empty array. I've also tried .find('inventories.title': 'activeInventory') which strangely returns the entire inventories array. If possible, I'd like to keep the chaining query format as I find it much more readable.

My Ideal Query

return user
    .findOne({email : params.username})
    .select('inventories')
    .where('title')
    .equals('activeInventory')
    .select('vehicles')
    .id(vehicleID)
    .exec(cb)

Obviously it does not work but it can give you an idea what I'm trying to do.

like image 861
NicolasMoise Avatar asked Apr 08 '14 15:04

NicolasMoise


People also ask

How do I find a particular element in an array in MongoDB?

To query if the array field contains at least one element with the specified value, use the filter { <field>: <value> } where <value> is the element value. To specify conditions on the elements in the array field, use query operators in the query filter document: { <array field>: { <operator1>: <value1>, ... } }

How do I filter an array element in MongoDB?

Filter MongoDB Array Element Using $Filter Operator This operator uses three variables: input – This represents the array that we want to extract. cond – This represents the set of conditions that must be met. as – This optional field contains a name for the variable that represent each element of the input array.

Does find return an array Mongoose?

Return valueThe returned value could be an array of documents, a single document if it matches only one, or an empty array if no document is matched.

How do I find a specific value in MongoDB?

Find() Method. In MongoDB, find() method is used to select documents in a collection and return a cursor to the selected documents. Cursor means a pointer that points to a document, when we use find() method it returns a pointer on the selected documents and returns one by one.


2 Answers

Using the $ positional operator, you can get the results. However, if you have multiple elements in the vehicles array all of them will be returned in the result, as you can only use one positional operator in the projection and you are working with 2 arrays (one inside another).

I would suggest you take a look at the aggregation framework, as you'll get a lot more flexibility. Here's an example query for your question that runs in the shell. I'm not familiar with mongoose, but I guess this will still help you and you'd be able to translate it:

db.collection.aggregate([
    // Get only the documents where "email" equals "[email protected]" -- REPLACE with params.username
    {"$match" : {email : "[email protected]"}}, 
    // Unwind the "inventories" array
    {"$unwind" : "$inventories"}, 
    // Get only elements where "inventories.title" equals "activeInventory"
    {"$match" : {"inventories.title":"activeInventory"}}, 
    // Unwind the "vehicles" array
    {"$unwind" : "$inventories.vehicles"}, 
    // Filter by vehicle ID -- REPLACE with vehicleID 
    {"$match" : {"inventories.vehicles._id":ObjectId("53440e94c02b3cae81eb0069")}}, 
    // Tidy up the output
    {"$project" : {_id:0, vehicle:"$inventories.vehicles"}}
])

This is the output you'll get:

{
        "result" : [
                {
                        "vehicle" : {
                                "_id" : ObjectId("53440e94c02b3cae81eb0069"),
                                "tags" : [
                                        "vehicle"
                                ],
                                "details" : [
                                        {
                                                "_id" : ObjectId("53440e94c02b3cae81eb0066"),
                                                "year" : 2007,
                                                "transmission" : "Manual",
                                                "price" : 1000,
                                                "model" : "Firecar",
                                                "mileageReading" : 50000,
                                                "make" : "Bentley",
                                                "interiorColor" : "blue",
                                                "history" : "CarProof",
                                                "exteriorColor" : "blue",
                                                "driveTrain" : "SWD",
                                                "description" : "test vehicle",
                                                "cylinders" : 4,
                                                "mileageType" : "kms"
                                        }
                                ]
                        }
                }
        ],
        "ok" : 1
}
like image 118
Anand Jayabalan Avatar answered Oct 15 '22 18:10

Anand Jayabalan


getting the chaining query format ... i dont know how to parse it but, what you are searching for is projection, you should take a look to http://docs.mongodb.org/manual/reference/operator/projection/

it would probably look like this :

user.findOne({email: params.username}, {'inventories.title': {$elemMatch: "activeInventory", 'invertories.vehicle.id': $elemMatch: params.vehicleId}, function(err, result) {
    console.log(err);
    console.log(result);
})
like image 23
Ostro Avatar answered Oct 15 '22 18:10

Ostro