Найти записи MongoDB, где поле массива не пустое

Все мои записи имеют поле под названием «картинки». Это поле представляет собой массив строк.

Теперь я хочу, чтобы новые 10 записей, где этот массив НЕ пуст.

Я искал googled, но, как ни странно, я этого не нашел. Я прочитал параметр $ where, но мне было интересно, как медленно это происходит с нативными функциями, и если есть лучшее решение.

И даже тогда это не работает:

ME.find({$where: 'this.pictures.length > 0'}).sort('-created').limit(10).execFind() 

Ничего не возвращает. Оставляя это. this.pictures без бит длины работают, но затем он также возвращает пустые записи, конечно.

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

 ME.find({ pictures: { $exists: true, $not: {$size: 0} } }) 

MongoDB не использует индексы, если используется $ size, поэтому это лучшее решение:

 ME.find({ pictures: { $exists: true, $ne: [] } }) 

Начиная с версии MongoDB 2.6, вы можете сравнить с оператором $gt но может привести к неожиданным результатам (вы можете найти подробное объяснение в этом ответе ):

 ME.find({ pictures: { $gt: [] } }) 

После того, как некоторые другие взгляды, особенно в документах mongodb, и загадочные биты вместе, это был ответ:

 ME.find({pictures: {$not: {$size: 0}}}) 

Это может также работать для вас:

 ME.find({'pictures.0': {$exists: true}}); 

Начиная с версии 2.6, другой способ сделать это – сравнить поле с пустым массивом:

 ME.find({pictures: {$gt: []}}) 

Тестирование в оболочке:

 > db.ME.insert([ {pictures: [1,2,3]}, {pictures: []}, {pictures: ['']}, {pictures: [0]}, {pictures: 1}, {foobar: 1} ]) > db.ME.find({pictures: {$gt: []}}) { "_id": ObjectId("54d4d9ff96340090b6c1c4a7"), "pictures": [ 1, 2, 3 ] } { "_id": ObjectId("54d4d9ff96340090b6c1c4a9"), "pictures": [ "" ] } { "_id": ObjectId("54d4d9ff96340090b6c1c4aa"), "pictures": [ 0 ] } 

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

Вы заботитесь о двух вещах при запросе – точности и производительности. Имея это в виду, я протестировал несколько разных подходов в MongoDB v3.0.14.

TL; DR db.doc.find({ nums: { $gt: -Infinity }}) является самым быстрым и надежным (по крайней мере, в версии MongoDB, которую я тестировал).

EDIT: Это больше не работает в MongoDB v3.6! См. Комментарии в этом сообщении для потенциального решения.

Настроить

Я вставил 1k docs w / oa поле списка, 1k docs с пустым списком и 5 документов с непустым списком.

 for (var i = 0; i < 1000; i++) { db.doc.insert({}); } for (var i = 0; i < 1000; i++) { db.doc.insert({ nums: [] }); } for (var i = 0; i < 5; i++) { db.doc.insert({ nums: [1, 2, 3] }); } db.doc.createIndex({ nums: 1 }); 

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

тесты

db.doc.find({'nums': {'$exists': true}}) возвращает неверные результаты (для чего мы пытаемся выполнить).

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': {'$exists': true}}).count() 1005 

-

db.doc.find({'nums.0': {'$exists': true}}) возвращает правильные результаты, но также медленно использует полное сканирование коллекции (обратите COLLSCAN этап COLLSCAN в объяснении).

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': {'$exists': true}}).count() 5 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': {'$exists': true}}).explain() { "queryPlanner": { "plannerVersion": 1, "namespace": "test.doc", "indexFilterSet": false, "parsedQuery": { "nums.0": { "$exists": true } }, "winningPlan": { "stage": "COLLSCAN", "filter": { "nums.0": { "$exists": true } }, "direction": "forward" }, "rejectedPlans": [ ] }, "serverInfo": { "host": "MacBook-Pro", "port": 27017, "version": "3.0.14", "gitVersion": "08352afcca24bfc145240a0fac9d28b978ab77f3" }, "ok": 1 } 

