Retrieve documents with matching values on a given field in mongodb - mongodb

Let's say I have the following two documents:
{ "_id" : ObjectId("5d1faa57a370cc52f0614313"), "bucket" : [ [2, 3], [ 111, 111 ]]}
{ "_id" : ObjectId("6d1faa57a370cc52f0614311"), "bucket" : [ [2, 3], [ 999, 999 ]]}
Both documents share the [2,3] value on the bucket field.
How can I retrieve such documents from a mongodb collection? Is this even possible?
Sorry if the question is stupid, I'm a mongo newbie.

That's possible. You just need to pass the matching argument inside find().
db.getCollection('test').find({"bucket" :[2,3]})
I have provided an image link for your reference.

Related

Is two-way referencing more efficient in Mongo for a 1 to N relationship?

I have a discussion at work about two-way referencing in a 1 to N relationship. According to this post in MongoDB blog, you can do it. We wouldn't need atomic updates at all, so no problem there. Following the example in the article, in our case you can only create or delete task but not change the task owner.
My argument is that two-way referencing is probably more efficient for fetching data from both sides, as we will need to display more often the owner with their tasks and less often just the tasks, in different parts of the program. My colleague says there won't be an efficiency gain and the data duplication is not worth it.
Do you have any info about the efficiency of this approach?
De-normalizing and storing the data helps when we have less write and more read. Here the efficiency depends upon how the data is retrieved. If our retrieval of data from the collections requires two way referencing and if we already have it then certainly it improves the efficiency of our query.
Student collection
{ _id:1, name: "Joseph", courses:[1, 3, 4]}
{ _id:2, name: "Mary", courses:[1, 3]}
{ _id:3, name: "Catherine", courses:[1, 2, 4]}
{ _id:4, name: "Robert", courses:[2, 4]}
Course Collection
{ _id:1, name: "Math101", students: [1, 2, 3]}
{ _id:2, name: "Science101", students: [3, 4]}
{ _id:3, name: "History101", students: [1, 2]}
{ _id:4, name: "Astronomy101", students: [1, 3, 4]}
Consider the above example of Students and Courses, here two way referencing is done, the courses array in Students collection gives us the different courses studied by the student. Similarly the Students array in the Courses collection gives us the students who are studying the respective course.
If we want to list the students who were studying Math101 then the query would be
db.courses.aggregate([{$match: {name:"Math101"}},
{$unwind:"$students"},
{$lookup:{from:"students",
localField:"students",
foreignField:"_id",
as:"result"}}])
$match, $unwind, $lookup in the aggregation pipeline are used to achieve the result. $match to reduce the data(it is good to use this operator in the start of the aggregation pipeline), $unwind to unwind the students array in the Courses collection, $lookup to look in to the Students collection and get the student details
The result after executing the above aggregation query on our sample collections is
{
"_id" : 1,
"name" : "Math101",
"students" : 1,
"result" : [
{
"_id" : 1,
"name" : "Joseph",
"courses" : [
1,
3,
4
]
}
]
}
{
"_id" : 1,
"name" : "Math101",
"students" : 2,
"result" : [
{
"_id" : 2,
"name" : "Mary",
"courses" : [
1,
3
]
}
]
}
{
"_id" : 1,
"name" : "Math101",
"students" : 3,
"result" : [
{
"_id" : 3,
"name" : "Catherine",
"courses" : [
1,
2,
4
]
}
]
}
The efficiency on two way referencing purely based on what we retrieve, hence design your schema closely aligned with your expected results.

Nested Query Mongodb

