0x00 关键词构建DAT细节

0000. 普通关键词DAT

  • 查询的关键词自组装成成一个字符串,组装格式为:关键词内容 + “\r” + 是否排序 + “\r” + 是否整词检索 + 属性json字符串。

  • 其中属性json的格式为:

1
2
3
4
5
6
7
8
9
10
11
12
{
"origin_content": "关键词",
"level": "分级映射",
"level_code": "分级编码",
"classify_code": "分类编码",
"industry_code": "行业编码",
"sp_only_show_official": "特殊时期是否只展示官方信息",
"sp_show_official_ctl": "特殊时期只展示官方信息是否开启",
"id": "关键词ID",
"label": "", // 为空
"type": "" // 为空
}
  • 构建次构建在内存中,以map的方式存储DAT树,key为词表code,value为DAT树。具体构建方式在DartsManager.class类中。

    • 首先对关键词做归一化处理:繁转简、全转半、大写转小写、转换同音形近字。
    • 检查关键词是否满足约定的规则:组合关键词属性信息检查"^[^\t]+\t?[^\t]*"和组合关键词关键词格式检查"^([^&|~]+)((?:[&|~][^&|~]+)*)"。匹配方式为:Perl5Matcher。实现代码KeywordMatcher.class中。
    • 默认不排序,默认为整词。
    • 获取真正的关键词列表,也就是把符合关键词根据字符&、|和~进行截取。注意这地方:如果关键词截取之后是相同的,因为使用了Multimap<String, CompoundKeywordInfo>,则不会出现存在覆盖的问题。
    • 构建跳跃表信息:SkipTermExtraInfo。如果关键词是纯英文字母和数字,那么就需要整词匹配。
    • 支持逆向匹配。
    • 在Darts#build(List<char[]> keywords,VocabularyProcess process,int initialCapacity)方法中,处理关键词的可能存在匹配的关键词和属性匹配的情况。
    • 对关键词父节点、子节点按照字母表顺序进行排列。
    • 构建DAT。

0010. 形近字关键词DAT

  • 查询的关键词自组装成成一个字符串,组装格式为:关键词转写为拼音 + “\r” + 是否排序 + “\r” + 是否整词检索 + 属性json字符串。
  • 其中转写逻辑如下:遍历关键词内容,把charAt(i)的ASCII码大于128的进行转写,转写成功为拼音,转写失败为*代替,小于128的字符则保持原样,每个拼音、符号、*之间用“”分隔符拼接。但是实际用到的地方是把“”转换为空字符串的。
  • 其中属性json的格式为:
1
2
3
4
5
6
7
8
9
10
11
12
13
[{
"level": "分级映射", // 可能没有
"level_code": "分级编码", // 可能没有
"classify_code": "分类编码", // 可能没有
"industry_code": "行业编码", // 可能没有
"sp_only_show_official": "特殊时期是否只展示官方信息", // 可能没有
"sp_show_official_ctl": "特殊时期只展示官方信息是否开启", // 可能没有
"label": "", // 为空,可能没有
"type": "", // 为空,可能没有
"id": "关键词ID",
"origin_content": "关键词"

}]

值得注意的是:上面的json串里面前面的大部分key可能不存在,取的是数据库中attr_info字段的内容。

其他部分和普通关键词一致。

0x001 关键词检索

0000. 中文检索

  • 前置处理:过滤emoji标签、标点符号

  • 检索内容映射为MappedCharArray,字符映射表实现类,利用一个int数组记录字符串在过滤前后的位置,目标字符串在源字符串中的位置可能不连续,目标字符串的长度记录在charCount中,使用者在处理时必须及时更新。

  • 从全局判断合作方是否开启了形近字转换。[放到预处理]

  • 再次处理MappedCharArray中的html以及形近字等归一化操作:这里的同音形近字都需要用到预处理的表,业务交由预处理逻辑处理,因此上,本期重构,检索服务不再处理数据预处理操作,直接检索,提升性能。

1
2
boolean needT2S, boolean needDBC, boolean ignoreCase, boolean filterNoneHanLetter, boolean convertSynonymy, boolean filterSymbol, boolean keepLastSymbol
# 对应的值:true, true, false, true, true, true
  • 关键检索的逻辑:WordTermsProcessingUtil,主要处跳词检索逻辑、整词检索.

  • 问题:设置关键词的跳词功能,不合理。因为跳词是在匹配检索过程,这个过程中,你连关键词都没找到,怎么知道关键词对应的属性呢?不知道属性,怎么知道要跳多少?(唯一的解决方法:根据词表中设置的最大的跳词进行,然后再后置处理,性能不敢想)

跳词是对检索的文本的跳动,跳过一些词来检索关键词。

  • 判断是否是复合关键词处理,满足正则表达式:^([^&|~]+)((?:[&|~][^&|~]+)*)$。具体处理逻辑:CompoundKeywordCalc#hitOrNot以及AbstractSearcher#judgeHittingLegalCompoundKeyword。

  • 复合关键词&、|、~的处理,复合关键词排序处理。

  • 对于非外文匹配,且需要分词的词表,对原文做分词,将分词结果与复合匹配结果做比较校正。味精 虫草, HanLP

  • 对于开启了同音字检索的,再通过上述方式方式匹配同音字,同时,对原文分词之后转拼音,和检索结果校正,将拼音的结果通前面原文的结果合并返回。

0010. 外文检索

外文检索,这里说的外文是指以空格为分隔符的语种,并不是特指国外的语言,类似日语、韩语不应该走这个检索模式。

检索方式和【中文检索】相似,去除上述逻辑中一系列针对中文进行的操作以及模糊操作。

0x010 存在问题以及注意点

  1. 老系统关键词检索中有一些预处理的逻辑,本期要移除掉,这些逻辑防止到预处理。

  2. 重点说一下形近字,该功能在预处理中,关键字检索中不应该保留。

  3. 跳词功能,跳词是检索的过程,不是对结果的处理,所以,设计上,如果每个关键词的跳词可配置,如何处理这种业务。跳词目前建议是3-5个字符,超过之后,性能以指数级别打折扣。

  4. 能支持的功能:

  • 作用在关键词上:空格分隔语种(也就是纯粹匹配)、复合关键词、同音字、顺序识别、整词匹配
  • 作用在标签上:是否分词。如果从检索流程来说,其中是否跳词应该放在这个级别。这个新版中放在策略配置中。
  1. 性能上:
  • 考虑一个合作方一棵树。标签属性等其他属性最后在结果处理阶段完成。DAT检索性能远远高于多次遍历检索原文。
  • 增加动态BinTrie,算法可用FlashText等,百万以内关键词性能也是10毫秒以内。
  • DAT节点使用缓存行补齐,通过空间换取时间。
  • jvm性能优化:开启重复字符串压缩,减少重复关键对内存的消耗。