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 components

$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

share|improve this question
please provide a sample (the expected result of the query). – Karoly Horvath Sep 13 '11 at 13:14
read the second part of question; removed my answer. – Petrogad Sep 13 '11 at 13:17
Example result would be something like {name: 'Name of item', components: 3} – JimBlizz Sep 13 '11 at 13:33

3 Answers

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.

share|improve this answer
PPrice; just curious how does this effect performance with large collections? Thanks for the post I found it helpful. – Petrogad Sep 15 '11 at 13:41
1  
Performance will degrade at NxM rate where N is the average number of properties in the components sub-documents and M is the total number of documents in the table. I would strongly suggest you add a count property to each of the documents so that you don't have to compute it on the file - especially if you are using the count to filter query results (index the count property). – PPrice Sep 20 '11 at 19:22

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

share|improve this answer
just curious how does this impact performance on a large scale? – Petrogad Sep 13 '11 at 13:37

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']);
}
share|improve this answer
Shame, I was hoping to avoid doing it in PHP or as a seperate "action". Not the end of the world though I guess! – JimBlizz Sep 13 '11 at 13:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.