11

If this is my collection structure:

{ _id: ObjectId("4fdbaf608b446b0477000142"), name: "product 1" }
{ _id: ObjectId("4fdbaf608b446b0477000143"), name: "product 2" }
{ _id: ObjectId("4fdbaf608b446b0477000144"), name: "product 3" }

and I query product 1, is there a way to query the next document, which in this case would be "product 2"?

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
Claremont
  • 325
  • 2
  • 5
  • 12
  • 1
    So you want to query for the first document after "product 1"? How are you defining the documents' order (what's considered 'after')? – JohnnyHK Aug 17 '12 at 20:08
  • I have not reordered them at all, they are in the original insertion order. The way I got product 1 is someone goes to testsite.com/4fdbaf608b446b0477000142 I take the number and query by id. – Claremont Aug 17 '12 at 20:35

3 Answers3

24

It is best to add explicit sort() criteria if you want a predictable order of results.

Assuming the order you are after is "insertion order" and you are using MongoDB's default generated ObjectIds, then you can query based on the ObjectId:

// Find next product created
db.products.find({_id: {$gt: ObjectId("4fdbaf608b446b0477000142") }}).limit(1)

Note that this example only works because:

  • the first four bytes of the ObjectId are calculated from a unix-style timestamp (see: ObjectId Specification)
  • a query on _id alone will use the default _id index (sorted by id) to find a match

So really, this implicit sort is the same as:

db.products.find({_id: {$gt: ObjectId("4fdbaf608b446b0477000142" )}}).sort({_id:1}).limit(1);

If you added more criteria to the query to qualify how to find the "next" product (for example, a category), the query could use a different index and the order may not be as you expect.

You can check index usage with explain().

Stennie
  • 63,885
  • 14
  • 149
  • 175
1

Starting in Mongo 5, it's a perfect use case for the new $setWindowFields aggregation operator:

// { _id: 1, name: "product 1" }
// { _id: 2, name: "product 2" }
// { _id: 3, name: "product 3" }
db.collection.aggregate([
  { $setWindowFields: {
    sortBy: { _id: 1 },
    output: { next: { $push: "$$ROOT", window: { documents: [1, 1] } } }
  }}
])
// { _id: 1, name: "product 1", next: [{ _id: 2, name: "product 2" }] }
// { _id: 2, name: "product 2", next: [{ _id: 3, name: "product 3" }] }
// { _id: 3, name: "product 3", next: [ ] }

This:

  • $sorts documents by their order of insertion using their _ids (ObjectIds contain the timestamp of insertion): sortBy: { _id: 1 }
  • adds the next field in each document (output: { running: { ... }})
    • by $pushing the whole document $$ROOT ($push: "$$ROOT")
    • on a specified span of documents (the window) which is in this case is a range of only the next document document: window: { documents: [1, 1] }.
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
0

You can get the items back in insertion order using ObjectId. See: http://www.mongodb.org/display/DOCS/Optimizing+Object+IDs#OptimizingObjectIDs

If you want to get them back in some other order then you will have to define what that order is and store more information in your documents.

InPursuit
  • 3,245
  • 24
  • 20