2016-01-11 5 views
0

Недавно мне было трудно найти объект, хранящийся в документе, с его ключом в другом поле того же документа.Найти объект MongoDB, используя значение другого поля

{ 
    list : { 
     "red" : 397n8, 
     "blue" : j3847, 
     "pink" : 8nc48, 
     "green" : 983c4, 
    }, 
    result : [ 
       { "id" : 397n8, value : "anger" }, 
       { "id" : j3847, value : "water" }, 
       { "id" : 8nc48, value : "girl" }, 
       { "id" : 983c4, value : "evil" } 
      ] 
    } 
} 

Я пытаюсь получить значение «синий», который имеет идентификатор «j3847» и значение «вода».

db.docs.find({ result.id : list.blue }, { result.value : 1 }); 

# list.blue would return water 
# list.pink would return girl 
# list.green would return evil 

Я пробовал много вещей, и даже нашел большую статью о том, как обновить значение, используя значение в том же документе .: Update MongoDB field using value of another field который я основанный себя на; без успеха ...:/

Как я могу найти объект MongoDB, используя значение другого поля?

ответ

2

Вы можете сделать это с $filter оператором в агрегации Монго. Он возвращает массив с только те элементы, которые соответствуют условию:

db.docs.aggregate([ 
    { 
    $project: { 
     result: { 
     $filter: { 
      input: "$result", 
      as:"item", 
      cond: { $eq: ["$list.blue", "$$item.id"]} 
     } 
     } 
    } 
    } 
]) 

Вывод для этого запроса выглядит следующим образом:

{ 
    "_id" : ObjectId("569415c8299692ceedf86573"), 
    "result" : [ { "id" : "j3847", "value" : "water" } ] 
} 
+0

Можно ли вернуть другие значения, отличные от тех, которые соответствуют условию? – samland

+0

@samland Да, вы можете добавить еще одно поле к своей [проекции] (https://docs.mongodb.org/v3.0/reference/operator/aggregation/project/) с таким синтаксисом: поле: 1. Или вы можете проецировать весь документ в поле с помощью [$$ ROOT] (https://docs.mongodb.org/manual/reference/aggregation-variables/). В проекции это будет выглядеть так: document: «ROOT» –

+0

Очень просто и работает как шарм. – samland

1

Одним из способов является использование оператора хотя бы не рекомендовал, как использовать его вызывает полное собрание сканирования независимо от того, что другие условий, возможно, использовать выбор индекса, а также вызывают интерпретатор JavaScript над каждым результирующим документом, который собирается быть значительно медленнее, чем собственный код.

При этом, используйте альтернативный .aggregate() метод для этого типа сравнения, вместо которых, безусловно, лучший вариант:

db.docs.aggregate([ 
    { "$unwind": "$result" }, 
    { 
     "$project": { 
      "result": 1, 
      "same": { "$eq": [ "$list.blue", "$result.id" ] } 
     } 
    }, 
    { "$match": { "same": true } }, 
    { 
     "$project": { 
      "_id": 0, 
      "value": "$result.value" 
     } 
    } 
]) 

Когда оператор $unwind применяется на поле result массива, он будет генерировать новая запись для каждого элемента поля результата, на котором выполняется размотка. Он в основном выравнивает данные, а затем в следующем $project шаг проверяет каждый элемент массива, чтобы сравнить, являются ли эти два поля одинаковыми.

Пример вывода

{ 
    "result" : [ 
     { 
      "value" : "water" 
     }   
    ], 
    "ok" : 1 
} 

Другой альтернативой является использование $map и $setDifference операторов в одном $project шаг, где вы можете избежать использования $unwind который может быть дорогостоящим на очень большом ионы и в большинстве случаев приводят к 16Мбам предела BSON ограничения:

db.docs.aggregate([ 
    { 
     "$project": { 
      "result": { 
       "$setDifference": [ 
        { 
         "$map": { 
          "input": "$result", 
          "as": "r", 
          "in": { 
           "$cond": [ 
            { "$eq": [ "$$r.id", "$list.blue" ] }, 
            "$$r", 
            false 
           ] 
          } 
         } 
        }, 
        [false] 
       ] 
      } 
     } 
    } 
]) 

Пример вывод

{ 
    "result" : [ 
     { 
      "_id" : ObjectId("569412e5a51a6656962af1c7"), 
      "result" : [ 
       { 
        "id" : "j3847", 
        "value" : "water" 
       } 
      ] 
     } 
    ], 
    "ok" : 1 
} 
+0

ли перебирать ваш запрос через всю коллекцию документов, или мы можем указать, какой документ для «агрегирования» с использованием документа _id? – samland

+0

Вы можете использовать оператор конвейера [** '$ match' **] (https://docs.mongodb.org/manual/reference/operator/aggregation/match/#pipe._S_match), если вы хотите фильтровать получение документов в конвейере агрегации с помощью '_id' или любого другого ключа. Это похоже на метод 'find()' коллекции MongoDB Collection и предложение 'WHERE' SQL. В основном это фильтрует данные, которые передаются следующему оператору. В конвейере может быть несколько операторов ** '$ match' **. – chridam

Смежные вопросы