2014-11-06 3 views
0

я следующие два пункта, вставленных в коллекции «кадры»:агрегации MongoDB с несколькими массивами

frame1 = { 
      "number": 1, 

      "hobjects": [ { "htype": 1, "weight": 50 }, 
         { "htype": 2, "weight": 220 }, 
         { "htype": 2, "weight": 290 }, 
         { "htype": 3, "weight": 450 } ], 

      "sobjects": [ { "stype": 1, "size": 10.0 }, 
         { "stype": 2, "size": 5.1 }, 
         { "stype": 2, "size": 6.5 } ], 
      } 

frame2 = { 
      "number": 2, 

      "hobjects": [ { "htype": 1, "weight": 61 }, 
         { "htype": 2, "weight": 210 }, 
         { "htype": 2, "weight": 250 } ], 

      "sobjects": [ { "stype": 1, "size": 12.1 }, 
         { "stype": 2, "size": 4.9 }, 
         { "stype": 2, "size": 6.2 }, 
         { "stype": 2, "size": 5.7 } ], 
      } 

frames.insert(frame1) 
frames.insert(frame2) 

Теперь я хочу, чтобы сделать запрос на неполных данных кадра:

query = { "hobjects.htype": 3, "sobjects.stype": 2 } 
db.frames.find(query) 

который результаты:

{u'_id': ObjectId('545b6ea7b9ad9a03462d743b'), u'hobjects': [{u'htype': 1, u'weight': 50}, {u'htype': 2, u'weight': 220}, {u'htype': 2, u'weight': 290}, {u'htype': 3, u'weight': 450}], u'number': 1, u'sobjects': [{u'stype': 1, u'size': 10.0}, {u'stype': 2, u'size': 5.1}, {u'stype': 2, u'size': 6.5}]} 

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

{u'_id': ObjectId('545b6ea7b9ad9a03462d743b'), u'hobjects': [{u'htype': 3, u'weight': 450}], u'number': 1, u'sobjects': [{u'stype': 2, u'size': 5.1}, {u'stype': 2, u'size': 6.5}]} 

Единственное решение, которое я нашел включает агрегацию с размотки и группировка в коллекции:

query = { "hobjects.htype": 3, "sobjects.stype": 2 } 
db.frames.aggregate([ 
    { "$match": query }, 
    { "$unwind": "$hobjects" }, 
    { "$match": dict((key, value) for key, value in query.iteritems() if "hobjects." in key) }, 
    { "$group": { "_id": "$_id", "number": { "$first": "$number" } , "hobjects": { "$push": "$hobjects" }, "sobjects": { "$first": "$sobjects" } } }, 
    { "$unwind": "$sobjects" }, 
    { "$match": dict((key, value) for key, value in query.iteritems() if "sobjects." in key) }, 
    { "$group": { "_id": "$_id", "number": { "$first": "$number" } , "hobjects": { "$first": "$hobjects" }, "sobjects": { "$push": "$sobjects" } } }, 
    ]) 

Я думаю, Thats не очень эффективный и гибкий способ выполнения запросов. Интересно, есть ли другие варианты?

ответ

0

При условии, что ваш сервер MongoDB 2.6 или выше, то вы всегда можете сделать это:

db.frames.aggregate([ 

    // Still helps to match the documents by conditions to filter 
    { "$match": { 
     "hobjects.htype": 3, "sobjects.stype": 2 
    }}, 

    // Now filter inline using $map and $setDifference 
    { "$project": { 
     "number": 1, 
     "hobjects": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$hobjects", 
        "as": "el", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$el.htype", 3 ] }, 
          "$$el", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     }, 
     "sobjects": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$sobjects", 
        "as": "el", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$el.stype", 2 ] }, 
          "$$el", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }} 
]) 

Загвоздка в том, что основная проекция и такие вещи, как $elemMatch могут только в настоящее время соответствуют первого элемента массив, соответствующий условию. Чтобы сделать что-нибудь еще, вам нужна какая-то форма расширенной манипуляции, доступная только для таких вещей, как структура агрегации.

Операторы $setDiffence и $map предоставляют вам «встроенный» способ обработки массивов и фактически «устанавливает» в отдельный документ. Это будет немного более эффективным, чем использование $unwind, особенно в случае больших массивов.

Обозначение JavaScript здесь Я знаю (в основном в комментариях), но это в основном то же, что и python.

+0

Спасибо @NeilLunn! Похож на свою оптимальную альтернативу. Попробуй! –

+0

Протестировано решение, и это сработало для моего примера. Хотя я бы предпочел более общее решение с произвольным запросом на все данные объекта. –

+0

@Neil Lunn - Не могли бы вы помочь написать этот запрос в java-драйвере. Спасибо – PVH

0

После агрегации может помочь вам

db.frames.aggregate({"$unwind":"$hobjects"},{"$unwind":"$sobjects"},{"$match":{"hobjects.htype": 3, "sobjects.stype": 2}},{"$group":{"_id":"$_id","u'hobjects":{"$first":"$hobjects"},"u'number":{"$first":"$number"},"u'sobjects":{"$push":"$sobjects"}}}) 
+0

Декартовы проблемы с разматыванием нескольких массивов. Означает, что вы просто дублировали элементы одного из них. –

+0

@NeilLunn спасибо за предложение. Я буду работать над решением этой проблемы. – Yogesh

+0

. Эта проблема была решена много раз раньше, я уверен, что я много раз ответил сам. Но это не то, о чем попросил ОП. Их код в порядке, они просто попросили что-то более оптимальное, и возможное объяснение того, что они делают, необходимо. –

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