2015-05-29 3 views
13

я коллекция, я исполняющая агрегацию, и я в основном получил его доMongoDB Aggregation - матч, если значение в массиве

{array:[1,2,3], value: 1}, 
{array:[1,2,3], value: 4} 

Как мне выполнить матч агрегации, чтобы проверить, если значение находится в массиве? Я пробовал использовать {$match: {"array: {$in: ["$value"]}}}, но ничего не нашел.

Я хотел бы выход (если используется выше в качестве примера) быть:

{array:[1,2,3], value:1} 
+0

Я не пробовал это, но я думаю, что вы, вероятно, хотите '$ elemMatch' (см [документы] (http://docs.mongodb.org/manual/reference/operator/query/elemMatch/# op._S_elemMatch)), а не '$ in' - вы хотите проверить, является ли значение массивом, а не является ли массив значением! – DNA

+1

Вам действительно нужно вернуться и посмотреть на этот вопрос, так как любой, кто предложил '$ unwind', дал вам наихудший возможный ответ на производительность, который вы могли бы реализовать в своем коде. Есть гораздо лучшие альтернативы. –

ответ

6

Небольшое изменение, основанный на @ chridam:

db.test.aggregate([ 
    { "$unwind": "$array" }, 
    { "$group": { 
        _id: { "_id": "$_id", "value": "$value" }, 
        array: { $push: "$array" }, 
        mcount: { $sum: {$cond: [{$eq: ["$value","$array"]},1,0]}} 
       } 
    }, 
    { $match: {mcount: {$gt: 0}}}, 
    { "$project": { "value": "$_id.value", "array": 1, "_id": 0 }} 
]) 

Идея заключается в $unwind и $group назад массив, считая в mcount количество элементов, соответствующих этому значению. После этого простой $match на mcount > 0 отфильтровывает нежелательные документы.

2

Вы можете использовать $where если агрегация не требуется

db.collection.find({ $where: function(){ 
    return (this.array.indexOf(this.value) !== -1)} 
}) 
+0

Я согласен с тем, что это предпочтительнее использовать '$ unwind', но, конечно, гораздо более эффективный способ сделать это. –

4

Более эффективный подход будет включают один трубопровод, который использует оператор $redact следующим образом:

db.collection.aggregate([ 
    { 
     "$redact": { 
      "$cond": [ 
       { 
        "$setIsSubset": [ 
         ["$value"], 
         "$array" 
        ] 
       }, 
       "$$KEEP", 
       "$$PRUNE" 
      ] 
     } 
    } 
]) 

Для более ранних версий MongoDB, которые не поддерживают $redact (версий < 2,6), а затем рассмотреть это агрегирование трубопровод, который использует $unwind оператор:

db.collection.aggregate([ 
    { "$unwind": "$array" }, 
    { 
     "$project": { 
      "isInArray": { 
       "$cond": [ 
        { "$eq": [ "$array", "$value" ] }, 
        1, 
        0 
       ] 
      }, 
      "value": 1, 
      "array": 1 
     } 
    }, 
    { "$sort": { "isInArray": -1 } }, 
    { 
     "$group": { 
      "_id": { 
       "_id": "$_id", 
       "value": "$value" 
      }, 
      "array": { "$push": "$array" }, 
      "isInArray": { "$first": "$isInArray" } 
     } 
    }, 
    { "$match": { "isInArray": 1 } }, 
    { "$project": { "value": "$_id.value", "array": 1, "_id": 0 } } 
]) 
11

Как указано, $where - хороший вариант, когда вам не нужно продолжать логику в конвейере агрегации.

Но если вы это сделаете, используйте $redact, с $map, чтобы преобразовать «значение» в массив и использовать $setIsSubSet для сравнения. Это самый быстрый способ сделать это, так как вам не нужно дублировать документы с помощью $unwind:

db.collection.aggregate([ 
    { "$redact": { 
     "$cond": { 
      "if": { "$setIsSubset": [ 
       { "$map": { 
        "input": { "$literal": ["A"] }, 
        "as": "a", 
        "in": "$value" 
       }}, 
       "$array" 
      ]}, 
      "then": "$$KEEP", 
      "else": "$$PRUNE" 
     } 
    }} 
]) 

$redact Оператор конвейера позволяет обрабатывающий логическое состояние в $cond и использует специальные операции $$KEEP «держать «документ, в котором логическое условие истинно, или $$PRUNE« удалить »документ, в котором условие было ложным.

Это позволяет работать как $project с последующим $match, но на одном этапе трубопровода, который является более эффективным.

Учитывая, что это собственные закодированные операторы, а не JavaScript, то это, скорее всего, «самый быстрый способ выполнить ваш матч». Таким образом, если вы используете версию MongoDB 2.6 или выше, то именно так вы должны это делать, чтобы сравнивать эти элементы в вашем документе.

+0

thumbs up for $ redact. Я не знал об этом, и он делает именно то, что мне нужно: посмотрите в каждом документе определенное значение в массиве. Благодаря! – Mattijs

1

Попробуйте сочетание $ эк и $ setIntersection

{$group :{ 
    _id: "$id", 
    yourName : { $sum: 
    { $cond :[ 
     {$and : [ 
      {$eq:[{$setIntersection : ["$someArrayField", ["$value"]] },["$value"]]} 
     ] 
     },1,0] 
    } 

} }

3

Немного поздно, чтобы ответить, но это представляет еще одно решение:

Используя addFields и матч отдельно, это дает большую гибкость, чем устранение. Вы можете открыть несколько полей, а затем использовать другую логику соответствия на основе результатов.

db.applications.aggregate([ 
    {$addFields: {"containsValueInArray": {$cond:[{$setIsSubset: [["valueToMatch"], "$arrayToMatchIn"]},true,false]}}}, 
    {$match: {"containsValueInArray":true}} 
]); 
0

Вы можете использовать выражение агрегации в регулярном запросе в версии 3.6.

db.collection_name.find({"$expr": {"$in": ["$value", "$array"]}}) 

Использование Aggregation:

Вы можете использовать $match + $expr в текущей версии 3.6.

db.collection_name.aggregate({"$match": {"$expr": {"$in": ["$value", "$array"]}}}) 

Вы можете попробовать $redact + $in выражение в 3.4 версии.

db.collection_name.aggregate({ 
    "$redact": { 
    "$cond": [ 
     { 
     "$in": [ 
      "$value", 
      "$array" 
     ] 
     }, 
     "$$KEEP", 
     "$$PRUNE" 
    ] 
    } 
}) 
Смежные вопросы