【问题标题】:Mongoose lookup fetching data from multi collectionMongoose 查找从多个集合中获取数据
【发布时间】:2019-11-02 10:48:50
【问题描述】:

当我结合 2 个表从 mongoDB 集合中获取数据时,我的预期结果出乎意料。请任何人帮助我解决相同的请求。

收藏:

食谱目录

{
    "_id":{"$oid":"5dada3c5761bb32a1201d4da"},
    "CategoryName":"Biryani"
}
{
    "_id":{"$oid":"5dada3c5761bb32a1201d4db"},
    "CategoryName":"Mutton Biryani"
}
{
    "_id":{"$oid":"5dada3c5761bb32a1201d4d4"},
    "CategoryName":"Chicken Biryani"
}
{
    "_id":{"$oid":"5daea43a517cf601a7e80a3b"},
    "CategoryName":"Kathirikai gothsu"
}

食谱:

{
    "_id":{"$oid":"5daffda85d9b4fd19ae4da30"},
    "recipeTitle":"Mutton dum biryani",
    "Recipetags":["Indian","NonVeg","Lunch"],
    "cookTime":"30 Mins",
    "recipeCategoryId":[{"$oid":"5dada3c5761bb32a1201d4da"},{"$oid":"5dada3c5761bb32a1201d4db"},{"$oid":"5dada3c5761bb32a1201d4dc"}],
    "recipeCuisienId":"Indian",
    "recepeType":false,
    "availaleStreaming":"TEXT",
    "postedOn":{"$date":{"$numberLong":"0"}},
    "postedBy":"shiva@yopmail.com"
}
{
    "_id":{"$oid":"5daffda85d9b4fd19ae4da30"},
    "recipeTitle":"Mutton Chicken biryani",
    "Recipetags":["Indian","NonVeg","Lunch"],
    "cookTime":"30 Mins",
    "recipeCategoryId":[{"$oid":"5dada3c5761bb32a1201d4da"},{"$oid":"5dada3c5761bb32a1201d4d4"},{"$oid":"5dada3c5761bb32a1201d4dc"}],
    "recipeCuisienId":"Indian",
    "recepeType":false,
    "availaleStreaming":"TEXT",
    "postedOn":{"$date":{"$numberLong":"0"}},
    "postedBy":"shiva@yopmail.com"
}

用户:

{
    "_id":{"$oid":"5da428b85e3cbd0f153c7f3b"},
    "emailId":"shiva@yopmail.com",
    "fullName":"siva prakash",
    "accessToken":"xxxxxxxxxxxxx",
    "__v":{"$numberInt":"0"}
}

node js 中的当前猫鼬代码

RecipeCatagory.aggregate([
        { "$match": { "_id": mongoose.Types.ObjectId(id) } },
        {
            "$lookup": {
                "from": "recipes",
                "localField": "_id",
                "foreignField": "recipeCategoryId",
                "as": "recipes"
            }
        },
        { "$unwind": "$recipes" },
        { "$unwind": "$recipes.recipeCategoryId" },
        {
            "$match": {
                "recipes.recipeCategoryId": mongoose.Types.ObjectId(id)
            }
        },
        {
            "$lookup": {
                "from": "users",
                "localField": "emailId",
                "foreignField": "recipes.postedBy",
                "as": "users"
            }
        },

    ])
        .exec(function (err, recipes) {
            if (err) {
                response
                    .status(400)
                    .json({
                        "status": "Failed",
                        "message": "Error",
                        "data": err | err.message
                    });
                return
            } else {
                response
                    .status(200)
                    .json({
                        "status": "Ok",
                        "message": "Success",
                        "data": recipes
                    });
                return
            }
        })

使用上述查询的当前输出

{
    "status": "Ok",
    "message": "Success",
    "data": [
        {
            "_id": "5dada3c5761bb32a1201d4da",
            "CategoryName": "Biryani",
            "recipes": {
                "_id": "5daffda85d9b4fd19ae4da30",
                "recipeTitle": "Mutton dum biryani",
                "Recipetags": [
                    "Indian",
                    "NonVeg",
                    "Lunch"
                ],
                "cookTime": "30 Mins",
                "recipeCategoryId": "5dada3c5761bb32a1201d4da",
                "recipeCuisienId": "Indian",
                "recepeType": false,
                "availaleStreaming": "TEXT",
                "postedOn": "1970-01-01T00:00:00.000Z",
                "postedBy": "shiva@yopmail.com"
            },
            "users": [
                {
                    "_id": "5da428b85e3cbd0f153c7f3b",
                    "emailId": "shiva@yopmail.com",
                    "fullName": "siva prakash",
                    "accessToken": "42eb19a0-ee57-11e9-86f7-a7b758fb7271",
                    "__v": 0
                }
            ]
        },
        {
            "_id": "5dada3c5761bb32a1201d4da",
            "CategoryName": "Biryani",
            "recipes": {
                "_id": "5daffda85d9b4fd19ae4da31",
                "recipeTitle": "Kumbakonam kathirikai gothsu",
                "Recipetags": [
                    "Indian",
                    "Veg"
                ],
                "cookTime": "30 Mins",
                "recipeCategoryId": "5dada3c5761bb32a1201d4da",
                "recipeCuisienId": "Indian",
                "recepeType": true,
                "availaleStreaming": "TEXT",
                "postedOn": "1970-01-01T00:00:00.000Z",
                "postedBy": "shiva@yopmail.com"
            },
            "users": [
                {
                    "_id": "5da428b85e3cbd0f153c7f3b",
                    "emailId": "shiva@yopmail.com",
                    "fullName": "siva prakash",
                    "accessToken": "xxxxxxxxxxxxx",
                    "__v": 0
                }
            ]
        }
    ]
}


