holyredbeard
holyredbeard

Reputation: 21278

Sorting on meta in Mongoose model

I'm working on an application where you upload images and the uploaded images are displayed side by side in thumbnails. I want to implement filter possibilities, so that one can show images in a different order, like for example the image with most points first in descending order.

I'm having troubles to come up with a good solution for how this should be done, and the only thing I can think of is creating a new route for this (the same as the one I'm using now, below), but different sorting.

This is however DRY coding and not a good thing especially because I have plans for even more sorting options.

I have two questions regarding this:

  1. Is there a better way of accomplish this (a solution so that I don't need to call a route, but instead do it on the client side? Or maybe passing a parameter to the route and with an if statement decide how to sort)?
  2. If the sorting should be done on the server side, how can I do this with points? The amount of points is calculated (upvotes - downvotes), and not stored directly in the Schema.

Model for Images:

var ImageSchema = new Schema({
    name : String,
    size : Number,
    title   : String,
    body : String,
    buf : Buffer,
    date: { type: Date, default: Date.now },
    comments : [CommentSchema],
    meta : {
        upvotes : Number,
        downvotes : Number,
        favs : Number,
        uniqueIPs : [String],
        tags : [String]
    }
});

Route for rendering images:

app.get('/images/:num?', function(req, res){

    var query = model.ImagePost.find({});

    query.where('date').lte(new Date());
    query.desc('date');

    query.exec(function (err, images) {
        if (err) {
            console.log(err);
            // do something
        }

        var query = tagModel.Tag.find({});

        query.exec(function (err, tags) {

            if (err) {
                console.log(err);
                // do something
            }

            query = albumModel.Album.find({});

            query.exec(function (err, albums) {

                if (err) {
                    console.log(err);
                    // do something
                }

                res.render('blogs/index', { title: 'Images', imageList: images, tagList: tags, albumList: albums, dateFormatter: dateFormatter });
            });
        });
    });
});

Jade file (partial) that's rendering out the images to the client:

#image
    div.post
        input(type='hidden', value=image.name + ',' + image.body + ',' + image.date);
        button.imageValue(value= image.name, name= image._id)
        div.albumImgChooseCover +
        img.images(src='../images/' + image.name)
        div.postDesc
            a#single_1(href='../images/' + image.name, title= image.body + ' (Published: '+ formatter.format(image.date) +')')
                img.zoom(src='../img/zoom.png')
            a.various#box1(data-fancybox-type='iframe', href='/image/' + image._id.toHexString())
                img.comments(src='../img/comments.png')
                p.nrOfComments= image.comments.length
            h2.subtitle= image.title
            p.postDescContent= 'Points' + ': ' + (image.meta.upvotes - blog.meta.downvotes)
            form.deleteButtonBox(action='/image/delete/' + image._id.toHexString(), method='POST')
                input(class='deleteButton', type='submit', value='x')
            a.various#box2(data-fancybox-type='iframe', href='/image/edit/' + blog._id.toHexString()) Edit
            - if (image.meta.tags)
                ul.tags
                    h3  Tags:
                        each tag in tagList
                            - if (image.meta.tags.indexOf(tag._id) != -1)
                                a(href='/tag/' + tag._id.toHexString())
                                    span.tagInImage= tag.name

Upvotes: 1

Views: 880

Answers (1)

JohnnyHK
JohnnyHK

Reputation: 312075

Adding a sort=<value> parameter to your existing route URL is the typical way to handle this.

As far as sorting by points, now that Mongo 2.2 is available you can use the aggregation framework for that:

ImageSchema.aggregate([
    {$project: {
        name: 1,
        size: 1,
        // ... more fields you want included
        points: {$subtract: {['$meta.upvotes', '$meta.downvotes']}}
    }},
    {$sort: {points: -1}}
], function(err, results) {
    console.log(results);
});

Upvotes: 1

Related Questions