Использование позиционного оператора MongoDB $ в глубоко вложенном запросе документа
Можно ли использовать позиционный оператор '$' в сочетании с запросом к массиву глубоко вложенных документов?
Рассмотрим следующий вложенный документ, определяющий "пользователя":
{
username: 'test',
kingdoms: [
{
buildings: [
{
type: 'castle'
},
{
type: 'treasury'
},
...
]
},
...
]
}
Мы хотели бы вернуть "замки" для конкретного пользователя, например, в форме:
{
kingdoms: [{
buildings: [{
type: 'castle'
}]
}]
}
Поскольку вы не можете использовать оператор $ дважды ( https://jira.mongodb.org/browse/server-831), я знаю, что не могу также запросить конкретное королевство, поэтому я пытаюсь написать оператор поиска для пятое королевство.
Кажется, это имеет смысл при обновлении глубоко вложенного поддокумента ( Mongodb - обновление глубоко вложенного поддокумента), но у меня меньше успеха с запросом поиска.
Я могу вернуть здания первого королевства с запросом:
db.users.findOne(
{ username: 'test' },
{ kingdoms: {$slice: [0, 1]}, 'kingdom.buildings': 1 }
);
Но это возвращает все здания этого королевства.
Следуя одноуровневым примерам оператора позиции, я пытаюсь выполнить такой запрос:
db.users.findOne(
{ username: 'test', 'kingdoms.buildings.type': 'castle' },
{ kingdoms: {$slice: [n, 1]}, 'kingdom.buildings.$': 1 }
);
чтобы быть в форме:
db.collection.find( { <array.field>: <value> ...}, { "<array>.$": 1 } )
как описано в документации http://docs.mongodb.org/manual/reference/operator/projection/positional/ S
Однако это не с ошибкой:
Positional operator does not match the query specifier
Предположительно, потому что kingdoms.buildings не считается массивом. Я также попробовал kingdoms.0.buildings
Это сбивает с толку, потому что это, кажется, работает для обновлений (согласно обновлению Mongodb глубоко вложенный документ)
Я неправильно понял синтаксис или он не поддерживается? Если да, есть ли способ достичь чего-то подобного?
1 ответ
Вы получаете ошибку от
db.users.findOne(
{ username: 'test', 'kingdoms.buildings.type': 'castle' },
{ kingdoms: {$slice: [n, 1]}, 'kingdom.buildings.$': 1 }
);
потому что есть орфографическая ошибка ("kingdom.buildings.$" должен быть "kingdoms.buildings. $").
Тем не менее, этот способ не может выполнить то, что вы ожидаете.
$ всегда нацелен на королевства на пути к королевствам. Билдинг - первый массив.
Это способ, который должен быть в состоянии решить проблему.
(Требуется V2.6+)
db.c.aggregate([ {
$match : {
username : 'test',
'kingdoms.buildings.type' : 'castle'
}
}, {
$project : {
_id : 0,
kingdoms : 1
}
}, {
$redact : {
$cond : {
"if" : {
$or : [ {
$gt : [ "$kingdoms", [] ]
}, {
$gt : [ "$buildings", [] ]
}, {
$eq : [ "$type", "castle" ]
} ]
},
"then" : "$$DESCEND",
"else" : "$$PRUNE"
}
}
} ]).pretty();
Чтобы зарезервировать только первый элемент королевств,
db.c.aggregate([ {
$match : {
username : 'test',
'kingdoms.buildings.type' : 'castle'
}
}, {
$redact : {
$cond : {
"if" : {
$or : [ {
$gt : [ "$kingdoms", [] ]
}, {
$gt : [ "$buildings", [] ]
}, {
$eq : [ "$type", "castle" ]
} ]
},
"then" : "$$DESCEND",
"else" : "$$PRUNE"
}
}
}, {
$unwind : "$kingdoms"
}, {
$group : {
_id : "$_id",
kingdom : {
$first : "$kingdoms"
}
}
}, {
$group : {
_id : "$_id",
kingdoms : {
$push : "$kingdom"
}
}
}, {
$project : {
_id : 0,
kingdoms : 1
}
} ]).pretty();