简体   繁体   中英

Sorting by virtual field in mongoDB (mongoose)

Let's say I have some Schema which has a virtual field like this

var schema = new mongoose.Schema(
{
    name: { type: String }
},
{
    toObject: { virtuals: true },
    toJSON: { virtuals: true }
});

schema.virtual("name_length").get(function(){
    return this.name.length;
});

In a query is it possible to sort the results by the virtual field? Something like

schema.find().sort("name_length").limit(5).exec(function(docs){ ... });

When I try this, the results are simple not sorted...

You won't be able to sort by a virtual field because they are not stored to the database.

Virtual attributes are attributes that are convenient to have around but that do not get persisted to mongodb.

http://mongoosejs.com/docs/2.7.x/docs/virtuals.html

I don't know about any ways to reuse existing virtuals in aggregations but you might be able to project virtuals to your documents in aggregation stages.

I abstracted an aggregation pipeline I recently build. Imagine you have a collection of keys with an array of players directly stored in the team document. The following pipeline would sort those teams by the ranking of the favoredPlayer where the favoredPlayer is basically a virtual property containing the most relevant player of the team under certain circumstances (in the example we only want to consider offense and defense players). This circumstances in our case have a variety of possibilities and are depending on the users choices and can therefore not be persisted on the document.

Our "team" document is pretty large and since projected documents are only available in memory it would come with a huge performance cost if we just add a field and have the whole documents of the teams collection in memory. Therefore we project only those fields we need for sorting and restore the original document after limiting the results.

[
  // find all teams from germany
  { '$match': { country: 'de' } },
  // project to documents containing sort relevant fields
  // and add field favoredPlayer per team
  { '$project': {
    rank: 1,
    'favoredPlayer': {
      '$arrayElemAt': [
        {
          // remove players that should be ignored
          $filter: {
            input: '$players',
            as: 'p',
            cond: { $in: ['$$p.position', ['offense', 'defense']] },
          },
        },
        // take first of the filtered players since players are already sorted by relevance in our db
        0,
      ],
    },
  }},
  // sort teams by the ranking of the favoredPlayer
  { '$sort': { 'favoredPlayer.ranking': -1, rank: -1 } },
  { '$limit': 10 },
  // $lookup $unwind $replaceRoot to restore original database document
  { '$lookup': { from: 'teams', localField: '_id', foreignField: '_id', as: 'subdoc' } },
  { '$unwind': { path: '$subdoc' } },
  { '$replaceRoot': { newRoot: '$subdoc' } },
];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM