fugu
fugu

Reputation: 6568

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

Upvotes: 7

Views: 6948

Answers (2)

per.eight
per.eight

Reputation: 436

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.

Upvotes: 0

Thamaraiselvam
Thamaraiselvam

Reputation: 7080

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

Upvotes: 2

Related Questions