2011-09-13 3 views
2

Imaginez que j'ai une collection MonogDB contenant des documents comme suit:MongoDB et PHP - De retour d'un comptage d'un tableau imbriqué

{name: 'Some Name', components: {ARRAY OF ITEMS}} 

Comment puis-je retourner le nom et le nombre d'éléments dans les composants? Dois-je utiliser une carte/réduire? J'utilise l'extension Mongo de PHP.

EDIT: snippet de code actuel en PHP (travail) mais je veux juste le nombre des composants

$fields = array(
    'name', 'components' 
); 
$cursor = $this->collection->find(array(), $fields); 
$cursor->sort(array('created_ts' => -1)); 

if (empty($cursor) == true) { 
    return array(); 
} else { 
    return iterator_to_array($cursor); 
} 

Merci, Jim

+0

Veuillez fournir un exemple (le résultat attendu de la requête). –

+0

lire la deuxième partie de la question; enlevé ma réponse. – Petrogad

+0

Exemple de résultat serait quelque chose comme {nom: 'Nom de l'élément', composants: 3} – JimBlizz

Répondre

0

Jim-

Ce sont deux opérations distinctes; Sauf si vous voulez tirer parti de comptage de PHP sur les résultats que vous obtenez que vous voudriez faire quelque chose comme:

$m = new Mongo(); 
$db = $m->selectDB('yourDB'); 
$collection = $db->selectCollection('MyCollection'); 
$cursor = $collection->find(array(), array("name"=>1, "components"=>1)); 
foreach($cursor as $key){ 
    echo($key['name'].' components: '.count($key['components']); 
} 
+0

Dommage, j'espérais éviter de le faire en PHP ou comme une "action" séparée. Pas la fin du monde bien que je suppose! – JimBlizz

0

Vous pouvez utiliser db.eval() et écrire le calcul en JavaScript.

+0

juste curieux de savoir comment cela affecte la performance à grande échelle? – Petrogad

1

Vous pouvez utiliser map-reduce ou vous pouvez utiliser une simple requête group comme suit. Depuis que je suis en supposant que votre propriété de nom est une clé unique, cela devrait fonctionner même si ce n'est pas une raison que vous auriez normalement utiliser la fonction de groupe:

db.test.group({ 
key: { name:true }, 
reduce: function(obj,prev) { 
    var count = 0; 
    for(k in obj.components) 
    count++; 
    prev.count = count; 
}, 
initial: { count: 0} 
}); 

Vous avez dit que vous avez un tableau de composants , mais il semble que vous stockez des composants en tant qu'objet {} et non et le tableau []. C'est pourquoi j'ai dû ajouter la boucle dans la fonction reduce, pour compter toutes les propriétés de l'objet components. S'il s'agissait en fait d'un tableau, vous pouvez simplement utiliser la propriété .length.

En PHP, il ressemblerait à quelque chose comme ça (du Manual):

$keys = array('name' => 1); 
$initial = array('count' => 0); 
$reduce =<<<JS 
function(obj,prev) { 
    var count = 0; 
    for(k in obj.components) 
    count++; 
    prev.count = count; 
}, 
JS; 

$m = new Mongo(); 
$db = $m->selectDB('Database'); 
$coll = $db->selectCollection('Collection'); 
$data = $coll->group($keys, $initial, $reduce); 

Enfin, je suggère fortement que si vous essayez d'accéder au nombre de vos composants sur une base régulière que vous stockez le compte comme une propriété supplémentaire du document et le mettre à jour chaque fois qu'il change. Si vous tentez d'écrire des requêtes filtrées en fonction de ce nombre, vous pourrez également ajouter un index sur cette propriété.

+0

PPrice; juste curieux comment cela affecte-t-il la performance avec de grandes collections? Merci pour le post, je l'ai trouvé utile. – Petrogad

+1

Les performances vont se dégrader au débit NxM où N est le nombre moyen de propriétés dans les sous-documents composants et M est le nombre total de documents dans la table. Je vous suggère fortement d'ajouter une propriété count à chacun des documents afin que vous n'ayez pas besoin de le calculer sur le fichier - en particulier si vous utilisez le nombre pour filtrer les résultats de la requête (indexer la propriété count). – PPrice

0

couru à travers aujourd'hui, si vous utilisez le nouveau pilote avec un agrégat, vous pouvez le faire en php, (donné ce schéma)

{name: 'Some Name', components: {ARRAY OF ITEMS}} 

En PHP:

$collection = (new Client())->db->my_collection; 
    $collection->aggregate([ 
     '$match' => ['name' => 'Some Name'], 
     '$group' => [ 
      '_id' => null, 
      'total'=> ['$sum' => "\$components"] 
     ] 
    ]); 

L'astuce ici PHP est d'échapper au signe dollar, c'est essentiellement ce que dit la documentation mongo lors de l'utilisation de la taille ou la somme

https://docs.mongodb.com/manual/reference/operator/aggregation/size/

https://docs.mongodb.com/manual/reference/operator/aggregation/sum/

Le problème que je HAD mongo met champs comme "$field" et PHP ne pas comme ça du tout à cause de la façon dont il fait une interpolation variable. Cependant, une fois que vous échapper à la $, cela fonctionne très bien.

Je pense que pour ce cas particulier, vous aurez besoin de faire quelque chose de similaire, mais avec $project au lieu de $group Vous aimez cette

$collection = (new Client())->db->my_collection; 
    $collection->aggregate([ 
     '$match' => ['name' => 'Some Name'], 
     '$project' => [ 
      'name' => "\$name", 
      'total'=> ['$sum' => "\$components"] 
     ] 
    ]); 

C'est une vieille question, mais car il n'y a pas de réponse choisi, je vais laisse juste ceci ici.

Questions connexes