**Expected Out:**
    {
    "status": "Ok",
    "message": "Success",
    "data": [
        {
            "_id": "5dada3c5761bb32a1201d4da",
            "CategoryName": "chiken Biryani",
            "recipes": {
                "_id": "5daffda85d9b4fd19ae4da30",
                "recipeTitle": "Mutton dum biryani",
                "Recipetags": [
                    "Indian",
                    "NonVeg",
                    "Lunch"
                ],
                "cookTime": "30 Mins",
                "recipeCategoryId": "5dada3c5761bb32a1201d4da",
                "recipeCuisienId": "Indian",
                "recepeType": false,
                "availaleStreaming": "TEXT",
                "postedOn": "1970-01-01T00:00:00.000Z",
                "postedBy": "shiva@yopmail.com"
            },
            "users": [
                {
                    "_id": "5da428b85e3cbd0f153c7f3b",
                    "emailId": "shiva@yopmail.com",
                    "fullName": "siva prakash",
                    "accessToken": "42eb19a0-ee57-11e9-86f7-a7b758fb7271",
                    "__v": 0
                }
            ]
        },
        {
            "_id": "5dada3c5761bb32a1201d4da",
            "CategoryName": "Biryani",
            "recipes": [
                {
                "_id": "5daffda85d9b4fd19ae4da31",
                "recipeTitle": "Mutton dum biryani",
                "Recipetags": [
                    "Indian",
                    "Veg"
                ],
                "cookTime": "30 Mins",
                "recipeCategoryId": "5dada3c5761bb32a1201d4da",
                "recipeCuisienId": "Indian",
                "recepeType": true,
                "availaleStreaming": "TEXT",
                "postedOn": "1970-01-01T00:00:00.000Z",
                "postedBy": "shiva@yopmail.com"
                },
                {
                "_id": "5daffda85d9b4fd19ae4da31",
                "recipeTitle": "Chicken biryani",
                "Recipetags": [
                    "Indian",
                    "Veg"
                ],
                "cookTime": "30 Mins",
                "recipeCategoryId": "5dada3c5761bb32a1201d4da",
                "recipeCuisienId": "Indian",
                "recepeType": true,
                "availaleStreaming": "TEXT",
                "postedOn": "1970-01-01T00:00:00.000Z",
                "postedBy": "shiva@yopmail.com"
                }
            ],
            "users": [
                {
                    "_id": "5da428b85e3cbd0f153c7f3b",
                    "emailId": "shiva@yopmail.com",
                    "fullName": "siva prakash",
                    "accessToken": "xxxxxxxxxxxx",
                    "__v": 0
                }
            ]
        }
    ]
}

我很惊讶得到预期的输出...我想要食谱作为具有食谱类别的数组在食谱集合中...

【问题讨论】:

  • 你能在问题中添加猫鼬模型(食谱、食谱)吗?

标签: node.js mongodb express mongoose


【解决方案1】:

您基本上是以错误的方式执行此操作,而应该从 Recipe 模型中查询。您已经拥有包含在该文档的数组中的“类别 ID 值”

基本上你应该有这样的东西:

const wantedCategories = [
  ObjectId("5dada3c5761bb32a1201d4da"),
  ObjectId("5dada3c5761bb32a1201d4db")
];

