Mongodb 根据计算值对集合进行排序
我有一个mongo数据库,包含两个集合:Mongodb 根据计算值对集合进行排序,mongodb,mongoose,mongodb-query,aggregation-framework,Mongodb,Mongoose,Mongodb Query,Aggregation Framework,我有一个mongo数据库,包含两个集合: module.exports = mongoose.model('Article', { title : String, text : String, topics : Array, category : String }); module.exports = mongoose.model('User', { email : String, password : String,
module.exports = mongoose.model('Article', {
title : String,
text : String,
topics : Array,
category : String
});
module.exports = mongoose.model('User', {
email : String,
password : String,
topics : [{
_id: false,
topic: String,
interest: {type: Number, default: 0}
}]
});
“文章主题”字段包含文章主题,而“用户主题”字段包含与用户相关的主题
我想做一个查询,返回按字段排序的文章,该字段是文章的主题数组和给定ID的用户(当前用户)的主题数组的线性组合
例如,如果文章主题为:
[“食物”、“比萨饼”、“意大利面”]
用户主题为:
[{主题:“汽车”,兴趣:2},{主题:“比萨饼”,兴趣:5},{主题:“意大利面”,兴趣:1}]
项目字段=5+1=6,因为比萨饼和意大利面重合
那么我想在那个领域下订单
我怎么做
我想得到这样的东西:
Users.findById(req.user._id).exec(function (err, user) {
Article.aggregate([
{
// projectField = sum user.topics.interest for any user.topics.topic in topics
},
{ $sort : { projectField : -1} }
], function (err, articles) {
console.log(articles);
});
});
您基本上需要通过
.aggregate()
将其输入,然后根据计算值进行排序
本质上是这样。您已经在内存中有选择的<代码>用户>代码>对象,但是我们会考虑它是一个选择,并继续从承诺:
User.findById(userId).then(user =>
Arictle.aggregate([
{ "$addFields": {
"score": {
"$sum": {
"$map": {
"input": user.topics,
"as": "u",
"in": {
"$cond": {
"if": { "$setIsSubset": [ ["$$u.topic"], "$topics" ] },
"then": "$$u.interest",
"else": 0
}
}
}
}
}
}},
{ "$sort": { "score": -1 } }
])
).then( results => {
// work with results here
});
因此,本质上我们从用户
中提供“数组”,并将其作为参数提供给,它坦率地接受“任何数组”,并且不需要成为文档本身的一部分
迭代每个项目时,将与当前文章中的“主题”
进行比较,以查看用户提供的数组中当前的“主题”
是否匹配。这种比较是通过,记住将奇异值包装在一个数组中进行比较
当它匹配时,我们提供兴趣
值,否则0
。然后将“映射”数组馈送到,您将获得一个“分数”,然后可以在后一阶段对其进行排序
从技术上讲,您可以将输入提供给from,而不是使用inside来确定输出,但是这样做的语法“更简洁一些”,因此避免使用“交换”值
当一切都完成后,你只需在计算字段上,这就是你的结果
如果需要,添加和阶段,或者更好的是完成所有计算和“排序”之后的“分页”,以实现结果的“分页”。但基本过程就在这里
注意:管道阶段将是最佳的使用方式,因为您只需要提供“新”字段,并将其附加到文档中。如果您的MongoDB版本不支持此管道阶段,则只需替换为,注意您必须明确指定文档中要返回的每个字段,并添加新的计算字段。但在实现的逻辑中绝对没有其他区别
完整列表
生成结果:
Mongoose: articles.remove({}, {})
Mongoose: users.remove({}, {})
Mongoose: users.insert({ email: 'bill@example.com', _id: ObjectId("5970969d3248a329766d5e72"), topics: [ { topic: 'pizza', interest: 5 }, { topic: 'pasta', interest: 1 } ], __v: 0 })
{
"__v": 0,
"email": "bill@example.com",
"_id": "5970969d3248a329766d5e72",
"topics": [
{
"topic": "pizza",
"interest": 5
},
{
"topic": "pasta",
"interest": 1
}
]
}
Mongoose: articles.insert({ title: 'test', _id: ObjectId("5970969d3248a329766d5e73"), topics: [ 'food', 'pizza', 'pasta' ], __v: 0 })
{
"__v": 0,
"title": "test",
"_id": "5970969d3248a329766d5e73",
"topics": [
"food",
"pizza",
"pasta"
]
}
Mongoose: users.findOne({ _id: ObjectId("5970969d3248a329766d5e72") }, { fields: {} })
Mongoose: articles.aggregate([ { '$addFields': { score: { '$sum': { '$map': { input: [ { interest: 5, topic: 'pizza' }, { interest: 1, topic: 'pasta' } ], as: 'u', in: { '$cond': { if: { '$setIsSubset': [ [ '$$u.topic' ], '$topics' ] }, then: '$$u.interest', else: 0 } } } } } } }, { '$sort': { score: -1 } } ], {})
[
{
"_id": "5970969d3248a329766d5e73",
"title": "test",
"topics": [
"food",
"pizza",
"pasta"
],
"__v": 0,
"score": 6
}
]
获取一篇文章,从中获取主题,聚合用户的主题。如果您遇到任何特殊问题,请显示代码。我通过添加我的天真方法更新了问题。@AlexBlex实际上,从用户
中简单地$map
数组,而不是“过滤”任何内容,会更有意义。至少这是我对它的看法和我在这里使用的方法。@NeilLunn,是的,不错。应该使用最小的调整。@AlexBlex什么调整?已经给出了完全有效的答案。这里没有什么可以改变的。完美的卓越。我掌握了这个概念。我仍然无法理解为什么会出现这样的错误:“MongoError:无法识别的表达式“$setIsSubSet”“”,即使我的版本(db.version())是3.4.2,并且在文档中说它受版本>=2的支持。6@GabrielePicco这是我的一个“打字错误”,自从你复制了代码后就被修正了。它是$setIsSubset
,就像它在答案的其余部分所说的那样。@GabrielEpico为您添加了完整的列表和输出。当我把它们作为答案提交时,我确实知道这些东西是有效的。我经常举一个我自己运行的例子,再现你在问题中提出的相同条件。就像这里,我的错我控制不了!谢谢,你的答案很完美!
Mongoose: articles.remove({}, {})
Mongoose: users.remove({}, {})
Mongoose: users.insert({ email: 'bill@example.com', _id: ObjectId("5970969d3248a329766d5e72"), topics: [ { topic: 'pizza', interest: 5 }, { topic: 'pasta', interest: 1 } ], __v: 0 })
{
"__v": 0,
"email": "bill@example.com",
"_id": "5970969d3248a329766d5e72",
"topics": [
{
"topic": "pizza",
"interest": 5
},
{
"topic": "pasta",
"interest": 1
}
]
}
Mongoose: articles.insert({ title: 'test', _id: ObjectId("5970969d3248a329766d5e73"), topics: [ 'food', 'pizza', 'pasta' ], __v: 0 })
{
"__v": 0,
"title": "test",
"_id": "5970969d3248a329766d5e73",
"topics": [
"food",
"pizza",
"pasta"
]
}
Mongoose: users.findOne({ _id: ObjectId("5970969d3248a329766d5e72") }, { fields: {} })
Mongoose: articles.aggregate([ { '$addFields': { score: { '$sum': { '$map': { input: [ { interest: 5, topic: 'pizza' }, { interest: 1, topic: 'pasta' } ], as: 'u', in: { '$cond': { if: { '$setIsSubset': [ [ '$$u.topic' ], '$topics' ] }, then: '$$u.interest', else: 0 } } } } } } }, { '$sort': { score: -1 } } ], {})
[
{
"_id": "5970969d3248a329766d5e73",
"title": "test",
"topics": [
"food",
"pizza",
"pasta"
],
"__v": 0,
"score": 6
}
]