Not very clear here how "tags" actually appears in your document, where it is either just a single value in a field or in an array. At any rate if you intending to count tags then you are best off using the aggregation framework rather than "post processing" your .find() results in code.
First with "tags" as a single field value:
Story.aggregate(
[
// Match the documents with tags
{ "$match": { "tags": { "$in": tags } }},
// Group the tags with counts
{ "$group": {
"_id": "$tags",
"count": { "$sum": 1 }
}},
// Group to single array with total count
{ "$group": {
"_id": null,
"tags": {
"$push": {
"tag": "$_id",
"count": "$count"
}
},
"totalCount": { "$sum": "$count" }
}},
// Project to remove the _id
{ "$project": {
"_id": 0,
"tags": 1,
"totalCount": 0
}}
],
function(err,result) {
if (err)
res.send(err);
res.json( result[0] );
}
);
That pretty much encapsulates your entire listing so there is no need for that additional .count() operation, but also not sure if you really need it.
Where the "tags" is actually an array then you need to do things a little differently, as you need to extract your matches from the array to get the counts:
Story.aggregate(
[
// Match the documents with tags
{ "$match": { "tags": { "$in": tags } }},
// Unwind the array
{ "$unwind": "$tags" },
// Actually match the tags within the now unwound array
{ "$match": { "tags": { "$in": tags } }},
// Group by each tag
{ "$group": {
"_id": "$tags",
"count": { "$sum": 1 }
}},
// Rename the _id for your output
{ "$project": {
"_id": 0,
"tag": "$_id",
"count": 1
}}
],
function(err,result) {
if (err)
res.send(err);
res.json({ "totalCount": count, "tags": result });
}
);
Or with MongoDB 2.6 or greater you can streamline a little possibly by filtering the array before you $unwind the array using the $map operator and others.
I'll just expand the tags value here for clarity
Story.aggregate(
[
// Match the documents with tags
{ "$match": { "tags": { "$in": ["tag1","tag2" } }},
// Filter the array for matches
{ "$project": {
"tags": {
"$setDifference": [
{
"$map": {
"input": "$tags",
"as": "el",
"in": {
"$cond": [
{ "$or": [
{ "$eq": ["$$el", "tag1" ] },
{ "$eq": ["$$el", "tag2" ] },
]},
"$$el",
false
]
}
}
},
[false]
]
}
}},
// Unwind the array already filtered
{ "$unwind": "$tags" },
// Group by each tag
{ "$group": {
"_id": "$tags",
"count": { "$sum": 1 }
}},
// Rename the _id for your output
{ "$project": {
"_id": 0,
"tag": "$_id",
"count": 1
}}
],
function(err,result) {
if (err)
res.send(err);
res.json({ "totalCount": count, "tags": result });
}
);
Which has the advantage of removing the "tags" you do not want from the array before calling $unwind which can speed up the matching depending on how many array elements there are.
Of course while the above examples differ from the first in obtaining the document count, you can still include this with a little extra grouping if you prefer the combined result:
Story.aggregate(
[
// Match the documents with tags
{ "$match": { "tags": { "$in": ["tag1","tag2" } }},
// Filter the array for matches
{ "$project": {
"tags": {
"$setDifference": [
{
"$map": {
"input": "$tags",
"as": "el",
"in": {
"$cond": [
{ "$or": [
{ "$eq": ["$$el", "tag1" ] },
{ "$eq": ["$$el", "tag2" ] },
]},
"$$el",
false
]
}
}
},
[false]
]
}
}},
// Unwind the array already filtered
{ "$unwind": "$tags" },
// Group by each tag
{ "$group": {
"_id": "$tags",
"count": { "$sum": 1 },
"docs": { "$addToSet": "$_id" }
}},
// Group to a single array response
{ "$group": {
"_id": null,
"tags": {
"$push": {
"tag": "$_id",
"count": "$count"
}
},
"totalCount": { "$sum": { "$size": "$docs" } }
}
// Rename the fields for your output
{ "$project": {
"_id": 0,
"tags": 1,
"count": 1,
"totalCount"
}}
],
function(err,result) {
if (err)
res.send(err);
res.json( result[0] );
}
);
Again, obtaining that "totalCount" in a singular result can be done without the help of operators such as $size and indeed without the $addToSet by grouping differently.
But the overall direction here is this is where you basically want to go if you are counting "tags", as you can let the server do the work for you.
tags : [ { element: element1, count: count1 }, { element: element2, count: count2 } ](Are the stories the elements? And are the counts the number of times the tags appear in the stories?)tags : [ { tag: tag1, count: 4 }, { tag: tag2, count: 2 } ](stories are the db docs that have an array of tags) where this would be returning an array that consists of each queried tag and that tag's count (of stories wherein that tag appears)Story.countisn't necessary since it's counting all stories that contain those tags. What you need is the second query, theStory.findthat you have above, and then perhaps iterate over those stories collecting the stats you need to output in thetagsblock you've described.