Tous mes enregistrements ont un champ appelé "images". Ce champ est un tableau de chaînes.
Je veux maintenant les 10 derniers enregistrements où ce tableau N'EST PAS vide.
J'ai fait des recherches sur Google, mais curieusement, je n'ai pas trouvé grand-chose à ce sujet. J'ai lu l'option $ where, mais je me demandais à quel point cela é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 cela renvoie également des enregistrements vides, bien sûr.
mongoengine
ME.find({ pictures: { $gt: [] } })
EST DANGEREUX, même dans les nouvelles versions de MongoDB. Si vous avez un index sur votre champ de liste et que cet index est utilisé pendant la requête, vous obtiendrez des résultats inattendus. Par exemple:db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count()
retourne le bon numéro, tandis quedb.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count()
retourne0
.Après un peu plus de recherche, en particulier dans les documents mongodb, et des morceaux déroutants ensemble, ce fut la réponse:
la source
pictures
champ.Cela pourrait également fonctionner pour vous:
la source
pictures.2
existe mais quipictures.1
n'existe pas?$exists
opérateur est un booléen, pas un offset. @tenbatsu devrait être utilisé à latrue
place de1
.Would there ever be a case where pictures.2 exists but pictures.1 does not?
Oui, ce cas pourrait se produire.pictures
s'agit d'un sous-document, pas d'un tableau. par exemplepictures: {'2': 123}
pictures
.Vous vous souciez de deux choses lorsque vous interrogez - la précision et les performances. Dans cet esprit, j'ai testé quelques approches différentes dans MongoDB v3.0.14.
TL; DR
db.doc.find({ nums: { $gt: -Infinity }})
est le plus rapide et le plus fiable (au moins dans la version MongoDB que j'ai testée).EDIT: Cela ne fonctionne plus dans MongoDB v3.6! Voir les commentaires sous ce post pour une solution potentielle.
Installer
J'ai inséré 1 000 documents sans champ de liste, 1 000 documents avec une liste vide et 5 documents avec une liste non vide.
Je reconnais que ce n'est pas une échelle suffisante pour prendre les performances aussi au sérieux que dans les tests ci-dessous, mais cela suffit pour présenter l'exactitude des diverses requêtes et le comportement des plans de requête choisis.
Les tests
db.doc.find({'nums': {'$exists': true}})
renvoie des résultats erronés (pour ce que nous essayons d'accomplir).-
db.doc.find({'nums.0': {'$exists': true}})
renvoie des résultats corrects, mais il est également lent à l'aide d'une analyse complète de la collection (COLLSCAN
étape de notification dans l'explication).-
db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}})
renvoie des résultats erronés. C'est à cause d'une analyse d'index non valide qui ne fait avancer aucun document. Il sera probablement précis mais lent sans l'index.-
db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}})
renvoie des résultats corrects, mais les performances sont mauvaises. Techniquement, il effectue un scan d'index, mais il avance toujours tous les documents et doit ensuite les filtrer).-
db.doc.find({'nums': { $exists: true, $ne: [] }})
renvoie des résultats corrects et est légèrement plus rapide, mais les performances ne sont toujours pas idéales. Il utilise IXSCAN qui ne fait avancer les documents qu'avec un champ de liste existant, mais doit ensuite filtrer les listes vides une par une.-
db.doc.find({'nums': { $gt: [] }})
EST DANGEREUX PARCE QUE SELON L'INDICE UTILISÉ PEUT DONNER DES RÉSULTATS INATTENDUS. C'est à cause d'une analyse d'index invalide qui ne fait avancer aucun document.-
db.doc.find({'nums.0’: { $gt: -Infinity }})
renvoie des résultats corrects, mais a de mauvaises performances (utilise une analyse complète de la collection).-
db.doc.find({'nums': { $gt: -Infinity }})
étonnamment, cela fonctionne très bien! Il donne les bons résultats et c'est rapide, en avançant 5 documents de la phase de scan d'index.la source
seen_events
tableau String, qui est également indexé. En recherchant avec{ $gt: -Infinity }
, j'obtiens immédiatement 0 document. En utilisant,{ $exists: true, $ne: [] }
j'obtiens les documents les plus probables de 1,2 m, avec beaucoup de temps perdu dans l'étape FETCH: gist.github.com/N-Coder/b9e89a925e895c605d84bfeed648d82cdb.test_collection.find({"seen_events.0": {$exists: true}})
est mauvais car il utilise un scan de collection. 2.db.test_collection.find({seen_events: {$exists: true, $ne: []}})
est mauvais parce que son IXSCAN correspond à tous les documents, puis le filtrage est effectué dans la phase lente FETCH 3. Idem pourdb.test_collection.find({seen_events: {$exists: true, $not: {$size: 0}}})
4. Toutes les autres requêtes renvoient des résultats invalidesseen_events
contiennent des chaînes, vous pouvez utiliser ceci:db.test_collection.find({seen_events: {$gt: ''}}).count()
. Pour confirmer qu'il fonctionne bien, consultezdb.test_collection.find({seen_events: {$gt: ''}}).explain(true).executionStats
. Vous pouvez probablement faire en sorte que les événements vus soient des chaînes via la validation de schéma: docs.mongodb.com/manual/core/schema-validationÀ partir de la version 2.6, une autre méthode consiste à comparer le champ à un tableau vide:
Le tester dans le shell:
Ainsi, il inclut correctement les documents où
pictures
a au moins un élément de tableau et exclut les documents oùpictures
est soit un tableau vide, pas un tableau, ou manquant.la source
db.ME.createIndex({ pictures: 1 })
et ensuitedb.ME.find({pictures: {$gt: []}})
ne retournera aucun résultat, au moins dans MongoDB v3.0.14Vous pouvez utiliser l'une des méthodes suivantes pour y parvenir.
Les deux prennent également soin de ne pas retourner de résultat pour les objets qui ne contiennent pas la clé demandée:
la source
Récupérer tous et uniquement les documents où «images» est un tableau et n'est pas vide
Si vous utilisez une version MongoDb antérieure à 3.2 , utilisez à la
$type: 4
place de$type: 'array'
. Notez que cette solution n'utilise même pas $ size , donc il n'y a pas de problème avec les index ("Les requêtes ne peuvent pas utiliser d'index pour la partie $ size d'une requête")Autres solutions, y compris celles-ci (réponse acceptée):
sont mal parce qu'ils renvoient des documents même si, par exemple, « images » est
null
,undefined
, 0, etc.la source
Utiliser l'
$elemMatch
opérateur: selon la documentation$elemMatches
s'assure que la valeur est un tableau et qu'elle n'est pas vide. Ainsi, la requête serait quelque chose commeME.find({ pictures: { $elemMatch: {$exists: true }}})
PS Une variante de ce code se trouve dans le cours M121 de l'Université MongoDB.
la source
Vous pouvez également utiliser la méthode d'assistance Existe sur l'opérateur Mongo $ existe
la source
utilisez le $ où et passez le this.field_name.length qui retourne la taille du champ du tableau et vérifiez-le en comparant avec le nombre. si un tableau a une valeur dont la taille du tableau doit être d'au moins 1. donc tous les champs du tableau ont une longueur supérieure à un, cela signifie qu'il contient des données dans ce tableau
la source
Aussi simple que cela, cela a fonctionné pour moi.
la source