-

db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}) возвращает неверные результаты. Это из-за неверного сканирования индекса, не продвигающего никаких документов. Скорее всего, он будет точным, но медленным без индекса.

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}).count() 0 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}).explain('executionStats').executionStats.executionStages { "stage": "KEEP_MUTATIONS", "nReturned": 0, "executionTimeMillisEstimate": 0, "works": 2, "advanced": 0, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "inputStage": { "stage": "FETCH", "filter": { "$and": [ { "nums": { "$gt": { "$size": 0 } } }, { "nums": { "$exists": true } } ] }, "nReturned": 0, "executionTimeMillisEstimate": 0, "works": 1, "advanced": 0, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "docsExamined": 0, "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 0, "executionTimeMillisEstimate": 0, "works": 1, "advanced": 0, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "keyPattern": { "nums": 1 }, "indexName": "nums_1", "isMultiKey": true, "direction": "forward", "indexBounds": { "nums": [ "({ $size: 0.0 }, [])" ] }, "keysExamined": 0, "dupsTested": 0, "dupsDropped": 0, "seenInvalidated": 0, "matchTested": 0 } } } 

-

db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}) возвращает правильные результаты, но производительность плохая. Он технически выполняет сканирование индекса, но затем он все еще продвигает все документы, а затем фильтрует их).

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}).count() 5 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}).explain('executionStats').executionStats.executionStages { "stage": "KEEP_MUTATIONS", "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 2016, "advanced": 5, "needTime": 2010, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "inputStage": { "stage": "FETCH", "filter": { "$and": [ { "nums": { "$exists": true } }, { "$not": { "nums": { "$size": 0 } } } ] }, "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 2016, "advanced": 5, "needTime": 2010, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "docsExamined": 2005, "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 2005, "executionTimeMillisEstimate": 0, "works": 2015, "advanced": 2005, "needTime": 10, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "keyPattern": { "nums": 1 }, "indexName": "nums_1", "isMultiKey": true, "direction": "forward", "indexBounds": { "nums": [ "[MinKey, MaxKey]" ] }, "keysExamined": 2015, "dupsTested": 2015, "dupsDropped": 10, "seenInvalidated": 0, "matchTested": 0 } } } 

-

db.doc.find({'nums': { $exists: true, $ne: [] }}) возвращает правильные результаты и немного быстрее, но производительность по-прежнему не идеальна. Он использует IXSCAN, который только продвигает документы с существующим полем списка, но затем должен отфильтровывать пустые списки один за другим.

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $ne: [] }}).count() 5 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $ne: [] }}).explain('executionStats').executionStats.executionStages { "stage": "KEEP_MUTATIONS", "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 1018, "advanced": 5, "needTime": 1011, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "inputStage": { "stage": "FETCH", "filter": { "$and": [ { "$not": { "nums": { "$eq": [ ] } } }, { "nums": { "$exists": true } } ] }, "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 1017, "advanced": 5, "needTime": 1011, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "docsExamined": 1005, "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 1005, "executionTimeMillisEstimate": 0, "works": 1016, "advanced": 1005, "needTime": 11, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "keyPattern": { "nums": 1 }, "indexName": "nums_1", "isMultiKey": true, "direction": "forward", "indexBounds": { "nums": [ "[MinKey, undefined)", "(undefined, [])", "([], MaxKey]" ] }, "keysExamined": 1016, "dupsTested": 1015, "dupsDropped": 10, "seenInvalidated": 0, "matchTested": 0 } } } 

-

db.doc.find({'nums': { $gt: [] }}) ОПАСНО, ПОТОМУ ЧТО В ЗАВИСИМОСТИ ОТ ИНДЕКСА ИСПОЛЬЗУЕМОГО ЭТО МОЖЕТ ПРЕДОСТАВИТЬ НЕОЖИДАННЫЕ РЕЗУЛЬТАТЫ. Это из-за неверного сканирования индекса, который не продвигает никаких документов.

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).count() 0 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count() 0 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count() 5 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).explain('executionStats').executionStats.executionStages { "stage": "KEEP_MUTATIONS", "nReturned": 0, "executionTimeMillisEstimate": 0, "works": 1, "advanced": 0, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "inputStage": { "stage": "FETCH", "filter": { "nums": { "$gt": [ ] } }, "nReturned": 0, "executionTimeMillisEstimate": 0, "works": 1, "advanced": 0, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "docsExamined": 0, "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 0, "executionTimeMillisEstimate": 0, "works": 1, "advanced": 0, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "keyPattern": { "nums": 1 }, "indexName": "nums_1", "isMultiKey": true, "direction": "forward", "indexBounds": { "nums": [ "([], BinData(0, ))" ] }, "keysExamined": 0, "dupsTested": 0, "dupsDropped": 0, "seenInvalidated": 0, "matchTested": 0 } } } 

-

db.doc.find({'nums.0': { $gt: -Infinity }}) возвращает правильные результаты, но имеет плохую производительность (использует полное сканирование коллекции).

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': { $gt: -Infinity }}).count() 5 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': { $gt: -Infinity }}).explain('executionStats').executionStats.executionStages { "stage": "COLLSCAN", "filter": { "nums.0": { "$gt": -Infinity } }, "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 2007, "advanced": 5, "needTime": 2001, "needFetch": 0, "saveState": 15, "restoreState": 15, "isEOF": 1, "invalidates": 0, "direction": "forward", "docsExamined": 2005 } 

-

db.doc.find({'nums': { $gt: -Infinity }}) удивительно, это работает очень хорошо! Он дает правильные результаты и быстро, продвигая 5 документов с этапа сканирования индекса.

 MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: -Infinity }}).explain('executionStats').executionStats.executionStages { "stage": "FETCH", "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 16, "advanced": 5, "needTime": 10, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "docsExamined": 5, "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 5, "executionTimeMillisEstimate": 0, "works": 15, "advanced": 5, "needTime": 10, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "keyPattern": { "nums": 1 }, "indexName": "nums_1", "isMultiKey": true, "direction": "forward", "indexBounds": { "nums": [ "(-inf.0, inf.0]" ] }, "keysExamined": 15, "dupsTested": 15, "dupsDropped": 10, "seenInvalidated": 0, "matchTested": 0 } } 

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

 db.video.find({pictures: {$exists: true, $gt: {$size: 0}}}) db.video.find({comments: {$exists: true, $not: {$size: 0}}}) 

