Поиск в Mongoose, извлекающий данные из нескольких коллекций - PullRequest
0 голосов
/ 01 ноября 2019

Когда я объединяю 2 таблицы, чтобы получить данные из коллекции mongoDB, я получил ожидаемый результат. Пожалуйста, помогите мне исправить то же самое.

Коллекция:

recipecatagories

{
    "_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"}
}

Текущий код Mongoose в узле 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
                }
            ]
        }
    ]
}

Я поражен, чтобы получить ожидаемый результат ... Я хочу, чтобы рецепты были массивом, который имеет recipecategory в коллекции рецептов ...

1 Ответ

1 голос
/ 02 ноября 2019

Вы в основном делаете это неправильно и вместо этого должны запрашивать модель 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();
  }

})()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...