Encuentra registros de MongoDB donde el campo de matriz no está vacío

Todos mis registros tienen un campo llamado “imágenes”. Este campo es una matriz de cadenas.

Ahora quiero los 10 registros más nuevos donde esta matriz NO está vacía.

He buscado en Google, pero, curiosamente, no he encontrado mucho sobre esto. Leí en la opción $ where, pero me preguntaba qué tan lenta es para las funciones nativas, y si hay una mejor solución.

E incluso entonces, eso no funciona:

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

No devuelve nada Dejar this.pictures sin el bit de longitud funciona, pero luego también devuelve registros vacíos, por supuesto.

Si también tiene documentos que no tienen la clave, puede usar:

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

MongoDB no usa índices si se trata de $ size, así que aquí hay una mejor solución:

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

Desde la versión de MongoDB 2.6, puede comparar con el operador $gt pero podría generar resultados inesperados (puede encontrar una explicación detallada en esta respuesta ):

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

Después de mirar un poco más, especialmente en los documentos de mongodb, y enigmáticos fragmentos, esta fue la respuesta:

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

Esto también podría funcionar para usted:

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

Comenzando con la versión 2.6, otra forma de hacerlo es comparar el campo con una matriz vacía:

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

Probándolo en el caparazón:

 > 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 ] } 

Por lo tanto, incluye correctamente los documentos donde las pictures tienen al menos un elemento de matriz y excluye los documentos donde las pictures son una matriz vacía, no una matriz, o falta.

Le importan dos cosas cuando consulta: precisión y rendimiento. Con eso en mente, probé algunos enfoques diferentes en MongoDB v3.0.14.

TL; DR db.doc.find({ nums: { $gt: -Infinity }}) es el más rápido y confiable (al menos en la versión de MongoDB que probé).

EDITAR: ¡Esto ya no funciona en MongoDB v3.6! Vea los comentarios debajo de esta publicación para una posible solución.

Preparar

Inserté 1k documentos sin un campo de lista, 1k documentos con una lista vacía y 5 documentos con una lista no vacía.

 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 }); 

Reconozco que esto no es una escala suficiente para tomar el rendimiento tan en serio como lo estoy en las pruebas a continuación, pero es suficiente para presentar la corrección de varias consultas y el comportamiento de los planes de consulta elegidos.

Pruebas

db.doc.find({'nums': {'$exists': true}}) devuelve resultados incorrectos (por lo que estamos tratando de lograr).

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

-

db.doc.find({'nums.0': {'$exists': true}}) arroja resultados correctos, pero también es lento usando un escaneo completo de la colección (observe la etapa COLLSCAN en la explicación).

 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 }}}) devuelve resultados incorrectos. Eso se debe a un escaneo de índice no válido que no avanza en ningún documento. Es probable que sea preciso pero lento sin el índice.

 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, "restreState": 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, "restreState": 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, "restreState": 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 }}}) devuelve resultados correctos, pero el rendimiento es malo. Técnicamente hace un escaneo de índice, pero luego avanza todos los documentos y luego tiene que filtrarse a través de ellos).

 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, "restreState": 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, "restreState": 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, "restreState": 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: [] }}) arroja resultados correctos y es un poco más rápido, pero el rendimiento aún no es ideal. Utiliza IXSCAN que solo avanza documentos con un campo de lista existente, pero luego tiene que filtrar las listas vacías una por una.

 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, "restreState": 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, "restreState": 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, "restreState": 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: [] }}) ES PELIGROSO DEBIDO A QUE DEPENDIENDO DEL ÍNDICE UTILIZADO, PUEDE DAR RESULTADOS INESPERADOS. Eso se debe a un análisis de índice no válido que no avanza en ningún documento.

 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, "restreState": 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, "restreState": 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, "restreState": 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 }}) devuelve los resultados correctos, pero tiene un mal rendimiento (utiliza un escaneo completo de la colección).

 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, "restreState": 15, "isEOF": 1, "invalidates": 0, "direction": "forward", "docsExamined": 2005 } 

-

db.doc.find({'nums': { $gt: -Infinity }}) sorprendentemente, ¡esto funciona muy bien! Da los resultados correctos y es rápido, avanzando 5 documentos desde la fase de exploración de índice.

 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, "restreState": 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, "restreState": 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 } } 

Puede usar cualquiera de los siguientes para lograr esto.
Ambos también se encargan de no devolver un resultado para los objetos que no tienen la clave solicitada en ellos:

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

También puede usar el método de ayuda Existe el operador Mongo $ existe

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

utilice $ donde y pase el this.field_name.length que devuelve el tamaño del campo del array y compruébelo comparando con el número. si cualquier matriz tiene algún valor que el tamaño de la matriz debe ser al menos 1. por lo que todos los campos de la matriz tienen una longitud de más de uno, esto significa que tienen algunos datos en esa matriz

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

Tan simple como eso, esto funcionó para mí.