-
本地数据库存储在腾讯云的MySQL中,包括2类数据:
- 富文本网页数据存储在MySQL的text字段中
- 文档(doc、ppt、pdf、excel)数据存储在对象云存储中,我们使用了腾讯云COS存储,MySQL只存储了对象访问地址。
-
第三方数据源
第三方数据源需要和本地数据库es检索的结果混排后展示给页面显示。
热门搜索词主要来源用户搜索,目前场景比较简单,每天定时任务统计N天前的用户搜词词频,按搜索频次降序排序,存储到MySQL或者redis中。
但这种方法可能存在问题,热搜词没有时间权重衰减, 例如:一个关键词10天前搜索了101次,昨天搜索了100次,10天前的词还能继续排在昨天的热词前面吗? 因此需要设计一个热词衰减权重,搜索时间越长热度权重应该相应的衰减。
联想搜索需要在用户输入关键词时,联想用户可能想要搜索的词,该功能需要解决2个问题:
- 关联词来源
- 关联词方式
关联词来源可以通过用户搜索关键词记录来提取。
技术选择可以根据业务和当前的系统环境来判断:
- 尽量不重复造轮子;
- 使用已有、成熟的、开源的技术栈;
- 能满足需求,成本低、技术可控。
搜索存储引擎目前最成熟活跃的方案非 莫属,我们的系统基于腾讯云框架搭建,业务数据存在在腾讯云的中,因此搜索业务全文检索计划使用腾讯云最新版本的。
我们的需求里面需要将不同类型的文档转换为纯文本,这里有几个开源的实现方案:
2. 支持多种格式的文档转换。
3. 支持多语言。
4. 可以运行在多种操作系统上。
2. 某些高级功能不如付费的办公软件强大。
3. 需要较大的系统资源运行。
2. 支持大量的文档格式。
3. 可以提取文档中的元数据和文本内容。
2. 需要Java环境运行。
2. 使用Apache Tika进行文档解析。
3. 可以直接与Elasticsearch集成,方便进行全文搜索。
2. 对于一些复杂的文档格式,提取的结果可能不准确。
2. 支持OCR和全文搜索。
3. 可以直接与Elasticsearch集成。
2. 只能处理文件系统中的文件,不能处理网络上的文件。
我结合自身业务的情况,选了上面4种之外的方法,我们文档存储在,直接调用`腾讯云API转html 或者text文档:文档转 HTML
如果没有使用腾讯云我这边推荐使用`FsCrawler, 可参考我之前的博文。
同步工具选型
同步工具开源方案目前最活跃的应该是阿里巴巴开源的canal ,可以看官网的原理介绍
- MySQL主备复制原理:
- MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志- 事件binary log events,可以通过 show binlog events 进行查看)
- MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
- MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
- canal 工作原理
- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
- canal 解析 binary log 对象(原始为 byte 流)
注意:
- 可以配置不同数据源的评分权重(或者评分算法)以便优先要展示的结果;
- 需要记录不同数据源的偏移量和不同数据的查询结果,以便实现下次查询分页处理;
- 查询ES和查询私有云数据使用协程并行操作,等待2个结果共同返回处理。
另外你的第三方数据源没有存储在es数据库中,不能直接给出文档评分的,可以考虑以下混排方案:
2. 支持复杂的查询和排序。
2. 需要处理索引的创建和删除。
3. 可能需要处理数据源之间的数据差异。
2. 支持复杂的查询和排序。
2. 需要处理内存索引的创建和维护。
3. 可能需要处理数据源之间的数据差异。
2. 内存占用相对较低。
2. 对于复杂的查询和排序需求,可能不够灵活。
3. 可能需要处理数据源之间的数据差异。
由于要对2个数据源进行混排,要支持搜索跳页比较困难,因此在功能实现上目前只能支持上下翻页来实现
翻页计算公式
前端需要保存每一页es 和 api 两个数据源的偏移量:EsOffset 和 ApiOffset,可以使用对象数组保存, 翻页计算公式如下:
-
当前页码计算公式:
-
上一页:将当前页码减1 : , 获取上一页页面缓存的上一页 EsOffset 和 ApiOffset
-
下一页: , , 获取下一页的EsOffset 和 ApiOffset
注意:查询到第一页时,可清空页面分页缓存数组对象,重新存储。
翻页举例说明
-
查询首页,假设没页显示20条数据
-
请求参数:EsOffset = 0,ApiOffset=0, PageSize=20
-
返回结果:
-
前端需要需要页面需要保存当前第一页页面的 和 , ,页面缓存数据:
-
继续查询下一页
-
请求参数:
-
返回结果:
-
前端继续缓存当前分页数据:
-
-
查询上一页
-
请求参数:当前页码减1
-
返回结果:与第一页一致
如果是查询第一数据,清空缓存数组,重新缓存当前分页数据。
搜索权限需要满足根据用户权限过滤部分无权限的文档,以下是召回前处理和召回后处理权限的方案对比。
2. 减少了无关文档的返回,提高了性能。
2. 对于复杂的权限体系,实现可能较为复杂。
2. 适用于简单的权限体系。
2. 需要在查询后处理权限信息,增加了实现复杂度。
根据我的需求和场景,对性能要求较高,我们使用召回前过滤。如果希望简化查询过程,页可以考虑使用召回后过滤。
下面是在文档的权限格式,包含文档有权限的用户ID和部门ID
我们在设计索引mapping时
查询权限过滤参数:
搜索行为日志收集
通过收集用户搜索关键词存储在MySQL,每日定时任务统计热词和联想词来实现热门搜索榜和搜索联想词。
搜索热词设计
通过收集用户搜索词来统计热搜词, 其中热搜词的热度通过统计搜词词频来统计,统计包括热度周期,T+n 天的搜词次数排序,可以设计一个热度公式。
-
初始热度权重: w (默认1)
-
单位时间词频: c :(时间单位可以是 分钟、小时、天等,例如:以天为单位统计)
-
统计时间段数:T:(例如:连续统计30天关键词搜索频次)
-
单位时间热度: w / T(每个单位时间热度权重)
-
距离当前时间单位: n (例如计算最近30天,昨天的单位为:0)
一个词热度计算公式:
hot = [(T - 0)* c0 + (T - 1) * c1 + (T-2) * c2 + … + (T-n) * cn] * (w / T)
我们以按天统计,统计30天为例,每天的访问词频从近到远为: c0, c1, c2, … cn ( n 从0到29), 热度默认权重:1,时间段 T = 30(最近30天)
hot = [(30 - 0) * c0 + (30 -1) * c1 + (30-2)*c2 + … + (30 - 29) * c29] * 1 / 30
=> (30 * c0 + 29 * c1 + 28 * c2 + …+ c29) / 30
化简后可以得到热度公式:
-
每天定时任务统计用户搜索词,使用公式根据近30天的热度值,按从大到小排序top n;
-
将top n 结果存储到中,方便后续人工干预。
思考:同义词合并逻辑,类似词的搜索建议合并成一个词条,避免相似词太多。
搜索联想词设计
搜索联想词,使用了es的数据类型, 提供了一个叫做 的功能,它是一个基于前缀的自动完成建议器,可以用来实现关键词联想。当你输入一个词的前缀时,它就可以提供一些可能的完成建议。
-
每日统计用户近3个月搜索词,排序前top n 个词;
-
将top n 写入es。(n >= 1000)。
全文搜索模版
联想词索引
-
文档质量优化:
- 清洗低质量文档;
- 去除重复文档;
-
排序优先级配置:支持不同来源数据设置不同权重。
-
搜索词库完善,补充分词器搜索词库:可以通过热门搜索词表和日常维护;
-
搜索词自动补全(completion类型)?