Использовать агрегацию для сопоставления элементов документов

Я не знаю, как сформулировать этот вопрос, поэтому лучше покажу на примере.

Предположим, у меня есть документы, хранящиеся в следующем формате:

{'category': [1, 2, 3, ...],
'delete': [2, ...]}

Как получить документ только с «категориями», которые не нужно «удалять»? Результат суммирования должен быть:

{'category': [1, 3]}

Я пытаюсь сделать это следующим образом, сначала раскручиваю все категории

{'$unwind': '$category'},

Затем я хочу использовать $match для сопоставления только для категорий, которые не находятся в «удалить», но я получаю сообщение об ошибке: $nin нужен массив

{'$match': {'category': {'$nin': '$delete'}}}

Я не понимаю, почему "$delete" не является массивом?


person voronin.py    schedule 25.02.2014    source источник


Ответы (2)


В предстоящем выпуске MongoDB 2.6 новый оператор структуры агрегации $setDifference делает это простым, см. пример ниже с использованием текущего релиз-кандидата:

$ ./mongo --port 31100
MongoDB shell version: 2.6.0-rc0
connecting to: 127.0.0.1:31100/test
test-rs0:PRIMARY> db.foo.insert({category: [1, 2, 3], delete : [2] })
WriteResult({ "nInserted" : 1 })
test-rs0:PRIMARY> db.foo.aggregate(
...     { $project : { diff : { $setDifference : [ "$category", "$delete" ] } } } )
{ "_id" : ObjectId("530ddc2845cec73af61c81c1"), "diff" : [ 1, 3 ] }
test-rs0:PRIMARY>

MongoDB 2.6-rc0 был выпущен на прошлой неделе, поэтому рабочая версия должна быть доступна в ближайшее время.

person Jon Rangel    schedule 25.02.2014

Итак, учитывая условие:

{"$match": {"category": {"$nin": "$delete" } }}

Вы получаете сообщение об ошибке, что тип аргумента для $nin не является массивом.

Также рассмотрите следующее

{$match: {"category": { "$nin": ["$delete"] } }}

И это не соответствует, как и не соответствует:

{"$unwind": "$delete" },
{"$match": {"category": {"$ne": "$delete"}}}

Но, конечно, любая форма этого будет:

{"$match": {"category": { "$nin": [2] } }}

or:

{"$unwind": "$delete" },
{"$match": {"category": {"$ne": 2 }}}

Как видите, ссылка "$delete" не расширяется так, как вы ожидаете. в этих случаях он этого не делает, а делает это только на этапах оператора $project и $group.

Вам нужно использовать литерал, как показано в последних двух случаях.

person Neil Lunn    schedule 25.02.2014
comment
Этот ответ не соответствует цели постера с вопросами, которая состоит в том, чтобы вернуть документы, содержащие только те категории, которые не нужно удалять. Использование литеральных значений в конвейере агрегации не помогает в достижении этой цели, поскольку значения удаленных категорий в каждом документе могут быть разными. - person Jon Rangel; 26.02.2014
comment
@JonRangel Хотя есть и другая логика, отличная от наборов, я считаю, что она учитывает тот факт, что ожидаемое расширение, предложенное в вопросе, не работает. Это правда, что это стандартная математическая задача, и для ее решения будут соответствующие инструменты. Но на момент написания, не для производства. - person Neil Lunn; 26.02.2014