let data = await Recipe.aggregate([
  // Match wanted category(ies)
  { "$match": {
    "recipeCategoryId": { "$in": wantedCategories }
  }},
  // Filter the content of the array
  { "$addFields": {
    "recipeCategoryId": {
      "$filter": {
        "input": "$recipeCategoryId",
        "cond": {
          "$in": [ "$$this", wantedCategories ]
        }
      }
    }
  }},
  // Lookup the related matching category(ies)
  { "$lookup": {
    "from": RecipeCategory.collection.name,
    "let": { "recipeCategoryIds": "$recipeCategoryId" },
    "pipeline": [
      { "$match": {
        "$expr": { "$in": [ "$_id", "$$recipeCategoryIds" ] }
      }}
    ],
    "as": "recipeCategoryId"
  }},
  // Lookup the related user to postedBy
  { "$lookup": {
    "from": User.collection.name,
    "let": { "postedBy": "$postedBy" },
    "pipeline": [
      { "$match": { "$expr": { "$eq": [ "$emailId", "$$postedBy" ] } } }
    ],
    "as": "postedBy"
  }},
  // postedBy is "singular"
  { "$unwind": "$postedBy" }
]);

这会返回如下结果:

{
  "data": [
    {
      "_id": "5dbce992010163139853168c",
      "Recipetags": [
        "Indian",
        "NonVeg",
        "Lunch"
      ],
      "recipeCategoryId": [
        {
          "_id": "5dada3c5761bb32a1201d4da",
          "CategoryName": "Biryani",
          "__v": 0
        },
        {
          "_id": "5dada3c5761bb32a1201d4db",
          "CategoryName": "Mutton Biryani",
          "__v": 0
        }
      ],
      "recipeTitle": "Mutton dum biryani",
      "cookTime": "30 Mins",
      "recepeType": false,
      "postedBy": {
        "_id": "5dbce992010163139853168e",
        "emailId": "shiva@yopmail.com",
        "fullName": "siva prakash",
        "accessToken": "xxxxxxxxxxxxx",
        "__v": 0
      },
      "__v": 0
    },
    {
      "_id": "5dbce992010163139853168d",
      "Recipetags": [
        "Indian",
        "NonVeg",
        "Lunch"
      ],
      "recipeCategoryId": [
        {
          "_id": "5dada3c5761bb32a1201d4da",
          "CategoryName": "Biryani",
          "__v": 0
        }
      ],
      "recipeTitle": "Mutton Chicken biryani",
      "cookTime": "30 Mins",
      "recepeType": false,
      "postedBy": {
        "_id": "5dbce992010163139853168e",
        "emailId": "shiva@yopmail.com",
        "fullName": "siva prakash",
        "accessToken": "xxxxxxxxxxxxx",
        "__v": 0
      },
      "__v": 0
    }
  ]
}

注意:我确实用RecipeCategory 而不是RecipeCatagory 更正了模型的英文拼写,如问题所示。根据需要应用于您自己的实现。

您可能会注意到$in 在不同阶段的查询表单和聚合运算符表单中都使用了“id 列表”。就我个人而言,即使目前只提供了一个值,我也会以这种方式进行编码,因为这意味着除了 input 变量之外几乎没有什么可以改变的想要多个值,例如分面搜索选项中的“多个类别”。

因此,这演示了“列表”参数方法,尽管它仍然适用于问题中的奇异值。

整个过程遵循 cmets 在每个管道阶段所说的内容,即您首先通过选定的 "category" 值匹配 recipes 集合中想要的“文档” (s)。然后我们只想删除那些文档数组中 category 的所有 不匹配 数据。如果您只想显示与该食谱相关的所有类别,无论它们是否符合标准,这实际上可以被视为“可选”。在这种情况下,您只需删除包含$filter 语句的阶段,代码就会以这种方式正常工作。

当然还有$lookup 阶段,每个相关 集合都有一个阶段。这里的示例实际上展示了$lookup 管道阶段的表达形式。这实际上只是为了演示,因为标准的localFieldforeignField 形式非常适合您在此处执行的操作,并且不需要进一步的语法。无论如何,MongoDB 基本上会将旧语法转换为新的表达形式,如内部所示。

您可能会注意到 Model.collection.namefrom 参数中的用法。在使用 mongoose 进行编码时,这实际上是一件很方便的事情。 MongoDB 本身期望实际的 collection name 作为这里的参数。由于 mongoose 通常会将模型名称复数为引用的实际集合提供的模型名称,或者以其他方式为模型定义提供显式参数,因此使用模型中的 .collection.name 访问器可确保您拥有正确的实际集合名称,即使这在模型定义中的某个时间发生了变化。

这里唯一的另一个简单步骤是最后的$unwind,只是因为$lookup的输出是总是一个数组,这里将postedBy属性替换为匹配的相关内容总是预计只有一个项。因此,为了结果的简单可读性,我们可以将其设为单个值,而不是在此处使用数组。


关于这一切如何结合在一起的更多上下文,这里是语句的代码和数据的创建都在一个自包含的列表中,当然上面发布的“输出”实际上是从中获得的:

const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');

const uri = 'mongodb://localhost:27017/menu';
const options = { useNewUrlParser: true, useUnifiedTopology: true };