I have this beautiful Json , and I'm trying with the powerful mongodb query to get all comments with file_id 12....so this is what I would like have back [4,5,7,10,11,15].
I tried with this query but the file_id it's completely ignored by the engine:
db.collection.distinct("changes.comments",
{"my_uuid":"bf48e757-1a65-4546-bf24-2bb001effddd",
"changes":{$elemMatch:{file_id:12}} }
)
Output:
{
"_id" : ObjectId("5342bf796b03d7ffc834afcc"),
"my_uuid" : "bf48e757-1a65-4546-bf24-2bb001effddd",
"changes" : [
{
"file_id" : 12,
"version" : 1,
"comments" : [
4,
5,
7
],
"lastseen" : 1394640549
},
{
"file_id" : 12,
"version" : 2,
"comments" : [
10,
11,
15
],
"lastseen" : 1394640511
},
{
"file_id" : 234,
"version" : 1,
"comments" : [
100,
110,
150
],
"lastseen" : 1394640555
}
]
}
Thanks in advance
You can use the aggregation framework to achieve what you what. Although the query looks complex for what you are trying to do, it's simple once you get a hang of it.
db.collection.aggregate([
// Get only documents where "my_uuid" equals "bf48e757-1a65-4546-bf24-2bb001effddd"
{"$match":{"my_uuid":"bf48e757-1a65-4546-bf24-2bb001effddd"}},
// Unwind the "changes" array
{"$unwind":"$changes"},
// Get only elements of the "changes" array where "file_id" equals 12
{"$match":{"changes.file_id":12}},
// Unwind the "comments" array
{"$unwind":"$changes.comments"},
// Group by _id and add the comments to array only if not already present
{"$group":{_id:"$_id", comments:{$addToSet:"$changes.comments"}}},
// Cleanup the output
{"$project":{_id:0, comments:1}}
])
Output:
{
"result" : [
{
"comments" : [
4,
5,
7,
10,
11,
15
]
}
],
"ok" : 1
}
EDIT: Including my_uuid in the results is fairly straight-forward. We just need to group by my_uuid instead of _id:
db.collection.aggregate([
{"$match":{"my_uuid":"bf48e757-1a65-4546-bf24-2bb001effddd"}},
{"$unwind":"$changes"},
{"$match":{"changes.file_id":12}},
{"$unwind":"$changes.comments"},
{"$group":{_id:"$my_uuid", comments:{$addToSet:"$changes.comments"}}},
{"$project":{_id:0, my_uuid:"$_id", comments:1}}
])
Currently there is no straight forward way of pulling out only the matching document from an array. The $elemMatch operator will only ensure that at least one of the documents within the array satisfies the condition provided by you. The query will however, always return the entire document. One way to achieve what you are looking for is -
db.sample4.aggregate({$unwind:"$changes"},{$match:{"changes.file_id":12}},{$project:{"changes.comments":1,"_id":0}});
These topics are covered here in stackoverflow, where map-reduce approach as well is listed to achieve this. If the requirement was to return the first matching document, the you could have projected using changes.comments.$:1 Eg. - db.sample4.find({"changes":{$elemMatch:{"file_id":12}} },{"changes.comments.$":1} )

Mongo: Array field has no values in given array?

I'm trying to write up a mongo query that finds all entries where the "steps" field has no values within an array argument.
So for example, given two entries with values:
Entry1:
steps: [3, 4]
Entry2:
steps: [3, 5]
The query should return entry1, but not entry 2, for input array [4, 8, 10]. I'm quite new to mongo - any ideas appreciated.
You mean you have some records:
db.foo.find()
{ "_id" : 1, "steps" : [ 3, 4 ] }
{ "_id" : 2, "steps" : [ 3, 5 ] }
Then you would query:
> db.foo.find({steps:{$in:[4,8,10]}})
{ "_id" : 1, "steps" : [ 3, 4 ] }
the $in clause will pick records in which any stored element matches any of terms in the array supplied in the query

Mongo Array in Array Query

Given the below example record, how can I find all users that belong to at least one group from an arbitrary set of groups to query against? For example, find all users that belong to any one of the following groups - 1, 10, 43. I'm looking for a generalized solution. I know I can build out an or query but is there a more efficient way to handle this?
> db.users.findOne()
{
"_id" : ObjectId("508f477aca442be537000000"),
"name" : "Some Name",
"email" : "some#email.com",
"groups" : [
1,5,10
]
}
{ groups: {$in: [1, 10, 43]} }

How to query recent comments in Mongodb

The the post document looks like this:
{
...
comments: [{
_id:...
body:...
createDate:...
},
...
]
}
How do I get recent 10 comments from the collection?
If your comments are always in a predictable order (i.e. newest first, or newest last), then you can use the $slice operator to return just a subset of the full comments field when querying:
test> db.foo.save({name: "hello", comments: [1, 2, 3, 4, 5]})
test> db.foo.find({}, {comments: {$slice: 3}})
{ "_id" : ObjectId("4ec7d1c8e72da9b6f31e2528"), "name" : "hello", "comments" : [ 1, 2, 3 ] }
test> db.foo.find({}, {comments: {$slice: -3}})
{ "_id" : ObjectId("4ec7d1c8e72da9b6f31e2528"), "name" : "hello", "comments" : [ 3, 4, 5 ] }
You can read more about controlling the returned fields at http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
There is no way to partially select the items from embedded document. No matter what it will return the entire array of document. You have to do the filter in your application code. Thats the only way.
But i recommend to have a separate collection for comments. That way you can skip & limit the set.