在使用Elasticsearch制作的搜索引擎中,考虑用户点击结果项目以提高用户展示次数的文档得分的最佳解决方案是什么?
是否有任何工具或插件可供使用,或者应该从头开始编写?
预计解决方案会像谷歌一样考虑以下内容:
答案 0 :(得分:3)
如果您正在使用rails / ruby开发API,那么您可以通过使用更多的搜索解决方案变得更加智能,来查看searchkick几乎完成这项工作。
现在,如果您不在轨道上,或者您想开发自己的内部实施,那么这里有一些关于架构的建议。
让我们首先介绍基本概述,关键模块,缺点,并根据解决方案的缺点调整体系结构。
你需要
1)评分算法,您可以在其中定义公式的公式,该公式将生成每个文档的分数。 让我们考虑你提到的参数
a)每个文件都没有出现过 b)没有点击任何文件。 c)查询搜索的文档。
现在你没有提到a)和b)如何适应当前的背景。我会假设一个更简单的,但如果你想建立一个真正先进的智能解决方案,我也会将a)b)与c)结合起来。例如 - 文档针对给定关键字出现的次数。像我一样,搜索“雪地靴”应该考虑这个(出现次数/无点击次数)仅适用于查询或多或少像“雪地靴”而不是所有情况。其中“雪地靴”可以被破坏为具有关键字顺序近似的以下元的关键字。
{
"keyword": "snow",
"document_ids": [3, 5, 6, 8],
"document_ids_views": [{
"doc_id": 3,
"views ": 110,
"clicks": 560
}, {
"doc_id": 5,
"views": 100,
"clicks": 78
}, {
"doc_id": 6,
"views": 100,
"clicks": 120
}, {
"doc_id": 3,
"views": 100,
"clicks": 465
}]
}
{
"keyword": "boots",
"document_ids": [3, 5, 6, 8],
"document_ids_views": [{
"doc_id": 3,
"views ": 100,
"clicks": 56
}, {
"doc_id": 5,
"views": 100,
"clicks": 78
}, {
"doc_id": 6,
"views": 100,
"clicks": 120
}, {
"doc_id": 3,
"views": 100,
"clicks": 465
}]
}
以上是存储在每个关键字的单独数据库中的汇总数据。
像这样我会在一个单独的数据存储区中每天建立一个统计数据的元数据,比方说mongo。如果我的meta中已经有“snow”,并且使用此关键字输入新查询,我将更新相同的元文档。
现在我想讨论一下缺点,以及为什么我选择将它们保存在单独的数据库中,而不是将它们附加到elasticsearch文档中。
每次触发新查询以更新弹性文档中的点击次数和视图计数时,我都不想使用elasticsearch集群,因为我知道更新对于反向索引合并非常I / O广泛。
现在为了弥补这个缺点,我将有一个每日或每日两次的批处理作业,以弹性方式将这些元信息移植到每个文档。我将使用这个新的元信息重建整个集群,并将别名从旧索引移动到新索引,而不会有任何停机时间。
现在要将此信息与弹性文档关联或添加,我会使用parent-child documents relationship将弹性文档与关联的关键字进行映射。
所以我的基本父文档和子文档看起来像
父文件
PUT /index/type/3
{
"name": "Reebok shoes",
"category": "snow boots",
"price": 120
}
子文件
PUT /index/type_meta/1?parent=3
{
"keyword": "boots",
"document_id": 3,
"doc_id": 3,
"views ": 100,
"clicks": 56
}
PUT /index/type_meta/1?parent=3
{
"keyword": "snow",
"document_id": 3,
"doc_id": 3,
"views ": 110,
"clicks": 560
}
上述父子文档几乎解释了我如何构建每个文档的搜索统计信息的元数据。
到目前为止,我们已经构建了一个非常智能的解决方案来收集搜索统计数据的事件数据,并成功地将它们与弹性的每个文档相关联。
让我们在这里开始查看评分查询 -
我不会在这里设计得分算法,但我会更多地实现查询,该查询可以根据视图对文档进行评分,点击关联关键字以及与关键字相关。
现在我可以选择给名字中的比赛赋予更多权重而不是类别。从你的用法观点来看,我都不会深入为你设计得分公式。
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"boost": "5",
"functions": [{
"filter": {
"match": {
"name": "snow"
}
},
"random_score": {},
"weight": 200
}, {
"filter": {
"match": {
"name": "boots"
}
},
"weight": 200
}, {
"filter": {
"match": {
"category": "snow"
}
},
"random_score": {},
"weight": 100
}, {
"filter": {
"match": {
"category": "boots"
}
},
"weight": 100
}, {
"filter": {
"query": {
"has_parent": {
"type": "type_meta",
"query": {
"match": {
"keyword": "snow"
}
}
}
}
},
"script_score": {
"script": {
"lang": "painless",
"inline": "_score + 20*doc['clicks'].value + 40 * doc['views].value"
}
}
}, {
"filter": {
"query": {
"has_parent": {
"type": "type_meta",
"query": {
"match": {
"keyword": "boots"
}
}
}
}
},
"script_score": {
"script": {
"lang": "painless",
"inline": "_score + 20*doc['clicks'].value + 40 * doc['views].value"
}
}
}],
"score_mode": "max",
"boost_mode": "multiply"
}
}
}
所以你可以使用上面的查询simillar,我刚刚为每个子句选择了一个非常简单的带有demo boost params的公式,这个查询可以重构为实现提前计分算法。
脚本分数功能在这里非常重要,因为我首先根据该单个父文档的搜索关键字过滤子文档,然后使用脚本分数来使用点击和查看计数来影响我的整体文档分数。
现在这是我希望在我的项目中实施的一种解决方案,我愿意接受我的解决方案的建议和改进。
请分享您的建议和改进。
希望这会有所帮助 感谢