Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set sort order for mongoose document

I have a mongoose schema:

models/profile.js

var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");

var profileSchema = new mongoose.Schema({ 
    username: String,
    complete: { type: Boolean, default: false },
    email: { type: String, default: "" },
    city: { type: String, default: "" }
}, { discriminatorKey: 'accountType' });

profileSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('Profile', profileSchema);

That has two discriminators associated with it:

models/finder.js

var Profile = require('./profile');

var Finder = Profile.discriminator('finder', new mongoose.Schema({
    position: { type: String, default: "" },
    skills: Array
}));

module.exports = mongoose.model("Finder");

models/helper.js

var Profile = require('./profile');

var Helper = Profile.discriminator('helper', new mongoose.Schema({
    jobTitle: { type: String, default: "" },
    lastPosition: { type: String, default: "" }
}));

module.exports = mongoose.model("Helper");

I am using this within an express framework, and on one page - shown below - I want to iterate over the key/value pairs in Profile to build a table.

I would like to retain the order designated in the Schema, so that the table ordering is consistent between pages.

Is it possible to define a sort order on Schema creation?

Here's my profile.ejs file where I make the table:

<table class="table profile-display">
    <tbody>

    <% for(var key in profile.toObject({versionKey: false})) { %>
        <% if (key != '_id') { %>
            <% if (profile[key].length === 0){ %>
                <tr class="incomplete-warning table-rows">
            <% } else { %>
                <tr class="table-rows">
            <% } %>
                    <td class="key-text"><%=key.toUpperCase()%>:</td>
                    <td><%=profile[key]%></td>
                </tr>    
       <% } %>
    <% } %>

    </tbody>
</table>

Please let me know if I can provide more information

like image 731
fugu Avatar asked Feb 25 '19 18:02

fugu


2 Answers

You can use retainKeyOrder

By default, mongoose reverses key order in documents as a performance optimization. For example, new Model({ first: 1, second: 2 }); would actually be stored in MongoDB as { second: 2, first: 1 }. This behavior is considered deprecated because it has numerous unintended side effects, including making it difficult to manipulate documents whose _id field is an object.

Mongoose >= 4.6.4 has a retainKeyOrder option for schemas that ensures that mongoose will always keep the correct order for your object keys.

var testSchema = new Schema({ first: Number, second: Number }, { retainKeyOrder: true });
var Test = mongoose.model('Test', testSchema);
Test.create({ first: 1, second: 2 }); // Will be stored in mongodb as `{ first: 1, second: 2 }`

References:

https://github.com/Automattic/mongoose/issues/1514 https://mongoosejs.com/docs/4.x/docs/guide.html#retainKeyOrder

like image 98
Thamaraiselvam Avatar answered Sep 30 '22 12:09

Thamaraiselvam


Every browser handles object sorts differently. I suggest returning a JSON object which maps your schema that has a sort value or such and iterates over your profile.ejs template. Then you can just map the value from mongoose output key whichever you like.

{
   1 : 'username',
   2 : 'type',
   3 : 'complete',
   4 : 'email',
   5 : 'city',
   6 : 'position',
   7 : 'skills'
}

or an array

[
    'username',
    'type',
    'complete',
    'email',
    'city',
    'position',
    'skills'
]

Then from that, you can just map your schema from the value of the object or from the array. I like using an array in this case as its easier to iterate over by just using the index key. It depends on your usage and purpose.

Hope it help.

UPDATE: To minimizing in hardcoding the schema twice you can create an object which has sorts with the schema value.

Code Example.

// Construct your object
var objSchema = {
    obj1 : {
        schema : { type: String },
        order : 2
    },
    obj2 : {
        schema : { type: String },
        order : 3
    },
    obj3 : {
        schema : { type: String },
        order : 1
    }
}

// Function to construct your schema object and sort array
function mapSortSchema(objs) {
    var result = {
        schema : {},
        order : []
    }

    for (var obj in objs) {
        result.schema[obj] = objs[obj].schema;
        result.order.push({
            'key': obj,
            'order': objs[obj].order
        });
    }

    result.order.sort(function(a, b) {
        return a.order - b.order;
    });

    return result;
}

Now you have schema for mongoose and order for template.

var mapSchema = mapSortSchema(objSchema);

// You can now use this with your mongoose schema
var forMongooseSchema = mapSchema.schema; 
// result {obj1 : { type: String }, obj2 : { type: String }, obj3 : { type: String }}

// You can now use this to loop through your template
var forTemplateLoop = mapSchema.order;
// result [{key: "obj3", order: 1}, {key: "obj1", order: 2}, {key: "obj2", order: 3}]

Haven't tested this in mongoose but it will give you the basic idea, you can improve the function base on your need. Hope it helps.

like image 43
per.eight Avatar answered Sep 30 '22 12:09

per.eight