0

Is there a way to specify the order of keys to sort by?

There seems to be some confusion in the comments. To be clear, I'm not asking about ascending or descending order. I'm asking if I can specify sorting priority.

For example, I want to sort by:

  • pinned (Boolean)
  • score (Number)
  • newest (Date or ObjectId)

so that

  • only docs with the same pinned value get sorted by score, and
  • only docs with the same pinned and score get sorted by newest?

In other words,

  • score, newest should never be considered if pinned value is different (just sort by pinned)
  • newest should never be considered if score is different.

For example,

{ pinned: false, score:1, _id: new }
{ pinned: true, score:1, _id: oldest }
{ pinned: false, score:2, _id: old }

should be ordered as

{ pinned: true,  score: 1, _id: oldest }
{ pinned: false, score: 1, _id: new }
{ pinned: false, score: 2, _id: old }
Neil Lunn
  • 130,590
  • 33
  • 275
  • 280
az_
  • 1,423
  • 1
  • 14
  • 22
  • IMHO, it would be easier for people to reply if you put some sample data... – Askar Apr 25 '14 at 02:41
  • I didn't think the question was that complex, but I've added an example. – az_ Apr 25 '14 at 02:45
  • Did you check the documentation? http://docs.mongodb.org/manual/reference/method/cursor.sort/#cursor.sort – JohnnyHK Apr 25 '14 at 02:49
  • I have, but I can't seem to find anything that answers my question. Is there anything specific you could point me to in the documentation? – az_ Apr 25 '14 at 02:52
  • does the sort() applies? db.col.find({...}).sort({field1: 1, field2: 1, ...}) – yaoxing Apr 25 '14 at 02:54
  • I've added content to the question. Hopefully it clears things up. Thanks :) – az_ Apr 25 '14 at 03:02
  • I think what you're asking for is just standard multi-field sorting as in @yaoxing's comment. Is there something you tried that didn't work? – JohnnyHK Apr 25 '14 at 03:05
  • It seems to me according to your update, the sort() still applies. db.col.find({...}).sort({pinned: 1, score: 1, _id: 1}); What you are asking IS how sort() works. – yaoxing Apr 25 '14 at 03:11
  • I've tried it, and it doesn't work. In javascript, an Object by definition is "an unordered collection of properties". `{pinned: 1, score: 1, _id: 1} === {_id: 1, pinned: 1, score: 1}`. In my case, I have it as `{pinned: -1, score: -1, _id: -1}` but it continues sorting by pinned -> _id -> score rather than what I'd want (pinned -> score -> _id) – az_ Apr 25 '14 at 04:00
  • How did you write the query? – yaoxing Apr 25 '14 at 04:02
  • Exactly as you have it. – az_ Apr 25 '14 at 04:08
  • 1
    are you using the mongo shell for your testing? In mongo order *is* significant in objects in several places. Sort clause is one of them. Index specification is another. – Asya Kamsky Apr 25 '14 at 04:21
  • 1
    @AsyaKamsky can you make that an answer? It's the only response that gets to the root of the solution (that mongodb doesn't follow Ecmascript) – az_ Apr 25 '14 at 04:34
  • 1
    According to JSON spec yes it should be an unordered collection. However, here the order does matter. – yaoxing Apr 25 '14 at 04:35

2 Answers2

2

While JSON documents are unordered, in MongoDB there are several places where order matters. The most important ones to remember are:

  • sort order
  • index specification order

When you specify a sort, the sort order will follow the order of fields, so in your case it would be .sort({pinned:1,score:1,newest:1}) you can see an example in the documentation.

Some operations expect fields in particular order.

It also matters for subdocuments if you are trying to match them (whereas top level field order in the query does not matter).

Asya Kamsky
  • 39,247
  • 5
  • 96
  • 124
  • How can MongoDB distinguish between the identical objects `{score:1,newest:1}` and `{newest:1,score:1}` – Dmitri Zaitsev Jul 04 '15 at 16:18
  • They are not identical in their bson representation at all. Look at them as strings, for example - not at all identical and have a clearly defined sort order. – Asya Kamsky Jul 04 '15 at 17:25
  • Their string (or `bson`) representations are not identical but as **JavaScript objects** - they are indistinguishable, as the key order is not guaranteed. So if I do `.sort(obj)` with JS object `obj` inside, I don't see how to reliably declare the order between the fields. – Dmitri Zaitsev Jul 05 '15 at 02:32
  • BTW I find Mongo's description of Bson vs Json very confusing and obscure: https://www.mongodb.com/json-and-bson is saying "MongoDB represents JSON documents in binary-encoded format called BSON behind the scenes". So is Bson only used *behind the scene*? – Dmitri Zaitsev Jul 05 '15 at 03:39
1

So I inserted some sample data as is in your question with mongo shell:

db.test.insert({ pinned: false, score:1, _id: "new" });
db.test.insert({ pinned: true, score:1, _id: "oldest" });
db.test.insert({ pinned: false, score:2, _id: "old" });

Try to sort it by:

db.test.find().sort({pinned: -1, score: 1, _id: 1})

It turns out the result is:

{ "_id" : "oldest", "pinned" : true, "score" : 1 }
{ "_id" : "new", "pinned" : false, "score" : 1 }
{ "_id" : "old", "pinned" : false, "score" : 2 }

Isn't that what you want?

EDIT: Keep in mind that JavaScript doesn't strictly obey JSON spec. e.g. JSON spec also says property names should quoted with ", JavaScript however you don't have to quote, or you can quote with '.

yaoxing
  • 3,533
  • 2
  • 17
  • 29
  • Thanks for your responses above and here! It turns out that I had a typo in my sort query - and so it was ignoring both score and _id. I really appreciate your help, but next time, it'd be a lot more helpful if you explained the "why" along with the "what". edit: and I see you added it right before I posted this comment. :+1: – az_ Apr 25 '14 at 04:37
  • I'd like to explain why over what. I just didn't know which part confused you. – yaoxing Apr 25 '14 at 04:41
  • 1
    Actually, it's specifically stated as _unordered_ in [both JSON and ECMAScript](http://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order). I wonder why the MongoDB devs decided to ignore it. – az_ Apr 25 '14 at 04:49
  • 1
    It's not mongodb doesn't want to obey the spec, it's JavaScript doesn't. And mongo shell is based on JavaScript V8 engine. I added another example in my answer. I though people using JavaScript know about this trick, and didn't know you stuck there. – yaoxing Apr 25 '14 at 04:52