На самом деле существуют «две» проблемы, которые не рассматриваются при таком условии.
Первый заключается в том, что в качестве отдельных аргументов нет ничего, чтобы сказать, что значения для каждого свойства должны быть в пределах одного и того же элемента, или фактически, если конкретная комбинация действительно появляется в этом элементе. Чтобы исправить, что вы используете $elemMatch
условия внутри $or
выражения:
var query = {
"$or": [
{ "topic": {
"$elemMatch": {
"topicName": "Reproduction in plants",
"subTopic": "Pollination"
}
}},
{ "topic": {
"$elemMatch": {
"topicName": "Plant Cycle",
"subTopic": "Photosynthesis"
}
}}
]
}
По крайней мере, выбирает документы с комбинацией в элементы, которые вы требуете.
Но ничего не может «фильтровать» несколько результатов, от внешнего "topic"
или «внутреннего» "subTopic"
массивов. Для выполнения этой задачи вам необходима рамка агрегации, поскольку такие функции не доступны для базовой проекции:
var pipeline = [
// Still use the same match to filter possible documents
{ "$match": {
"$or": [
{ "topic": {
"$elemMatch": {
"topicName": "Reproduction in plants",
"subTopic": "Pollination"
}
}},
{ "topic": {
"$elemMatch": {
"topicName": "Plant Cycle",
"subTopic": "Photosynthesis"
}
}}
]
}},
// Filter the arrays for matches
{ "$project": {
"topics": {
"$filter": {
"input": {
"$map": {
"input": "$topic",
"as": "topic",
"in": {
"topicName": "$$topic.topicName",
"subTopic": {
"$filter": {
"input": "$$topic.subTopic",
"as": "subTopic",
"cond": {
"$or": [
{ "$and": [
{ "$eq": [ "$$topic.topicName", "Reproduction in plants" ] },
{ "$eq": [ "$$subTopic", "Pollination" ] }
]},
{ "$and": [
{ "$eq": [ "$$topic.topicName", "Plant Cycle" ] },
{ "$eq": [ "$$subTopic", "Photosynthesis" ] }
]}
]
}
}
}
}
}
},
"as": "topic",
"cond": {
"$and": [
{ "$or": [
{ "$eq": [ "$$topic.topicName", "Reproduction in plants" ] },
{ "$eq": [ "$$topic.topicName", "Plant Cycle" ] }
]},
{ "$ne": [ "$$topic.subTopic", [] ] }
]
}
}
}
}}
];
// API call to aggregate
Model.aggregate(pipeline,function(err,results) {
// results in here
});
Thats наиболее оптимального подхода с MongoDB 3.2 с использованием $filter
операции на массивах. Поэтому сначала обратите внимание, что внутренние элементы "subTopic"
проверяются на соответствие условиям вместе с внешним элементом, чтобы решить, какие из них нужно вернуть.Это помещается в пределах $map
, так что «фильтрованное» содержимое возвращается к свойству внешнего массива для дальнейшего изучения.
Внешний массив затем «фильтруется», так что возвращаются только соответствующие значения "topicName"
и, конечно, только если массив "subTopic"
не был «пустым» в результате фильтрации.
Можно сделать это в более ранних версиях, но типичный процесс с $unwind
получает очень долго и дорого:
{ "$unwind": "$topic" },
{ "$unwind": "$topic.subTopic" },
{ "$match": {
"$or": [
{
"topic.topicName": "Reproduction in plants",
"topic.subTopic": "Pollination"
},
{
"topic.topicName": "Plant Cycle",
"topic.subTopic": "Photosynthesis"
}
]
}},
{ "$group": {
"_id": {
"_id": "$_id",
"topicName": "$topic.topicName",
},
"subTopic": { "$push": "$topic.subTopic" }
}},
{ "$group": {
"_id": "$_id._id",
"topic": {
"$push": {
"topicName": "$_id.topicName",
"subTopic": "$_id.subTopic"
}
}
}}
Хотя это выглядит проще следовать, это много дороже из-за природа чего $unwind
. И, конечно, каждая добавленная стадия конвейера агрегации имеет свои собственные затраты на обработку, тогда как современная версия может сделать это простым $project
.
Лучше всего, если у вас есть более ранняя версия, чтобы использовать начальный «запрос», как указано, используя как $or
, так и $elemMatch
, а затем выполните фильтрацию массива в коде.
Если, конечно, вам не нужны эти данные для дальнейшей обработки в конвейере агрегации, где вы бы застряли с этим процессом, чтобы «фильтровать».
В любом случае, результат вы получите:
{
"topic": [
{
"topicName": "Reproduction in plants",
"subTopic": ["Pollination"]
},
{
"topicName": "Plant Cycle",
"subTopic": ["Photosynthesis"]
}
]
}
который возвращает только те элементы, соответствующие «отфильтрованные» в любом документе.
Точно ответ, на который я долгое время. Спасибо за разработанный ответ. Я приложу к фильтрации в коде, что также довольно сложно. – minheq
@minheq Как сказано, фильтрация действительно является вспомогательной для фактического первоначального выбора запроса для правильного соответствия на ваших условиях. На самом деле ваши условия в значительной степени определяют, каково должно быть результирующее содержимое массива в любом случае, поэтому проще просто предположить, что это возвращаемые данные, а не манипулировать данными тем, что должно быть заранее заданным результатом. –
Я думаю, что вы правы, я закончил писать алгоритм, но, во-первых, он не очень хорошо разбирается в коде. И я оцениваю, есть ли действительная ценность для бизнеса, чтобы принудительно реализовать такую фильтрацию, которая могла бы замедлить все запросы для «вопросов». Кроме того, маловероятно, что существует одно и то же имя субтипа под двумя разными темами. Запрос должен быть правильным, но я рассматриваю вопрос, будет ли проще просто фильтровать «вычет» в интерфейсе. ** Еще раз спасибо за ваш вклад. ** – minheq