mongoose.set('debug', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);

const recipeCategorySchema = new Schema({
  CategoryName: String
});

const recipeSchema = new Schema({
  recipeTitle: String,
  Recipetags: [String],
  cookTime: String,
  recipeCategoryId: [{ type: Schema.Types.ObjectId, ref: 'RecipeCategory' }],
  recipeCuisineId: String,
  recepeType: Boolean,
  availableStreaming: String,
  postedBy: String
});

const userSchema = new Schema({
  emailId: String,
  fullName: String,
  accessToken: String
});

const RecipeCategory = mongoose.model('RecipeCategory', recipeCategorySchema);
const Recipe = mongoose.model('Recipe', recipeSchema);
const User = mongoose.model('User', userSchema);

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  try {

    const conn = await mongoose.connect(uri, options);

    // Clean data for demonstration
    await Promise.all(
      Object.values(conn.models).map(m => m.deleteMany())
    );

    // Insert some data
    await RecipeCategory.insertMany([
      {
        "_id": ObjectId( "5dada3c5761bb32a1201d4da"),
        "CategoryName":"Biryani"
      },
      {
        "_id": ObjectId("5dada3c5761bb32a1201d4db"),
        "CategoryName":"Mutton Biryani"
      },
      {
        "_id": ObjectId("5dada3c5761bb32a1201d4d4"),
        "CategoryName":"Chicken Biryani"
      },
      {
        "_id": ObjectId("5daea43a517cf601a7e80a3b"),
        "CategoryName":"Kathirikai gothsu"
      }
    ]);

    await Recipe.insertMany([

      {
        "recipeTitle":"Mutton dum biryani",
        "Recipetags":["Indian","NonVeg","Lunch"],
        "cookTime":"30 Mins",
        "recipeCategoryId":[
          ObjectId("5dada3c5761bb32a1201d4da"),
          ObjectId("5dada3c5761bb32a1201d4db"),
          ObjectId("5dada3c5761bb32a1201d4dc")
        ],
        "recipeCuisienId":"Indian",
        "recepeType":false,
        "availaleStreaming":"TEXT",
        "postedOn": new Date(),
        "postedBy":"shiva@yopmail.com"
      },
      {
        "recipeTitle":"Mutton Chicken biryani",
        "Recipetags":["Indian","NonVeg","Lunch"],
        "cookTime":"30 Mins",
        "recipeCategoryId":[
          ObjectId("5dada3c5761bb32a1201d4da"),
          ObjectId("5dada3c5761bb32a1201d4d4"),
          ObjectId("5dada3c5761bb32a1201d4dc")
        ],
        "recipeCuisienId":"Indian",
        "recepeType":false,
        "availaleStreaming":"TEXT",
        "postedOn": new Date(),
        "postedBy":"shiva@yopmail.com"
      }
    ]);

    await User.create({
      "emailId":"shiva@yopmail.com",
      "fullName":"siva prakash",
      "accessToken":"xxxxxxxxxxxxx",
    });

    const wantedCategories = [
      ObjectId("5dada3c5761bb32a1201d4da"),
      ObjectId("5dada3c5761bb32a1201d4db")
    ];

    let data = await Recipe.aggregate([
      // Match wanted category(ies)
      { "$match": {
        "recipeCategoryId": { "$in": wantedCategories }
      }},
      // Filter the content of the array
      { "$addFields": {
        "recipeCategoryId": {
          "$filter": {
            "input": "$recipeCategoryId",
            "cond": {
              "$in": [ "$$this", wantedCategories ]
            }
          }
        }
      }},
      // Lookup the related matching category(ies)
      { "$lookup": {
        "from": RecipeCategory.collection.name,
        "let": { "recipeCategoryIds": "$recipeCategoryId" },
        "pipeline": [
          { "$match": {
            "$expr": { "$in": [ "$_id", "$$recipeCategoryIds" ] }
          }}
        ],
        "as": "recipeCategoryId"
      }},
      // Lookup the related user to postedBy
      { "$lookup": {
        "from": User.collection.name,
        "let": { "postedBy": "$postedBy" },
        "pipeline": [
          { "$match": { "$expr": { "$eq": [ "$emailId", "$$postedBy" ] } } }
        ],
        "as": "postedBy"
      }},
      // postedBy is "singular"
      { "$unwind": "$postedBy" }
    ]);

    log({ data });

  } catch (e) {
    console.error(e)
  } finally {
    mongoose.disconnect();
  }

})()

【讨论】:

  • 非常感谢您提供完美的解决方案....您有正确的解释,这对我来说非常用户...
猜你喜欢
  • 2016-06-20
  • 2016-06-18
  • 2018-08-21
  • 2019-04-11
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
相关资源
最近更新 更多