MongoDB: Get just single member of array field on document - mongodb

Say I have a Foo document like the following:
{
_id: 1,
bar: [{_id: 1, ...bar props}, {_id: 2, ...bar props}, {_id: 3, ...bar props)}],
... other foo props
}
How do I query the database for a single Bar, such that my result looks like:
{_id: 2, ...bar props}
Something like:
db().collection('foo').findOne({ _id: 1, {foo: _id: 2}}, {foo: 1, _id: 0})

Matching and projection are separate operations in MongoDB and you should also keep them separate when you are thinking (and asking) about queries.
You cannot "query for a single Bar". Queries always match documents. What you can do is find a document which contains a Bar which matches conditions, or you can find a document which contains exactly one Bar which also matches conditions, etc. In all of these cases you still get the top-level document(s) as a result.
To retrieve (only) one, several or all of the Bars in whichever documents matched your query conditions, instead of those documents, use projection (either second argument to find or $project aggregation pipeline stage).
When you are using the aggregation pipeline, you can mix $match and $project stages so that, for example, you $match to filter down documents, then $project to reduce the documents to some of their fields, then $match to further filter down the resulting documents, and so on. Still matching and projection are separate operations.

Related

Find current and previous documents from mongo db

I have to return 2 documents from a single query. The first value which I will be giving in the query and the second will be the previous one(sorted).
I am able to design both separately. The below code gives separate outputs.
db.collection.find({'_id':'value1'})
db.collection.find({'_id': {'$lt': 'value1'}}).sort({'_id':-1}).limit(1)
How to combine them? So when I execute from my appl it returns 2 outputs
Fetch only a specific key instead of entire document
You can use $lte instead of $lt and limit with 2 - logically it will be the same operation
db.collection.find({ _id: { $lte: 'value1' } }, { _id: 1, yourKey: 1 }).sort({_id: -1}).limit(2)
EDIT: to get specific keys you need to specify them as second argument of .find()

MongoDB: Get whole document with aggregate method

I'm trying to reach something like this:
I have collections of activities that belong to some user.
I want to get the activity names distincted ordered by 'added_time', so I used 'group by' on the activity name and get the max value of 'added_time'.
Also, I want to sort them by 'added_time', and then to get the whole document.
The only thing that I reached so far, is to get only the name that I grouped by, and the 'added_time' property.
This is the query:
db.getCollection('user_activities').aggregate
(
{$match: {'type': 'food', 'user_id': '123'}},
{$group:{'_id':'$name', 'added_time':{$max:'$added_time'}}},
{$sort:{'added_time':-1}},
{$project: {_id: 0,name: "$_id",count: 1,sum: 1, 'added_time': 1}}
)
Can someone help me with reaching the whole document?
Thank's!

Using $match after a $lookup in MongoDB

I have two collections and I want to get fields from both, so I'm using $lookup in an aggregation pipeline.
This works fine and returns all the documents with an extra field, an array with 0 or 1 elements (an object). If 0 elements, it means that the JOIN(in SQL world) didn't return anything. If 1 element, it means that there was a match and the element in an object with the fields of the second collection.
Now that I have those results, I'd like to use $match in order to filter some of the results.
In order to use $match I first want to use $unwind on that new extra field in order to extract the array. The problem is once I insert the $unwind stage, the result of the query is a single document.
Why is this happening? How can I $unwind and then $match all the documents I got from the $lookup stage?
assume we have documents after lookup:
{doc:{_id:1, lookupArray:[{doc:1},{doc:2}]}}
and
{doc:{_id:2, lookupArray:[/* empty */]}}
when we $unwind without any options we will get:
{doc:{_id:1, lookupArray:{doc:1}}}
{doc:{_id:1, lookupArray:{doc:2}}}
null
and when we specify
{ $unwind: { path: "$array", preserveNullAndEmptyArrays: true } }
then we will get:
{doc:{_id:1, lookupArray:{doc:1}}}
{doc:{_id:1, lookupArray:{doc:2}}}
{doc:{_id:2, lookupArray:[/* empty */]}}
So when you want to perform a search for a value doc from lookupArray, $match will look like this:
{$match:{'lookupArray.doc':2}}
Any comments welcome!

