Mongoose Query: Find an element inside an array

2020-07-14 09:48发布

问题:

Mongoose/Mongo noob here:

My Data

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

{ "__v" : 1,
  "_id" : ObjectId( "53440e94c02b3cae81eb0065" ),
  "email" : "test@test.com",
  "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.

回答1:

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 "test@test.com" -- REPLACE with params.username
    {"$match" : {email : "test@test.com"}}, 
    // 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
}


回答2:

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);
})