开发者

Querying internal array size in MongoDB

开发者 https://www.devze.com 2023-03-21 00:50 出处:网络
Consider a MongoDB document in users collection: { username : \'Alex\', tags: [\'C#\', \'Java\', \'C++\'] }

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) ?

开发者_如何学JAVAThank you!


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 }


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"}
         }
      }
   ]
)


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 ...


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


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.


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.

0

精彩评论

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