1
votes

I'm trying to translate aggregation from the MongoDB shell to ruby code that uses Mongoid as ODM.

I have some documents like this (very simplified example):

{
  "name": "Foo",
  "tags": ["tag1", "tag2", "tagN"] 
},
{
  "name": "Bar",
  "tags": ["tagA", "tag2"]
},
...

Now I'd like to get all documents with the name field and the total number of tags for each.

In the MongoDB shell I can achieve it using aggregation framework like this:

db.documents.aggregate(
  {$project: {name: 1, tags_count: {$size: $tags}}
)

And it will return:

[{"name": "Foo", "tags_count": 3},
{"name": "Bar", "tags_count": 2}]

Now the frustrating part, I'm trying to implement the same query inside a rails app using Mongoid as ODM.

The code looks like (using rails console):

Document.collection.aggregate(
   [
     {'$project': {name: 1, tags_count: {'$size': '$tags'}}}
   ]
 ).to_a

And it returns the next error:

Mongo::Error::OperationFailure: The argument to $size must be an Array, but was of type: EOO (17124)

My question is: How can I make Mongoid understand that $tags makes reference to the correct field? Or what I'm missing from the code?

Thanks

1
It would appear that your data does not actually contain arrays, or perhaps not consistently. The same statement should fail in the shell on the same data for the same reason. - Neil Lunn

1 Answers

2
votes

It looks like there is data which does not consistently have an array in the field. For this you can use $ifNull to place an empty array where none is found and thus return the $size as 0:

Document.collection.aggregate(
   [
     {'$project': {name: 1, tags_count: {'$size': { '$ifNull': [ '$tags', [] ] } } }}
   ]
 ).to_a

Alternately you could simply skip where the field is not present at all using $exists:

Document.collection.aggregate(
   [
     {'$match': { 'tags_count': { '$exists': true } } },
     {'$project': {name: 1, tags_count: {'$size': '$tags'}}}
   ]
 ).to_a

But of course that will filter those documents from the selection, which may or may not be the desired effect.