range operator not returning expected results via mongodb database - mongodb

I've now tested this via the shell, Studio 3T IDE and within my API itself.
The first query looks something like this:
Notification.find({
userId: ObjectId("...")
}).limit(20).sort('-createdAt')
which returns the following documents sorted by their createdAt timestamp (which is also imbedded in the _id field):
I then run the next query which I would expect to return the results starting at _id: ObjectId("615869eac849ec00205aa112"):
Notification.find({
userId: ObjectId("..."),
_id: { $lt: ObjectId("615869eac849ec00205aa115"}
}).limit(20).sort('-createdAt')
I would expect this command to get me my next 20 results sorted in the same descending order as the original query above. However, I get the following:
which has 3 results from the original query. The _id field is clearly unique between the _id I use as a cursor and the incorrectly returned results but after inspection the createdAt timestamp is the exact same as the createdAt timestamp of the document _id I use for the range query.

The problem is you are querying on an unsorted field expecting that value to identify a specific point the result set.
Note that in the first result set entries 17 through 29 all have the same epoch timestamp in the _id value, and that those 13 entries are not in any particular order.
As luck would have it, entry 20 has the greatest _id of that group, so all 12 of the others are lesser, even the ones that happened to come before.
To make this work, also sort on _id like:
.sort({createdAt: -1, _id: -1})

Related

Best way to get the first inserted record in a collection of MongoDB

I need to fetch the first inserted record in a collection in MongoDB for which I am currently using the below query:
db.users.find({}).sort({"created_at":1}).limit(1);
But, this takes up a lot of memory. The collection has about 100K records.
What is the efficient way to do this?
MongoDB _id is unique identifier which is automatically generated upon insertion of document into MongoDB collection
_id field stores ObjectId value and is automatically indexed.
According to MongoDB documentation,
The 12-byte ObjectId value consists of:
4-byte value representing the seconds since the Unix epoch,
3-byte machine identifier,
2-byte process id, and
3-byte counter, starting with a random value.
According to description as mentioned into above question to fetch first inserted record please try executing following mongodb find operation into MongoDB shell.
db.users.find({}).sort({"_id":1}).limit(1);
In above query we have sorted result according to _id field since _id Object ID value consists of unix epoch timestamp
further to this you can add specific filters in query to get first record inserted for that criteria:
like suppose you collection contains data for storing employees from IT, ADMIN, FINANCE department and you want to look for the first document inserted for IT (i.e. first IT employee) then you can execute:
db.users.find({"Dept" : "IT"}).sort({"_id":1}).limit(1);
and similarly to find last employee:
db.users.find({"Dept" : "IT"}).sort({"_id":-1}).limit(1);
Note: for bigger collections/sharded collection it will take considerable time to get the result as it iterates entire _id field for ascending and descending criteria.

I got a mongodb collection which items has create_time and delete_time field.I want a Aggregation

I want a Aggregation.(or does aggregation can solve my question?)
for example:
There is a timestamp, if one item's create_time less than the timestamp and the delete_time large than the timestamp, it count 1, loop all items, i can get the count in the timestamp, a series of count is what i needed.
This process too slow in my app.
Can mongodb aggregation help me? thanks
If i understand correctly, this is the query
db.collection.aggregate([{ $project:{cmp:{$and: [{$lt:["$create_time",ISODate("2016-04-24T13:10:09.518Z")]}, {$gt:["$delete_time",ISODate("2016-04-25T13:10:09.518Z")]}] } } },{$match:{cmp:true}},{$group:{_id:"$cmp",count:{$sum:1}}}])
This one will basically gives output as { "_id" : true, "count" : 2 }.
Where count is number of documents satisfying the condition you specified. Replace ISODate("xxxx") with your ISO Date.
And if you also want number of documents that don't satisfy your query remove the match object in the pipeline.

Why does db.collection.find({}).maxTimeMS(100) return everything, but db.collection.find({}, {$maxTimeMS: 100}) only returns object IDs?

I'm using MongoDB 2.6.8. According to the $maxTimeMS reference, these two queries should behave identically:
> db.collection.find({}).maxTimeMS(100)
> db.collection.find({}, {$maxTimeMS: 100})
The first query does exactly what I want, but the second query restricts only returns the object IDs of the documents. I tried increasing $maxTimeMS to 100000000 and there was no change in behavior.
Why am I getting different results for these two commands?
You found a bug in the documentation.
The reason that db.collection.find({}, {$maxTimeMS: 100}) returns only the _id of each object is because mongoDB is interpreting the {$maxTimeMS: 100} portion of the query as a projection.
So it thinks you want to see all the documents and you want to see the fields _id and the field $maxTimeMS. Of course, none of your documents have a $maxTimeMS field, so they only show the _id.
The proper way to perform the query you want without the shortcut is:
db.collection.find({ $query: {}, $maxTimeMS: 100 })

Mongo: returning creation date in a record set

I'm querying mongodb using mongo shell. I'm looking at the most recent 10 orders like this:
# Gets most recent orders:
db.orders.find().sort( {'_id': -1} ).limit(10)
I'd like to include an additional column in the output that is the creation date in human readable form. I want to use getTimestamp() on the ObjectId.
Can i do this in a single query using a calculated field?
You can't do it in a single query. The results will only contain the actual fields in the document. You can post-process it in the shell like this though;
var results = db.orders.find().sort( {'_id': -1} ).limit(10);
results.forEach(function(result){
result.timestamp = result._id.getTimestamp();
});
printjson(results);

Mongodb update limited number of documents

I have a collection with 100 million documents. I want to safely update a number of the documents (by safely I mean update a document only if it hasn't already been updated). Is there an efficient way to do it in Mongo?
I was planning to use the $isolated operator with a limit clause but it appears mongo doesn't support limiting on updates.
This seems simple but I'm stuck. Any help would be appreciated.
Per Sammaye, it doesn't look like there is a "proper" way to do this.
My workaround was to create a sequence as outlined on the mongo site and simply add a 'seq' field to every record in my collection. Now I have a unique field which is reliably sortable to update on.
Reliably sortable is important here. I was going to just sort on the auto-generated _id but I quickly realized that natural order is NOT the same as ascending order for ObjectId's (from this page it looks like the string value takes precedence over the object value which matches the behavior I observed in testing). Also, it is entirely possible for a record to be relocated on disk which makes the natural order unreliable for sorting.
So now I can query for the record with the smallest 'seq' which has NOT already been updated to get an inclusive starting point. Next I query for records with 'seq' greater than my starting point and skip (it is important to skip since the 'seq' may be sparse if you remove documents, etc...) the number of records I want to update. Put a limit of 1 on that query and you've got a non-inclusive endpoint. Now I can issue an update with a query of 'updated' = 0, 'seq' >= my starting point and < my endpoint. Assuming no other thread has beat me to the punch the update should give me what I want.
Here are the steps again:
create an auto-increment sequence using findAndModify
add a field to your collection which uses the auto-increment sequence
query to find a suitable starting point: db.xx.find({ updated: 0 }).sort({ seq: 1 }).limit(1)
query to find a suitable endpoint: db.xx.find({ seq: { $gt: startSeq }}).sort({ seq: 1 }).skip(updateCount).limit(1)
update the collection using the starting and ending points: db.xx.update({ updated: 0, seq: { $gte: startSeq }, seq: { $lt: endSeq }, $isolated: 1}, { updated: 1 },{ multi: true })
Pretty painful but it gets the job done.