0

I have a mongodb document like this:

{
"_id" : 1,
"prdvalue" : 3600,
"[4,0:30,0]" : [
    {
        "h" : 21.0,
        "l" : 30.0
    }
],
"[10,0:24,55]" : [ 
    {
        "h" : 10.0,
        "l" : 24.55
    }, 
    {
        "h" : 10.0,
        "l" : 24.55
    }, 
    {
        "h" : 10.0,
        "l" : 24.55
    }
],
"[16,0:18,0]" : [ 
    {
        "h" : 16.0,
        "l" : 18.0
    },
    {
        "h" : 16.0,
        "l" : 18.0
    }
]}

Now, I want to sort the arrays inside it according to their length in descending order such that when I query this document I must get the result as shown below:

 {
"_id" : 1,
"prdvalue" : 3600,
"[10,0:24,55]" : [ 
    {
        "h" : 10.0,
        "l" : 24.55
    }, 
    {
        "h" : 10.0,
        "l" : 24.55
    }, 
    {
        "h" : 10.0,
        "l" : 24.55
    }
],
"[16,0:18,0]" : [ 
    {
        "h" : 16.0,
        "l" : 18.0
    },
    {
        "h" : 16.0,
        "l" : 18.0
    }
],
"[4,0:30,0]" : [
    {
        "h" : 21.0,
        "l" : 30.0
    }
]}

Here in my question unlike previous questions, array names are different so I am not understanding how to fire the query.

halfer
  • 19,824
  • 17
  • 99
  • 186
jarvis
  • 55
  • 10
  • Possible duplicate of [Sort by array length](https://stackoverflow.com/questions/23953544/sort-by-array-length) – mstorkson May 23 '17 at 17:57
  • I saw the link previously but in my question the names of arrays are variable. So can u plz help me with an exact query. – jarvis May 23 '17 at 18:37
  • Why you create such complicated structure, if possible then re-structure your documents. – Neo-coder May 23 '17 at 19:13
  • @jarvis Did anyone solve your problem? If so, could you please accept the best answer (click the checkmark under the points). That will help other users that come across your question quickly spot the accepted answer and it also gives 15 rep. points to the author (: – Danziger Jan 31 '18 at 06:00

1 Answers1

0

According to the JSON spec:

An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).

However, in the release notes for MongoDB 2.6 it says:

MongoDB preserves the order of the document fields following write operations except for the following cases:

  • The _id field is always the first field in the document.
  • Updates that include renaming of field names may result in the reordering of fields in the document.

That was not true for previous versions, as the MMAPv1 storage engine allocates space for document using a padding factor, and if an update on a document push it beyond that, the updated fields will jump to the end of the document. For more on that see: MongoDB field order and document position change after update

Then, you can achieve what you want using mapReduce to do that:

db.sourceCollection.mapReduce(function () {
    var obj = this;
    var newObject = {};

    Object
        .keys(this)
        .sort(function(a, b) {
            return a.length - b.length;
        })
        .map(function(key) {
            newObject[key] = obj[key];
        });

    emit(obj._id, newObject);
}, function(key, values) {
    return values[0];
}, {
    out: { inline: 1 }
}).results.map(function (doc) {
    db.destinationCollection.insert(doc.value);
});

Keep in mind that this will only sort the fields in the root of the document, but not the ones in subdocuments. Moreover, this is working due to to an implementation detail, but Object's properties order is not guaranteed according to the ECMAScript spec.

You could use Map instead, which does keep the insertion order:

A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.

However, I think that's not currently supported in the MongoDB shell.

Probably you should think if you are really using the appropriate schema for your data, as it looks like an array will work better.

Community
  • 1
  • 1
Danziger
  • 19,628
  • 4
  • 53
  • 83