Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return range of documents around ID in MongoDB

I have an ID of a document and need to return the document plus the 10 documents that come before and the 10 documents after it. 21 docs total.

I do not have a start or end value from any key. Only the limit in either direction.

Best way to do this? Thank you in advance.

like image 559
Brian Moore Avatar asked Feb 22 '14 00:02

Brian Moore


People also ask

How do I get only certain fields in MongoDB?

You can select a single field in MongoDB using the following syntax: db. yourCollectionName. find({"yourFieldName":yourValue},{"yourSingleFieldName":1,_id:0});

What is $$ in MongoDB?

To access the value of the variable, prefix the variable name with double dollar signs ( $$ ); i.e. "$$<variable>" . If the variable references an object, to access a specific field in the object, use the dot notation; i.e. "$$<variable>.

What does find () do in MongoDB?

In MongoDB, find() method is used to select documents in a collection and return a cursor to the selected documents.

What is $Not in MongoDB?

$not performs a logical NOT operation on the specified <operator-expression> and selects the documents that do not match the <operator-expression> . This includes documents that do not contain the field .


2 Answers

Did you know that ObjectID's contain a timestamp? And that therefore they always represent the natural insertion order. So if you are looking for documents before an after a known document _id you can do this:

Our documents:

{ "_id" : ObjectId("5307f2d80f936e03d1a1d1c8"), "a" : 1 }
{ "_id" : ObjectId("5307f2db0f936e03d1a1d1c9"), "b" : 1 }
{ "_id" : ObjectId("5307f2de0f936e03d1a1d1ca"), "c" : 1 }
{ "_id" : ObjectId("5307f2e20f936e03d1a1d1cb"), "d" : 1 }
{ "_id" : ObjectId("5307f2e50f936e03d1a1d1cc"), "e" : 1 }
{ "_id" : ObjectId("5307f2e90f936e03d1a1d1cd"), "f" : 1 }
{ "_id" : ObjectId("5307f2ec0f936e03d1a1d1ce"), "g" : 1 }
{ "_id" : ObjectId("5307f2ee0f936e03d1a1d1cf"), "h" : 1 }
{ "_id" : ObjectId("5307f2f10f936e03d1a1d1d0"), "i" : 1 }
{ "_id" : ObjectId("5307f2f50f936e03d1a1d1d1"), "j" : 1 }
{ "_id" : ObjectId("5307f3020f936e03d1a1d1d2"), "j" : 1 }

So we know the _id of "f", get it and the next 2 documents:

> db.items.find({ _id: {$gte: ObjectId("5307f2e90f936e03d1a1d1cd") } }).limit(3)

{ "_id" : ObjectId("5307f2e90f936e03d1a1d1cd"), "f" : 1 }
{ "_id" : ObjectId("5307f2ec0f936e03d1a1d1ce"), "g" : 1 }
{ "_id" : ObjectId("5307f2ee0f936e03d1a1d1cf"), "h" : 1 }

And do the same in reverse:

> db.items.find({ _id: {$lte: ObjectId("5307f2e90f936e03d1a1d1cd") } })
    .sort({ _id: -1 }).limit(3)
{ "_id" : ObjectId("5307f2e90f936e03d1a1d1cd"), "f" : 1 }
{ "_id" : ObjectId("5307f2e50f936e03d1a1d1cc"), "e" : 1 }
{ "_id" : ObjectId("5307f2e20f936e03d1a1d1cb"), "d" : 1 }

And that's a much better approach than scanning a collection.

like image 107
Neil Lunn Avatar answered Nov 15 '22 20:11

Neil Lunn


Neil's answer is a good answer to the question as stated (assuming that you are using automatically generated ObjectIds), but keep in mind that there's some subtlety around the concept of the 10 documents before and after a given document.

The complete format for an ObjectId is documented here. Note that it consists of the following fields:

  • timestamp to 1-second resolution,
  • machine identifier
  • process id
  • counter

Generally if you don't specify your own _ids they are automatically generated by the driver on the client machine. So as long as the ObjectIds are generated on a single process on a client single machine, their order does indeed reflect the order in which they were generated, which in a typical application will also be the insertion order (but need not be). However if you have multiple processes or multiple client machines, the order of the ObjectIds for objects generated within a given second by those multiple sources has an unpredictable relationship to the insertion order.

like image 43
Bruce Lucas Avatar answered Nov 15 '22 20:11

Bruce Lucas