MongoDB/Mongoid: поиск документов, соответствующих первому элементу в массиве

У меня есть документ с массивом:

{
    _id: ObjectId("515e10784903724d72000003"),
    association_chain: [
        {
            name: "Product",
            id: ObjectId("4e1e2cdd9a86652647000003")
        }
    ],
    //...
}

Я пытаюсь найти в коллекции документы, в которых name первого элемента в массиве association_chain соответствует заданному значению.

Как я могу сделать это с помощью Mongoid? Или если вы только знаете, как это можно сделать с помощью MongoDB, если вы опубликуете пример, то я, вероятно, смогу понять, как это сделать с Mongoid.


person Andrew    schedule 05.04.2013    source источник


Ответы (3)


Два способа сделать это:

1) если вы уже знаете, что вас интересует только первое название продукта, появляющееся в «association_chain», то это лучше:

db.items.find("association_chain.0.name":"something")

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

Если вы хотите это сделать, вам понадобится индекс:

db.items.ensureIndex({"association_chain.0.name":1},{background:1})

2) если вы ищете конкретный товар, но не уверены, в какой позиции ассоциации_цепочки он появляется, то сделайте так:

С оболочкой MongoDB вы можете получить доступ к любому хеш-ключу внутри вложенной структуры с помощью '.' точечный оператор! Обратите внимание, что это не зависит от того, насколько глубоко этот ключ вложен в запись (разве это не круто?)

Вы можете найти встроенный массив хэшей следующим образом:

db.items.find("association_chain.name":"something")

Это возвращает все записи в коллекции, которые содержат желаемый продукт, упомянутый где-либо в массиве ассоциаций.

Если вы хотите сделать это, вы должны убедиться, что у вас есть индекс:

db.items.ensureIndex({"association_chain.name":1},{background: 1})

См. «Точечная нотация» на этой странице: http://docs.mongodb.org/manual/core/document/

person Tilo    schedule 08.04.2013
comment
второй блок кода должен быть «db.items.ensureIndex», а не «.find» - person Matthew Schulkind; 17.06.2013

Используйте позиционный оператор. Вы можете запросить первый элемент массива с помощью .0 (а второй с помощью .1 и т. д.).

> db.items.insert({association_chain: [{name: 'foo'}, {name: 'bar'}]})
> db.items.find({"association_chain.0.name": "foo"})
{ "_id" : ObjectId("516348865862b60b7b85d962"), "association_chain" : [ { "name" : "foo" }, { "name" : "bar" } ] }

Вы можете видеть, что позиционный оператор действует, поскольку поиск foo во втором элементе не возвращает совпадения...

> db.items.find({"association_chain.1.name": "foo"})
>

... но поиск bar работает.

> db.items.find({"association_chain.1.name": "bar"})
{ "_id" : ObjectId("516348865862b60b7b85d962"), "association_chain" : [ { "name" : "foo" }, { "name" : "bar" } ] }

Вы даже можете индексировать это конкретное поле, не индексируя все имена всех документов цепочки ассоциаций:

> db.items.ensureIndex({"association_chain.0.name": 1})
> db.items.find({"association_chain.0.name": "foo"}).explain()
{
        "cursor" : "BtreeCursor association_chain.0.name_1",
        "nscanned" : 1,
        ...

}
> db.items.find({"association_chain.1.name": "foo"}).explain()
{
        "cursor" : "BasicCursor",
        "nscanned" : 3,
        ...
}
person Chris Heald    schedule 08.04.2013
comment
Я понятия не имел, что вы можете злоупотреблять индексами, как это. Любить это. - person pestilence669; 10.04.2013

Вы можете сделать это с помощью структуры агрегации. В оболочке mongo запустите запрос, который раскручивает документы, чтобы у вас был документ для каждого элемента массива (с дублированными данными в других полях), затем группируйте по идентификатору и любому другому полю, которое вы хотите включить, плюс массив с оператором $first . Затем просто включите оператор $match для фильтрации по имени или mongoid.

Вот запрос для поиска по первому названию продукта:

db.foo.aggregate([
{ $unwind:"$association_chain"     
},
{
  $group : {
            "_id" : {
                "_id" : "$_id",
                "other" : "$other"
            },
            "association_chain" : {
                $first : "$association_chain"
            }
        }
},
{  $match:{ "association_chain.name":"Product"}
}

])

Вот как запросить первый продукт у mongoid:

db.foo.aggregate([
{ $unwind:"$association_chain"     
},
{
   $group : {
            "_id" : {
                "_id" : "$_id",
                "other" : "$other"
            },
            "association_chain" : {
                $first : "$association_chain"
            }
        }
},
{  $match:{ "association_chain.id":ObjectId("4e1e2cdd9a86652647000007")}
}

])
person AntonioOtero    schedule 08.04.2013
comment
Разница между использованием структуры агрегации, как я уже упоминал, и использованием простого запроса с точечной нотацией заключается в том, что использование точечной нотации возвращает весь документ, включая все элементы массива Association_Chain. Использование этого запроса с агрегированием вернет документы только с тем элементом в массиве, который соответствует (первый), или вы можете преобразовать документ любым удобным для вас способом. Используйте тот, который лучше всего соответствует вашим потребностям. - person AntonioOtero; 09.04.2013