2015-09-02 2 views
10

У меня есть схема, похожая на эту.Проверить Если поле существует в поддоку Array

{id: Number, 
    line_items: [{ 
    id: String, 
    quantity: Number, 
    review_request_sent :Boolean 
    }], 
    total_price: String, 
    name: String, 
    order_number: Number 
} 

Что я хочу найти это все элементы, которые не имеют не установить поле review_request_sent для всех элементов массива.

Пример

{ 
    id: 1, 
    line_items: [ 
    { 
     id: 43, 
     review_request_sent: true 
    } 
    ] 
}, 
{ 
    id: 2, 
    line_items: [ 
    { 
     id: 1, 
     review_request_sent: false 
    }, 
    { 
     id: 39 
    }, 
] 
}, 
{ 
    id: 3, 
    line_items: [ 
    { 
    id: 23, 
    review_request_sent: true 
    }, 
    { 
    id: 85, 
    review_request_sent: true 
    }, 
    { 
    id: 12, 
    review_request_sent: false 
    } 
    ] 
} 

Я хотел бы помочь с запросом/найти, что будет возвращать только seccond документ, поскольку он не имеет review_request_sent поле во всех его элементов в его массиве.

ответ

13

Вы в основном хотите $elemMatch и оператор $exists, так как это будет проверять каждый элемент, чтобы увидеть, если условие «поле не существует» верно для любого элемента:

Model.find({ 
    "line_items": { 
     "$elemMatch": { "review_request_sent": { "$exists": false } } 
    } 
},function(err,docs) { 

}); 

Это возвращает только второй документ так как поле не присутствует в одном из массива поддокументов:

{ 
     "id" : 2, 
     "line_items" : [ 
       { 
         "id" : 1, 
         "review_request_sent" : false 
       }, 
       { 
         "id" : 39 
       } 
     ] 
} 

Заметим, что это «отличается» от этой формы:

Model.find({ 
    "line_items.review_request_sent": { "$exists": false } 
},function(err,docs) { 

}) 

В этом вопросе «все» элементов массива не содержат это поле, что неверно, если в документе есть хотя бы один элемент, в котором есть данное поле. Таким образом, $eleMatch проверяет условие на элемент «каждый», и вы получаете правильный ответ.


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

Model.aggregate(
     [ 
     { "$match": { 
      "line_items": { 
      "$elemMatch": { "review_request_sent": { "$exists": false } } 
      } 
     }}, 
     { "$project": { 
      "line_items": { 
      "$setDifference": [ 
       {"$map": { 
       "input": "$line_items", 
       "as": "item", 
       "in": { 
        "$cond": [ 
        { "$eq": [ 
         { "$ifNull": [ "$$item.review_request_sent", null ] }, 
         null 
        ]}, 
        "$$item.id", 
        false 
        ] 
       } 
       }}, 
       [false] 
      ] 
      } 
     }} 
    ], 
    function(err,docs) { 
     if (err) throw err; 
     async.each(
     docs, 
     function(doc,callback) { 
      async.each(
      doc.line_items, 
      function(item,callback) { 
       Model.update(
       { "_id": doc._id, "line_items.id": item }, 
       { "$set": { "line_items.$.review_request_sent": false } }, 
       callback 
      ); 
      }, 
      callback 
     ); 
     }, 
     function(err) { 
      if (err) throw err; 
      // done 
     } 
    ); 
    } 
); 

где .aggregate() результат не соответствует только документы, но и фильтрует содержимое из массива, где поле не присутствовал, так, чтобы только вернуть «идентификатор» этого конкретного поддокументу.

Затем закодированные инструкции .update() соответствуют каждому найденному элементу массива в каждом документе и добавляют недостающее поле со значением в соответствующей позиции.

Таким образом, у вас будет поле, присутствующее во всех поддокументах каждого документа, где он отсутствовал раньше.

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

{id: Number, 
    line_items: [{ 
    id: String, 
    quantity: Number, 
    review_request_sent: { type: Boolean, default: false } 
    }], 
    total_price: String, 
    name: String, 
    order_number: Number 
} 

Так что в следующий раз, когда вы добавлять новые элементы массив в вашем коде, элемент всегда будет существовать с его значением по умолчанию, если иное не задано явно. И это, вероятно, идея goood для этого, а также установка required на другие поля, которые вы всегда хотите, такие как «id».

+0

Ответы работают хорошо для меня. Мне бы хотелось установить значение по умолчанию для данных, но оно вытягивается из другой внешней системы и вставляется из python. В этот момент мы не хотим обрабатывать данные. Я работаю только с данными моего узла. Я хочу немного помочь. Что делать, если я хочу ** обновить ** объект с идентификатором id 3 и идентификатором объекта line_item из 85. Это идентификатор документа 3 и в массиве line_item из 85 – user841818

+0

$ elemMatch возвращает только первое совпадение. Что делать, если вы хотите больше соответствовать? – fanbondi

+0

Это то, что сработало для меня 'db.history.find ({" treat ": {" $ elemMatch ": {" nameOfPerson ": {" $ exists ": true}}}})' – Nav

1

Вы можете это еще один способ найти без $map и $unwind используется $redact так:

db.collectionName.aggregate({ 
    "$match": { 
    "line_items.review_request_sent": { 
     "$exists": true 
    } 
    } 
}, { 
    "$redact": { 
    "$cond": { 
     "if": { 
     "$eq": [{ 
      "$ifNull": ["$review_request_sent", "null"] 
     }, "null"] 
     }, 
     "then": "$$DESCEND", 
     "else": "$$PRUNE" 
    } 
    } 
}, { 
    "$match": { 
    "line_items": { 
     "$size": 1 
    } 
    } 
}).pretty() 

В приведенном выше запросе первый match проверка ли review_request_sent презенты или не redact$ifNull использовать, чтобы проверить, является ли review_request_sent подарки или если нет, то он будет заменен на "null" и $eq для проверки "null". Это возвратит документы, такие как

{ "_id" : ObjectId("55e6ca322c33bb07ff2c163e"), "id" : 1, "line_items" : [ ] } 
{ "_id" : ObjectId("55e6ca322c33bb07ff2c163f"), "id" : 2, "line_items" : [ { "id" : 39 } ] } 
{ "_id" : ObjectId("55e6ca322c33bb07ff2c1640"), "id" : 3, "line_items" : [ ] } 

и после того, что последняя проверка на совпадение только тех документов, для которых line_items массив содержит значение для $size т.е. 1

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