Trouver des enregistrements MongoDB où le champ du tableau n'est pas vide

Tous mes dossiers ont un champ appelé "images". Ce domaine est un tableau de chaînes de caractères.

je veux maintenant les 10 enregistrements les plus récents où ce tableau n'est pas vide.

j'ai cherché sur Google, mais étrangement, je n'ai pas trouvé grand chose sur ça. J'ai lu dans l'option $where, mais je me demandais à quel point c'était lent pour les fonctions natives, et s'il y avait une meilleure solution.

Et même alors, cela ne fonctionne pas:

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

ne renvoie rien. Laisser this.pictures sans le bit de longueur fonctionne, mais il retourne aussi des enregistrements vides, bien sûr.

347
demandé sur Neil Lunn 2013-02-09 19:39:06

10 réponses

si vous avez aussi des documents qui n'ont pas la clé, vous pouvez utiliser:

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

MongoDB n'utilisez pas d'index si $si la taille est impliquée, voici donc une meilleure solution:

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

depuis MongoDB 2.6 release, vous pouvez comparer avec l'opérateur $gt mais pourrait conduire à des résultats inattendus (vous pouvez trouver une explication détaillée dans cette réponse ):

ME.find({ pictures: { $gt: [] } })
558
répondu Chris' 2018-05-23 17:27:37

après un peu plus de recherche, en particulier dans les documents mongodb, et des morceaux de puzzle ensemble, c'était la réponse:

ME.find({pictures: {$not: {$size: 0}}})
159
répondu skerit 2013-02-09 16:02:48

cela pourrait aussi fonctionner pour vous:

ME.find({'pictures.0': {$exists: true}});
86
répondu tenbatsu 2014-07-08 08:17:36

à partir de la version 2.6, une autre façon de procéder est de comparer le champ à un tableau vide:

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

la Tester dans le shell:

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

donc il inclut correctement les docs où pictures a au moins un élément de tableau, et exclut les docs où pictures est soit un tableau vide, pas un tableau, ou manquant.

28
répondu JohnnyHK 2015-06-12 12:34:34

vous vous souciez de deux choses lorsque vous posez des questions - la précision et la performance. Avec cela à l'esprit, j'ai testé quelques approches différentes en MongoDB v3.0.14.

TL;DR db.doc.find({ nums: { $gt: -Infinity }}) est la manière la plus rapide et la plus fiable (au moins dans la MongoDB version que j'ai testé).

EDIT: Cela ne fonctionne plus dans MongoDB v3.6! Voir les commentaires sous ce post pour une solution potentielle.

Setup

j'ai inséré 1K docs avec un champ de liste, 1k docs avec une liste vide, et 5 docs avec une liste non vide.

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

je reconnais que ce n'est pas assez d'une échelle pour prendre la performance aussi au sérieux que je le suis dans les tests ci-dessous, mais c'est assez pour présenter la justesse de diverses requêtes et le comportement des plans de requête choisis.

Essais

db.doc.find({'nums': {'$exists': true}}) renvoie des résultats erronés (pour ce que nous essayons d'accomplir).

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

--

db.doc.find({'nums.0': {'$exists': true}}) retourne des résultats corrects, mais il est également lent en utilisant un balayage complet de collecte (avis COLLSCAN étape dans l'explication).

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 }}}) renvoie des résultats erronés. C'est à cause d'un scan d'index invalide qui n'avance aucun document. Il sera probablement exact mais lent sans l'indice.

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 }}}) retourne des résultats corrects, mais la performance est mauvaise. Il fait techniquement un scan index, mais ensuite il avance tous les docs et doit filtrer à travers eux).

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: [] }}) retourne des résultats corrects et est légèrement plus rapide, mais la performance n'est toujours pas idéale. Il utilise IXSCAN qui n'avance que docs avec un champ de liste existant, mais doit ensuite filtrer le champ vide Liste une par une.

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: [] }}) EST DANGEREUX CAR, SELON L'INDICE UTILISÉ, IL POURRAIT DONNER DES RÉSULTATS INATTENDUS. C'est à cause d'un scan d'index invalide qui n'avance aucun document.

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 }}) retourne les résultats corrects, mais a de mauvaises performances (utilise un balayage complet de la collection).

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 }}) étonnamment, cela fonctionne très bien! Il donne les bons résultats et il est rapide, avance de 5 docs à partir de la phase de scan index.

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
  }
}
26
répondu wojcikstefan 2018-05-16 13:07:45

vous pouvez utiliser l'un des suivants pour atteindre cet objectif.

Tous deux prennent également soin de ne pas retourner un résultat pour les objets qui n'ont pas la clé demandée en eux:

db.video.find({pictures: {$exists: true, $gt: {$size: 0}}})
db.video.find({comments: {$exists: true, $not: {$size: 0}}})
4
répondu Paul Imisi 2015-06-15 08:53:22

vous pouvez également utiliser la méthode helper existe sur l'opérateur Mongo $ existe

ME.find()
    .exists('pictures')
    .where('pictures').ne([])
    .sort('-created')
    .limit(10)
    .exec(function(err, results){
        ...
    });
0
répondu Eat at Joes 2016-02-10 19:28:01
{ $where: "this.pictures.length > 1" }

utilisez le $ où et passez le Ceci.nom_champ.longueur qui renvoie la taille du champ du tableau et le vérifie en le comparant avec le nombre. si une matrice a aucune valeur que la taille du tableau doit être d'au moins 1. donc tous les champs du tableau ont plus d'une longueur, cela signifie qu'il y a des données dans ce tableau

0
répondu Prabhat Yadav 2018-02-28 06:16:51

récupérez tous et seulement les documents où 'pictures' est un tableau et n'est pas vide

ME.find({pictures: {$type: 'array', $ne: []}})

si vous utilisez une version MongoDb antérieure à 3.2 , utilisez $type: 4 au lieu de $type: 'array' . Notez que cette solution n'utilise même pas $ size , donc il n'y a aucun problème avec les index ("les requêtes ne peuvent pas utiliser les index pour la partie $size d'une requête")

la réponse):

ME.find({ photos: { $existe: true, $pas: {$size: 0} } }); JE.find({ photos: { $existe: true, $ne: [] } })

sont erroné parce qu'ils renvoient des documents même si, par exemple, "Photos" est null , undefined , 0, etc.

0
répondu SC1000 2018-09-23 11:51:53
ME.find({pictures: {$exists: true}}) 

aussi Simple que ça, ça a marché pour moi.

-6
répondu Luis Fletes 2015-02-09 20:29:28