Mongo Aggregate - соответствие условию только второй коллекции, но предоставление всех документов из первой.
В общем, у меня есть 2 коллекции в моей Mongo DB -> Books , Scores.
Книги
{
"BOOK_ID" : "100",
"BOOK_NAME" : "Book 1",
"BOOK_DESC" : "abcd",
},
{
"BOOK_ID" : "101",
"BOOK_NAME" : "Book 2",
"BOOK_DESC" : "efgh",
},
{
"BOOK_ID" : "102",
"BOOK_NAME" : "Book 3",
"BOOK_DESC" : "ijkl",
}
Баллы
{
"BOOK_ID" : "100",
"BOOK_CATEGORY" : "kids",
"BOOK_SCORE" : "6",
},
{
"BOOK_ID" : "100",
"BOOK_CATEGORY" : "Educational",
"BOOK_SCORE" : "8",
},
{
"BOOK_ID" : "101",
"BOOK_CATEGORY" : "Kids",
"BOOK_SCORE" : "6",
},
{
"BOOK_ID" : "101",
"BOOK_CATEGORY" : "Fantasy",
"BOOK_SCORE" : "7",
}
Ожидаемый результат : Поиск всех книг с BOOKS_CATEGORY="Kids" и BOOKS_SCORE=6
{
"BOOK_ID" : "100",
"BOOK_NAME" : "Book 1",
"BOOK_DESC" : "abcd",
"BOOK_CATEGORY" : "Kids",
"BOOK_SCORE" : 6
},
{
"BOOK_ID" : "101",
"BOOK_NAME" : "Book 2",
"BOOK_DESC" : "efgh",
"BOOK_CATEGORY" : "Kids",
"BOOK_SCORE" : 6
},
{
"BOOK_ID" : "102",
"BOOK_NAME" : "Book 3",
"BOOK_DESC" : "ijkl",
}
Обратите внимание, что для всех книг, для которых доступны оценки, они добавляются. Если с книгой не связана ни одна оценка, она все равно попадает в результат.
Что я пробовал? Я пробовал использовать $lookup
pipeline = [
{
"$lookup": {
"from": "Scores",
"pipeline":[
{
"$match" : {
"BOOK_CATEGORY" : "Kids",
"BOOK_SCORE" : "6",
}
}
],
"localField": "BOOK_ID",
"foreignField": "BOOK_ID",
"as": "SCORES",
},
},
]
db.Books.aggregate(pipeline)
Кроме того, читая документацию по подзапросу $lookup ,(https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#join-conditions-and-subqueries-on-a-joined-collection) У меня возникло ощущение, что то, что я ожидаю, может оказаться невозможным. Может ли кто-нибудь помочь мне с выполнением такого запроса? (Я использую PyMongo btw)
Для последних двух этапов:
$replaceRoot
- Заменить входной документ(ы) новым документом(ами) путем слияния текущего документа с документом, который является первым документом из массиваSCORES
.$unset
- Удалить массивSCORES
.
db.Books.aggregate([
{
"$lookup": {
"from": "Scores",
"pipeline": [
{
"$match": {
"BOOK_CATEGORY": "Kids",
"BOOK_SCORE": "6",
}
}
],
"localField": "BOOK_ID",
"foreignField": "BOOK_ID",
"as": "SCORES"
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$$ROOT",
{
$first: "$$ROOT.SCORES"
}
]
}
}
},
{
$unset: "SCORES"
}
])
Вы можете достичь этого, используя условный $addFields
, если значение $lookup
существует, то заполните значения, в противном случае используйте $$REMOVE
для удаления поля, например, так:
db.Books.aggregate([
{
"$lookup": {
"from": "Scores",
"pipeline": [
{
"$match": {
"BOOK_CATEGORY": "kids",
"BOOK_SCORE": "6"
}
}
],
"localField": "BOOK_ID",
"foreignField": "BOOK_ID",
"as": "SCORES"
}
},
{
$addFields: {
SCORES: "$$REMOVE",
"BOOK_SCORE": {
$cond: [
{
"$ifNull": [
{
"$arrayElemAt": [
"$SCORES",
0
]
},
false
]
},
{
$getField: {
field: "BOOK_SCORE",
input: {
"$arrayElemAt": [
"$SCORES",
0
]
}
}
},
"$$REMOVE"
]
},
"BOOK_CATEGORY": {
$cond: [
{
"$ifNull": [
{
"$arrayElemAt": [
"$SCORES",
0
]
},
false
]
},
{
$getField: {
field: "BOOK_CATEGORY",
input: {
"$arrayElemAt": [
"$SCORES",
0
]
}
}
},
"$$REMOVE"
]
},
}
}
])