MongoDB - Pagination based on non-unique fields

I am familiar with the best practice of range based pagination on large MongoDB collections, however I am struggling with figuring out how to paginate a collection where the sort value is on a non-unique field.
For example, I have a large collection of users, and there is a field for the number of times they have done something. This field is defintely non-unique, and could have large groups of documents that have the same value.
I would like to return results sorted by that 'numTimesDoneSomething' field.
Here is a sample data set:
{_id: ObjectId("50c480d81ff137e805000003"), numTimesDoneSomething: 12}
{_id: ObjectId("50c480d81ff137e805000005"), numTimesDoneSomething: 9}
{_id: ObjectId("50c480d81ff137e805000006"), numTimesDoneSomething: 7}
{_id: ObjectId("50c480d81ff137e805000007"), numTimesDoneSomething: 1}
{_id: ObjectId("50c480d81ff137e805000002"), numTimesDoneSomething: 15}
{_id: ObjectId("50c480d81ff137e805000008"), numTimesDoneSomething: 1}
{_id: ObjectId("50c480d81ff137e805000009"), numTimesDoneSomething: 1}
{_id: ObjectId("50c480d81ff137e805000004"), numTimesDoneSomething: 12}
{_id: ObjectId("50c480d81ff137e805000010"), numTimesDoneSomething: 1}
{_id: ObjectId("50c480d81ff137e805000011"), numTimesDoneSomething: 1}
How would I return this data set sorted by 'numTimesDoneSomething' with 2 records per page?
#cubbuk shows a good example using offset (skip) but you can also mould the query he shows for ranged pagination as well:
db.collection.find().sort({numTimesDoneSomething:-1, _id:1})
Since the _id here will be unique and you are seconding on it you can actually then range by _id and the results, even between two records having numTimesDoneSomething of 12, should be consistent as to whether they should be on one page or the next.
So doing something as simple as
var q = db.collection.find({_id: {$gt: last_id}}).sort({numTimesDoneSomething:-1, _id:1}).limit(2)
Should work quite good for ranged pagination.
You can sort on multiple fields in this case sort on numTimesDoneSomething and id field. Since id_ field is ascending in itself already according to the insertion timestamp, you will able to paginate through the collection without iterating over duplicate data unless new data is inserted during the iteration.
db.collection.find().sort({numTimesDoneSomething:-1, _id:1}).offset(index).limit(2)

How do I do a "NOT IN" query in Mongo?

This is my document:
{
title:"Happy thanksgiving",
body: "come over for dinner",
blocked:[
{user:333, name:'john'},
{user:994, name:'jessica'},
{user:11, name: 'matt'},
]
}
What is the query to find all documents that do not have user 11 in "blocked"?
You can use $in or $nin for "not in"
Example ...
> db.people.find({ crowd : { $nin: ["cool"] }});
I put a bunch more examples here: http://learnmongo.com/posts/being-part-of-the-in-crowd/
Since you are comparing against a single value, your example actually doesn't need a NOT IN operation. This is because Mongo will apply its search criteria to every element of an array subdocument. You can use the NOT EQUALS operator, $ne, to get what you want as it takes the value that cannot turn up in the search:
db.myCollection.find({'blocked.user': {$ne: 11}});
However if you have many things that it cannot equal, that is when you would use the NOT IN operator, which is $nin. It takes an array of values that cannot turn up in the search:
db.myCollection.find({'blocked.user': {$nin: [11, 12, 13]}});
Try the following:
db.stack.find({"blocked.user":{$nin:[11]}})
This worked for me.
See http://docs.mongodb.org/manual/reference/operator/query/nin/#op._S_nin
db.inventory.find( { qty: { $nin: [ 5, 15 ] } } )
This query will
select all documents in the inventory collection where the qty field
value does not equal 5 nor 15. The selected documents will include
those documents that do not contain the qty field.
If the field holds an array, then the $nin operator selects the
documents whose field holds an array with no element equal to a value
in the specified array (e.g. , , etc.).