开发者

MongoDB & PHP - Returning a count of a nested array

Imagine I have a MonogDB collection containing documents as follows:

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

How can I return the name and the count of items in components? Do I have to use a map/reduce?

I am using PHP's Mongo extension.

EDIT: Snippet of current code in PHP (working) but I just want count of the compo开发者_开发知识库nents

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

Thanks, Jim


You could use map-reduce or you could use a simple group query as follows. Since I am assuming that your name property is a unique key, this should work even though it isn't a reason that you'd normally use the group function:

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

You mentioned that you have an array of components, but it appears that you are storing components as an object {} and not and array []. That is why I had to add the loop in the reduce function, to count all of the properties of the components object. If it were actually an array then you could simply use the .length property.

In PHP it would look something like this (from the 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);

Finally, I would strongly suggest that if you are trying to access the count of your components on a regular basis that you store the count as an additional property of the document and update it whenever it changes. If you are attempting to write queries that filter based on this count then you will also be able to add an index on that components property.


You could use db.eval() and write the calculation in JavaScript.


Jim-

These are two separate operations; Unless you want to leverage PHP's count on the results you get which you would then do something like:

$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']);
}


Ran across this today, If your using the new driver with aggregate you can do this in php, ( given this schema )

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

In PHP:

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

The trick here with PHP is to escape the $ dollar sign, this is basically what the mongo documentation says when using size or sum

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

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

The problem I had is mongo puts fields in as "$field" and PHP doesn't like that at all because of the way it does variable interpolation. However, once you escape the $, it works fine.

I think for this particular case you'd need to do something similar but with $project instead of $group Like this

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

This is an old question but seeing as there is no answer picked, I'll just leave this here.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