Вы в основном делаете это неправильно и вместо этого должны запрашивать модель Recipe
. У вас уже есть "значения идентификатора категории" , которые содержатся в массиве этого документа.
В основном у вас должно быть что-то вроде этого:
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
со «списком идентификаторов» как в форме запроса, так и в форме оператора агрегирования на разных этапах. Лично я закодировал бы это таким образом, даже если бы в настоящее время было предоставлено только одно значение, поскольку это означает, что будет мало что изменить, кроме переменной input , на метод в событии, которое я хотелнапример, несколько значений, таких как «несколько категорий» в параметре граненого поиска.
Таким образом, демонстрируется подход аргумента «список», хотя он по-прежнему применяется к единичным значениям, как в вопросе.
Весь процесс следует тому, что говорится в комментариях на каждом этапе конвейера, так как вы сначала сопоставляете требуемые «документы» из коллекции рецептов по выбранным «категориям» значений. Затем мы просто хотим удалить все несоответствующие данные для категории в массиве этих документов. На самом деле это можно рассматривать как «необязательный», если вы хотите просто показать ВСЕ категории, связанные с этим рецептом, независимо от того, соответствуют они критериям или нет. Если это так, все, что вам нужно сделать, это удалить этап, содержащий оператор $filter
, и код будет успешно работать таким образом.
Тогда, конечно, существуют этапы $lookup
, являющиеся единым целым. для каждой связанной коллекции. Пример, приведенный здесь, на самом деле показывает выразительную форму этапа $lookup
конвейера. Это опять-таки действительно только для демонстрации, поскольку стандартные формы localField
и foreignField
идеально подходят для целей, которые вы хотите здесь сделать, и дальнейший синтаксис не нужен. MongoDB в основном преобразует этот старый синтаксис в более новую выразительную форму, как показано внутренне в любом случае.
Вы можете заметить использование Model.collection.name
в аргументе from
. Это на самом деле удобно при кодировании с mongoose . Сам MongoDB ожидает фактическое имя коллекции в качестве аргумента здесь. Так как 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();
}
})()