如何解决在MongoDB聚合框架中使用多个字段按相关性排序
我有一个使用MongoDB(带有Mongoose驱动程序)的Node / NestJS后端应用程序。对于“获取”功能,我建立了一个聚合管道,首先可以应用一些“硬”过滤器,这些过滤器将全部内容过滤掉-现在,我需要一些软过滤器,该过滤器对搜索结果进行排名并将它们过滤掉不相关的。该算法应在文档上使用三个字段:标题,描述和标签。标题和标签应具有最大的权重。如果总相关性得分低于某个阈值,则结果将不包括在内。现在,我为此检查了其他一些StackOverflow帖子,例如this one,但它们似乎都只涉及“标签”字段。我找到了suggested to use indexes for this的文档,但是如果我大概知道该怎么做,我最好希望通过聚合框架来完成。
下面是来自另一个应用程序的代码,演示了该功能;
do {
let reg
if (Array.isArray(searchString)) {
reg = new RegExp(searchString[i],'gi')
} else {
reg = new RegExp(searchString,'gi')
}
for (const note of this.notes) {
const countTitle = (note.title.match(reg) || []).length
note.searchScore += countTitle
let countTags = 0
for (const tag of note.tags) {
const tagLength = (tag.match(reg) || []).length
countTags += tagLength
}
note.searchScore += countTags * 0.5
const countContent = (note.content.match(reg) || []).length
note.searchScore += countContent * 0.3
}
i++
} while (!Array.isArray(searchString) && i < searchString.length)
this.toDisplay = this.notes.filter(
f => f.searchScore > 0 + searchString.length / 4
)
this.showNew = false
this.sortUp = false
this.sortItems('relevance')
} else {
this.updateUI()
}
}
以上算法采用一个字符串或字符串数组。标题,标签和描述/内容的权重分别为1、0.5和0.3。设置了一个阈值,当分数低于或等于0 +搜索项的数量除以4时,项将被完全过滤掉。可以调整值,但是本质上,这是我要在聚合框架中实现的算法。看起来怎么样?预先感谢。
解决方法
您可以在聚合中使用文本索引-但这必须是第一步。
这是我的看法,只有一个搜索词:
const search = new RegExp(searchString,'i');
collection.aggregate().match(hardFilters)
// This step is not really necessary
.match({
$or: [{
tags: search
},{
title: search
},{
content: search
}]
})
.set({
relevance: {
$sum: [
{$multiply: [{$size: {$regexFindAll: {input: "$title",regex: search}}},100]},{$multiply: [{$size: {$regexFindAll: {input: {
$reduce: {
input: "$tags",initialValue: "",in: { $concat : ["$$value"," ","$$this"] }
}
},50]},{$multiply: [{$size: {$regexFindAll: {input: "$content",30]},]
}
})
.match({relevance: {$gte: searchString.length * 25}})
.sort({relevance: -1});
使用多个搜索字词,也许您可以这样做:
const search = new RegExp(searchStrings.join('|'),'i');
如果您确实想要,则可以通过以下操作分别搜索每个标签:
relevance: {
$sum: [].concat(...searches.map(search => [
{$multiply: [{$size: {$regexFindAll: {input: "$title",{$multiply: [{$size: {$regexFindAll: {input: ...,]))
}
也许您可以添加边界检查,而不管是多次搜索还是一次搜索:
const search = new RegExp("\b" + searchStrings.join('|') + "\b",'i');
,
鉴于Atlas Search默认情况下会返回按相关性排序的文档,并使用反向索引,因此这似乎是完成此任务的工具。相关性将更好,更可定制。根据您要构建的内容,您还会获得其他功能,例如突出显示和自动完成功能。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。