【问题标题】:Elasticsearch user clicks feedbackElasticsearch 用户点击反馈
【发布时间】:2017-06-02 09:23:29
【问题描述】:

在使用 Elasticsearch 制作的搜索引擎中,考虑用户点击结果项以提高具有更多用户印象的文档得分的最佳解决方案是什么?

是否有任何工具或插件可以使用,或者是否应该从头开始编写?

该解决方案预计会像 google 一样考虑以下几点:

  • 每个文档的显示次数
  • 用户点击文档的次数
  • 用户搜索的查询(文档可能在特定查询中很重要,但在其他查询中不重要)
  • ...

【问题讨论】:

  • 您希望 ES 如何了解点击?不可能,这是你需要在你的系统中实现的东西,稍后它将向 ES 提交一些点击数据作为提升/隐藏值
  • 我知道我的系统应该向 elasticsearch 提交点击数据。我的问题是如何让 elasticsearch 将此日志数据视为包含查询和单击结果的评分公式的一部分。这不像提升文档那么简单..
  • 是的,这并不简单,但是可以根据一些公式来实现一些简单的临时性操作,例如查询时间提升,比如 score = initial_score + clicksw * 节目或其他东西

标签: elasticsearch lucene elasticsearch-plugin


【解决方案1】:

如果您正在使用 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。如果我的元数据中已经有“雪”,并且新的查询带有这个关键字,我会更新相同的元文档。

现在我想讨论一下缺点以及为什么我选择将它们保存在单独的数据库中而不是将它们附加到 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
}

上面的父子文档几乎解释了我是如何为每个文档的搜索统计构建元数据的。

到目前为止,我们已经构建了一个非常智能的解决方案来收集搜索统计的事件数据,并成功地将它们与弹性中的每个文档相关联。

让我们从这里开始查看评分查询 -

我不会在这里深入设计评分算法,但我会更多地实现查询,它可以根据视图对文档进行评分,点击与关键字相关联以及与关键字相关。

Function score query

Script score

现在我可以选择在名称上比在类别上更重视匹配。从您的用例的角度来看,这就是全部内容,我不会深入为您设计分数公式。

{
    "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"
        }
    }
}

因此您可以使用与上述类似的查询,我刚刚为每个子句选择了一个带有演示提升参数的非常简单的公式,并且可以在实施高级评分算法时重构此查询。

脚本评分功能在这里很重要,因为我首先根据单个父文档的搜索关键字过滤子文档,然后使用脚本评分来使用点击和查看计数来影响我的整体文档评分。

现在这是我希望在我的项目中实施的一种解决方案,我愿意为我的解决方案提出建议和改进。

请分享您的建议和改进。

希望这会有所帮助 谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-29
    • 2015-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多