2016-09-24 2 views
1

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

У меня есть структура данных, которая выглядит следующим образом (упрощенный вариант):

games: {_id: ..., scenes: [{_id: ..., views: [{_id: ...}]}]} 

(т.е. игры содержит набор сцен, сцен содержит коллекцию взглядов).

Я хочу запросить здесь конкретный объект вида. Я полагаю, что ответ включает использование $ elemMatch, но как мне это настроить? После того, как несколько исследований + игры вокруг, я знаю, что могу сделать это, чтобы получить сцену:

db.collection("games").findOne({ 
    _id: ObjectId(req.params.gid)}, 
    { 
    scenes: { 
     $elemMatch: {_id: ObjectId(req.params.sid)} 
    } 
}... 

Но как я расширяю это так, что она тянет только конкретный вид я заинтересован в (по _id) ?

Я думаю, что всегда мог найти объект вида, который я ищу, используя цикл for, который вызывает другой вопрос. Эффективность Wrt, лучше делать запросы, подобные этому, используя Mongo или вручную, вытаскивая весь документ в цикл через коллекции?

+0

Вы не можете получить только объект внутри массива. Вы должны вытащить весь документ. Если вы хотите, чтобы субдокументы сами по себе, вам нужно использовать структуру агрегации. – cdbajorin

+0

@cdbajorin Когда вы говорите «самостоятельно», вы имеете в виду, что невозможно отфильтровать представления с значениями _id, которые я не хочу? Или вы имеете в виду, что запрос, который я ищу, дал бы мне структуру, похожую на 'result.scenes [0] .views [0]'? Я в порядке с последним. – JSideris

ответ

1

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

Так что, если это ваша коллекция:

> db.col.find().pretty() 
{ 
    "_id" : ObjectId("57e6a5404897ec06f1c3d86f"), 
    "games" : [ 
     { 
      "_id" : "game1", 
      "scenes" : [ 
       { 
        "_id" : "scene1", 
        "views" : [ 
         { 
          "_id" : "view1" 
         } 
        ] 
       } 
      ] 
     } 
    ] 
} 
{ 
    "_id" : ObjectId("57e6a5d24897ec06f1c3d870"), 
    "games" : [ 
     { 
      "_id" : "game1", 
      "scenes" : [ 
       { 
        "_id" : "scene11", 
        "views" : [ 
         { 
          "_id" : "view111" 
         }, 
         { 
          "_id" : "view112" 
         }, 
         { 
          "_id" : "view113" 
         } 
        ] 
       }, 
       { 
        "_id" : "scene12", 
        "views" : [ 
         { 
          "_id" : "view121" 
         } 
        ] 
       } 
      ] 
     }, 
     { 
      "_id" : "game2", 
      "scenes" : [ 
       { 
        "_id" : "scene21", 
        "views" : [ 
         { 
          "_id" : "view211" 
         } 
        ] 
       } 
      ] 
     } 
    ] 
} 

и предположим, что вы хотите найти вид с ID "view112", вы можете делать:

db.col.aggregate([ 
    { $unwind: "$games"}, 
    { $unwind: "$games.scenes"}, 
    { $unwind: "$games.scenes.views"}, 
    { 
     $match: { 
      "games.scenes.views._id": "view112" 
     } 
    } 
]) 

и вы получите:

{ 
    "_id": ObjectId("57e6a5d24897ec06f1c3d870"), 
    "games": { 
     "_id": "game1", 
     "scenes": { 
      "_id": "scene11", 
      "views": { 
       "_id": "view112" 
      } 
     } 
    } 
} 
+0

Это классно. Это требует, чтобы этот конкретный идентификатор вида не существовал ни в одной другой сцене? Любые советы по созданию индексов для этого запроса? – JSideris

+0

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

+0

Поскольку вы знаете идентификатор вида в первую очередь, вы должны иметь '$ match' в верхней части агрегации, чтобы фильтровать только игры, у которых есть это представление. – cdbajorin

0

Я не понимаю, лет u может запросить напрямую с

db.collection("games").findOne({"scenes.views._id":ObjectId(req.params.vid)},{_id:0,"scenes.$.views":1}); 

Он должен удалить другие сцены со сцены (предназначенные для каламбур). Но да, он все равно предоставит вам несколько просмотров, поскольку $ может использоваться только один раз в любом выражении. Там я думаю, что зацикливание - это ключ.

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

+0

Вы имели в виду сравнение scenes.views._id с req.params.vid (против sid, который является параметром, который я использую для сцены)? Что делает _id: 0? Обратите внимание, что идентификаторы представлений также являются объектами ObjectId. – JSideris

+0

Да, это должно быть req.params.vid, мой плохой, обновил ответ. если это объект Id, он гарантированно будет уникальным. –

+0

для второй части, _id: 0 говорит, что нам не нужно _id в результатах. это стратегия проектирования. Вы можете установить то, что хотите, и то, что вы не хотите, с 1 и 0 соответственно. –

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