Вы также можете использовать вспомогательный метод Exists над оператором Mongo $ существует

 ME.find() .exists('pictures') .where('pictures').ne([]) .sort('-created') .limit(10) .exec(function(err, results){ ... }); 
 { $where: "this.pictures.length > 1" } 

используйте $ where и передайте this.field_name.length, которые возвращают размер поля массива и проверяют его по сравнению с номером. если любой массив имеет какое-либо значение, чем размер массива, должен быть не менее 1., поэтому все поле массива имеет длину более одного, это означает, что у него есть некоторые данные в этом массиве

 ME.find({pictures: {$exists: true}}) 

Просто так, это сработало для меня.

  • Почему мангуста всегда добавляет s в конец имени моей коллекции
  • Как сделать сырые операции mongodb в мангусте?
  • Найти документ с массивом, который содержит определенное значение
  • MongoDB / Mongoose запрос в определенную дату?
  • Mongoose Уникальный индекс не работает!
  • Поддерживает ли Mongoose метод Mongodb `findAndModify`?
  • Удаление ключа из документа MongoDB с использованием Mongoose
  • Остановить Mongoose от создания свойства _id для элементов массива поддокумента
  • MongoDB, удалять объект из массива
  • Как вы превращаете документ Mongoose в простой объект?
  • Удаление каскадного стиля в Mongoose
  • Давайте будем гением компьютера.