MongoDB / Mongoose权重logging非空字段

我有一个MongoDB文档集合。 我已经为特定字段分配了权重,但是我需要使用任何非空名称来logging顶部的logging。 我不想按名称sorting,我只是喜欢有名字的logging出现在没有名字的logging之前。

示例模式:

new Schema({ slug: { type: String, index: {unique: true, dropDups: true} }, name: String, body: { type: String, required: true } }); 

示例索引:

 MySchema.index({ name:'text', body:'text' }, { name: 'best_match_index', weights: { name: 10, body: 1 } }); 

find查询:

 MyModel.find( criteria, { score : { $meta: 'textScore' } }) .sort({ score : { $meta : 'textScore' } }) .skip(offset) .limit(per_page) 

如果我在这里理解你的意思,你所说的是这样的文件:

 { "name" : "term", "body" : "unrelated" } { "name" : "unrelated", "body" : "unrelated" } { "body" : "term" } { "body" : "term term" } { "name" : "unrelated", "body" : "term" } 

对“term”的正常search会产生如下结果:

 { "name" : "term", "body" : "unrelated", "score" : 11 } { "body" : "term term", "score" : 1.5 } { "body" : "term", "score" : 1.1 } { "name" : "unrelated", "body" : "term", "score" : 1.1 } 

但是,你想要的是作为第二个条目获得最后一个条目。

为此,您需要另一个字段的“dynamic”投影来“权重”,在这个位置上您将使用聚合框架:

 MyModel.aggregate([ { "$match": { "$text": { "$search": "term" } }}, { "$project": { "slug": 1, "name": 1, "body": 1, "textScore": { "$meta": "textScore" }, "nameScore": { "$cond": [ { "$ne": [{ "$ifNull": [ "$name", "" ] }, ""] }, 1, 0 ] } }}, { "$sort": { "nameScore": -1, "textScore": -1 } }, { "$skip": offset }, { "$limit": per_page } ],function(err,results) { if (err) throw err; console.log( results ); }) 

哪些地方的项目与“名称”字段上面那些没有:

 { "name" : "term", "body" : "unrelelated", "textScore" : 11, "nameScore" : 1 } { "name" : "unrelated", "body" : "term", "textScore" : 1.1, "nameScore" : 1 } { "body" : "term term", "textScore" : 1.5, "nameScore" : 0 } { "body" : "term", "textScore" : 1.1, "nameScore" : 0 } 

本质上, $cond ternary中的$ifNull操作符testing是否存在“name”字段,然后返回1(如果存在)或0(如果不存在)。

这是传递到$sortpipe道,您的sorting是在“nameScore”首先将这些项目浮动到顶部,然后是“textScore”。

聚合pipe道有它自己的$skip$limit实现用于分页。

这与.find()实现中的操作基本相同,包括“match”,“project”,“sort”,“skip”和“limit”。 所以在这个过程中真的没有什么区别,只是对结果有更多的控制。

“跳过”和“限制”的使用并不是真正的最高性能解决scheme,但是有时候你会被困住,比如在需要提供“页面编号”的情况下。 但是如果你能摆脱它,只需要向前走,那么你可以尝试跟踪最后看到的“textScore”和“seen_ids”的列表到一定程度的粒度,这取决于你的“textScore”值是如何分布的是。 这些可以作为通过结果“跳过”的替代方法传入:

 MyModel.aggregate([ { "$match": { "$text": { "$search": "term" } }}, { "$project": { "slug": 1, "name": 1, "body": 1, "textScore": { "$meta": "textScore" }, "nameScore": { "$cond": [ { "$ne": [{ "$ifNull": [ "$name", "" ] }, ""] }, 1, 0 ] } }}, { "$match": { "_id": { "$nin": seen_ids } "textScore": { "$gte": last_score }, }}, { "$sort": { "nameScore": -1, "textScore": -1 } }, { "$limit": page_size } ]) 

这里唯一不幸的是,textScore的$meta还不能暴露给最初的$match操作,这将有助于缩小结果,而无需首先运行$project

所以你真的不能做像专门的$geoNear运算符一样的完全优化,但是文本版本或者允许前面的语句是好的。


您可能会注意到,从.aggregate()选项返回的对象只是原始的JavaScript对象,而不是从像.find()这样的操作返回的Mongoose“文档”对象。 这是“通过devise”,这里的主要原因是由于聚合框架允许您“操作”生成的文档,因此这里不能保证这些文档实际上与您在最初查询的模式中所具有的相同。

由于您并不是真正“改变”或“重新塑造”文档的目标,现在只需要回头看看代码,在后台自动完成所有的mongoose,并将每个原始结果“铸造”成标准的“types”。

这个清单通常应该告诉你你需要做什么:

 var async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.connect("mongodb://localhost/test"); var testSchema = new Schema({ name: String, body: { type: String, required: true }, textScore: Number, nameScore: Number },{ toObject: { virtuals: true }, toJSON: { virtuals: true } }); testSchema.virtual('favourite').get(function() { return "Fred"; }); var Test = mongoose.model( "Test", testSchema, "textscore" ); Test.aggregate([ { "$match": { "$text": { "$search": "term" } }}, { "$project": { "name": 1, "body": 1, "textScore": { "$meta": "textScore" }, "nameScore": { "$cond": [ { "$ne": [{ "$ifNull": [ "$name", "" ] }, "" ] }, 1, 0 ] } }}, { "$sort": { "nameScore": -1, "textScore": -1 } }, ],function(err,result) { if (err) throw err; result = result.map(function(doc) { return new Test( doc ); }); console.log( JSON.stringify( result, undefined, 4 )); process.exit(); }); 

其中包括输出中的“虚拟”字段:

 [ { "_id": "53d1a9b501e1b6c73aed2b52", "name": "term", "body": "unrelelated", "favourite": "Fred", "id": "53d1a9b501e1b6c73aed2b52" }, { "_id": "53d1ae1a01e1b6c73aed2b56", "name": "unrelated", "body": "term", "favourite": "Fred", "id": "53d1ae1a01e1b6c73aed2b56" }, { "_id": "53d1ada301e1b6c73aed2b55", "body": "term term", "favourite": "Fred", "id": "53d1ada301e1b6c73aed2b55" }, { "_id": "53d1ad9e01e1b6c73aed2b54", "body": "term", "favourite": "Fred", "id": "53d1ad9e01e1b6c73aed2b54" } ]