42

Consider a MongoDB document in users collection:

{ username : 'Alex', tags: ['C#', 'Java', 'C++'] }

Is there any way, to get the length of the tags array from the server side (without passing the tags to the client) ?

Thank you!

6 Answers 6

31

if username Alex is unique, you can use next code:

db.test.insert({username:"Alex", tags: ['C#', 'Java', 'C++'] });
db.test.aggregate(
  {$match: {username : "Alex"}}, 
  {$unwind: "$tags"},
  {$project: {count:{$add:1}}},
  {$group: {_id: null, number: {$sum: "$count" }}}
);
{ "result" : [ { "_id" : null, "number" : 3 } ], "ok" : 1 }
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. The question was asked back then when the aggregation framework was not available, but it makes perfect sense now.
20

Now MongoDB (2.6 release) supports $size operation in aggregation.

From the documentation:

{ <field>: { $size: <array> } }

What you want can be accomplished as following with either by using this:

db.users.aggregate(
   [
      {
         $group: {
            _id: "$username",
            tags_count:  {$first: {$size: "$tags" }}
         }
      }
   ]
)

or

db.users.aggregate(
   [
      {
         $project: {
            tags_count: {$size: "$tags"}
         }
      }
   ]
)

4 Comments

I don't think you can use $size operator just like this with $group. $size is not among aggregation operators. Instead, you can use projection if you don't need grouping: db.test.aggregate({$project: count: {$size: "$tags"}}) OR if you need grouping, smth like this: db.test.aggregate({$group: {_id: "$userName", count: {$sum: {$size:"$tags"}}}})
How is it possible to get when I have something like this: { username : 'Alex', tags: [['C#', 'Java', 'C++']] } How can we get the length/size of tags now?
@rkatkam if tags is simply an array of strings, check the answer. If it is an array of arrays, you might want to unwind tags first
I tried using $unwind, but that is giving me an empty list as output. See here stackoverflow.com/questions/28538982/…
16

I think it might be more efficient to calculate the number of tags on each save (as a separate field) using $inc perhaps or via a job on a schedule.

You could also do this with map/reduce (the canonical example) but that doesn't seem to be be what you'd want.

I'm not sure it's possible to do exactly what you are asking, but you can query all the documents that match a certain size with $size ...

> db.collection.find({ tags : { $size: 3 }});

That'd get you all the documents with 3 tags ...

Comments

10

xmm.dev's answer can be simplified: instead of having interm field 'count', you can sum directly in $group:

db.test.aggregate(
  {$match: {username : "Alex"}}, 
  {$unwind: "$tags"},
  {$group: {_id: null, number: {$sum: 1 }}}
) 

Comments

9

Currently, the only way to do it seems to be using db.eval, but this locks database for other operations.

The most speed-efficient way would be adding an extra field that stores the length of the array and maintaining it by $inc and $push operations.

1 Comment

Thank you very much. Yep, I also thought about keeping the size in a separate field, but hoped that there would be a better way.
0

I did a small work around as I needed to query the array size and return if it was greater than 0 but could be anything from 1-3.

Here was my solution:

db.test.find($or : [{$field : { $exists : true, $size : 1}},
                    {$field : { $exists : true, $size : 2}},
                    {$field : { $exists : true, $size : 3}}, ])

This basically returns a document when the attribute exists and the size is 1, 2, or 3. The user can add more statements and increment if they are looking for a specific size or within a range. I know its not perfect but it did work and was relatively quick. I only had 1-3 sizes in my attribute so this solution worked.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.