web-dev-qa-db-fra.com

Requête pour les documents dont la taille du tableau est supérieure à 1

J'ai une collection MongoDB avec des documents au format suivant:

{
  "_id" : ObjectId("4e8ae86d08101908e1000001"),
  "name" : ["Name"],
  "zipcode" : ["2223"]
}
{
  "_id" : ObjectId("4e8ae86d08101908e1000002"),
  "name" : ["Another ", "Name"],
  "zipcode" : ["2224"]
}

Je peux actuellement obtenir des documents correspondant à une taille de tableau spécifique: 

db.accommodations.find({ name : { $size : 2 }})

Cela renvoie correctement les documents avec 2 éléments dans le tableau name. Cependant, je ne peux pas faire une commande $gt pour renvoyer tous les documents où le champ name a une taille de tableau supérieure à 2:

db.accommodations.find({ name : { $size: { $gt : 1 } }})

Comment sélectionner tous les documents avec un tableau name de taille supérieure à un (de préférence sans modifier la structure de données actuelle)?

522
emson

Mettre à jour:

Pour les versions de mongodb 2.2+ un moyen plus efficace de le faire décrit par @JohnnyHK dans un autre answer .


1.Utiliser $ où

db.accommodations.find( { $where: "this.name.length > 1" } );

Mais...

Javascript s'exécute plus lentement que les opérateurs natifs répertoriés sur cette page, mais est très flexible. Voir la page de traitement côté serveur pour plus d'informations.

2.Créez extra champ NamesArrayLength, mettez-le à jour avec la longueur du tableau de noms, puis utilisez-le dans les requêtes:

db.accommodations.find({"NamesArrayLength": {$gt: 1} });

Ce sera une meilleure solution et fonctionnera beaucoup plus rapidement (vous pouvez créer un index dessus).

407
Andrew Orsich

Il existe un moyen plus efficace de le faire dans MongoDB 2.2+ depuis que vous pouvez utiliser des index de tableau numériques dans les clés d'objet de requête.

// Find all docs that have at least a second name array element.
db.accommodations.find({'name.1': {$exists: true}})

Vous pouvez prendre en charge cette requête avec un index qui utilise une expression de filtre partielle (requiert 3.2+):

db.accommodations.createIndex(
    {'name.1': 1},
    {partialFilterExpression: {'name.1': {$exists: true}}}
);
1104
JohnnyHK

Je pense que c’est la requête la plus rapide qui répond à votre question, car elle n’utilise pas de clause $where interprétée:

{$nor: [
    {name: {$exists: false}},
    {name: {$size: 0}},
    {name: {$size: 1}}
]}

Cela signifie "tous les documents sauf ceux sans nom (tableau non existant ou vide) ou avec un seul nom".

Tester:

> db.test.save({})
> db.test.save({name: []})
> db.test.save({name: ['George']})
> db.test.save({name: ['George', 'Raymond']})
> db.test.save({name: ['George', 'Raymond', 'Richard']})
> db.test.save({name: ['George', 'Raymond', 'Richard', 'Martin']})
> db.test.find({$nor: [{name: {$exists: false}}, {name: {$size: 0}}, {name: {$size: 1}}]})
{ "_id" : ObjectId("511907e3fb13145a3d2e225b"), "name" : [ "George", "Raymond" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225c"), "name" : [ "George", "Raymond", "Richard" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225d"), "name" : [ "George", "Raymond", "Richard", "Martin" ] }
>
111
Tobia

Vous pouvez également utiliser l'agrégat:

db.accommodations.aggregate(
[
     {$project: {_id:1, name:1, zipcode:1, 
                 size_of_name: {$size: "$name"}
                }
     },
     {$match: {"size_of_name": {$gt: 1}}}
])

// vous ajoutez "size_of_name" au document de transit et vous l'utilisez pour filtrer la taille du nom

46
one_cent_thought

Aucun de ce qui précède n'a fonctionné pour moi. Celui-ci l'a fait alors je le partage:

db.collection.find( {arrayName : {$exists:true}, $where:'this.arrayName.length>1'} )
33
Zloy Smiertniy

Essayez de faire quelque chose comme ça:

db.getCollection('collectionName').find({'ArrayName.1': {$exists: true}})

1 est le nombre, si vous voulez récupérer un enregistrement supérieur à 50, faites ArrayName.50.

30
Aman Goel
db.accommodations.find({"name":{"$exists":true, "$ne":[], "$not":{"$size":1}}})
18
Yadvendar

Vous pouvez utiliser $ expr (opérateur de version 3.6 mongo) pour utiliser les fonctions d’agrégation dans une requête normale. 

Comparer query operators vs aggregation comparison operators .

db.accommodations.find({$expr:{$gt:[{$size:"$name"}, 1]}})
15
Veeram

J'ai trouvé cette solution, pour trouver des éléments avec un champ de tableau supérieur à une certaine longueur

db.allusers.aggregate([
  {$match:{username:{$exists:true}}},
  {$project: { count: { $size:"$locations.lat" }}},
  {$match:{count:{$gt:20}}}
])

Le premier agrégat $ match utilise un argument qui vaut pour tous les documents. Si vide, j'aurais 

"errmsg" : "exception: The argument to $size must be an Array, but was of type: EOO"
11
Barrard

MongoDB 3.6 inclut $ expr https://docs.mongodb.com/manual/reference/operator/query/expr/

Vous pouvez utiliser $ expr pour évaluer une expression dans une correspondance $ ou find.

          {$match: {
                  $expr: {$gt: [{$size: "$yourArrayField"}, 0]}
              }
          }

ou trouver

collection.find ({$ expr: {$ gte: [{$ size: "$ yourArrayField"}, 0]}});

0
Daniele Tassone