输入关键字 抓取所有网页(树+TopK算法(二):Trie树存储)

优采云 发布时间: 2021-12-05 20:20

  输入关键字 抓取所有网页(树+TopK算法(二):Trie树存储)

  方案1 Trie树+TopK算法

  Trie树是字典树,也称为词搜索树或关键字树。它是一种树结构,是哈希树的一种变体。一个典型的应用是对大量的字符串(但不限于字符串)进行计数和排序,所以经常被搜索引擎系统用于文本词频统计。其优点是:尽量减少不必要的字符串比较,查询效率高于哈希表。Trie 是一棵存储多个字符串的树。相邻节点之间的边代表一个字符,这样树的每个分支代表一个子串,树的叶节点代表一个完整的串。与普通树的区别在于相同的字符串前缀共享相同的分支。例如,给定一组词 inn、int、at、age、adv、ant,我们可以得到以下 Trie:

  

  从上图可以看出,当用户输入前缀i时,搜索框可能会显示“in”、“inn”、“int”等以i为前缀的关键词,然后当用户输入前缀a,搜索框可能会提示以a为前缀的“ate”等关键词。这样一来,实现搜索引擎智能建议的第一步就很明确了,就是用trie树来存储大量的字符串。当前缀固定时,存储相对较热的后缀。

  TopK算法用于解决统计热词问题。解决TopK问题主要有两种策略:hashMap统计+排序,堆排序

  Hashmap 统计:首先对这批海量数据进行预处理。具体方法是:维护一个HashTable,Key为Query字符串,Value为Query出现的次数,即hash_map(Query, Value),每次读取一个Query,如果该字符串不在Table中,则添加字符串,并将 Value 值设置为 1;如果字符串在Table中,只需将字符串的计数加1,最后在O(N)时间复杂度内完成与Hash表的统计。

  堆排序:借助堆的数据结构,找到Top K,时间复杂度为N'logK。也就是说,借助堆结构,我们可以在日志时间中找到并调整/移动。因此,维护一个K(问题是大小为10)的小根堆,然后遍历300万个Query与根元素进行比较。因此,我们最终的时间复杂度为:O(N) + N' * O (logK),(N 为 1000 万,N' 为 300 万)。

  这个程序的问题是:

  方案二 Solr自带Suggest智能提示

  Solr作为一个被广泛使用的搜索引擎系统,内置了一个叫做Suggest模块的智能提示功能。模块可以选择根据提示词文本进行智能提示,也支持针对索引的某个字段创建索引词典进行智能提示。(详见 solr 的 wiki 页面)

  这个程序的问题是:

  方案三 Solrcloud建立单独的集合,使用Solr前缀查询来实现

  如上所述,上述两种方案的实现都存在一些问题。Trie树+TopK算法在处理汉字suggest时不是很优雅,需要维护两棵Trie树,实现起来比较复杂;Solr 自己的suggest 智能提示组件的问题在于它使用了freq 排序算法。返回结果完全基于索引中字符的出现次数,没有考虑用户搜索词的频率。因此,一些流行词不能排名更高。因此,我们继续寻找更优雅的解决方案来解决这个问题。

  此时,我们考虑专门为关键字创建一个索引集合,使用Solr前缀查询来实现。solr中的copyField可以很好的解决我们同时索引多个字段(汉字、拼音、缩写)的需求,并且当字段的multiValued属性设置为true时,可以解决多音素组合的问题同一个关键词。配置如下:

  schema.xml:

------------------multiValued表示字段是多值的-------------------------------------

kw

suggest

说明:

kw为原始关键字

pinyin和abbre的multiValued=true,在使用solrj建此索引时,定义成集合类型即可:如关键字“重庆”的pinyin字段为{chongqing,zhongqing}, abbre字段为{cq, zq}

kwfreq为用户搜索关键的频率,用于查询的时候排序

-------------------------------------------------------

------------------suggest_text----------------------------------

  KeywordTokenizerFactory:这个分词器不执行任何分词!整个字符流变成了一个标记。String 字段类型也有类似的效果,但不能配置文本分析的其他处理组件,例如大小写转换。对于任何用于排序和大多数分面功能的索引字段,该索引字段在原创字段值中只能有一个单词元素。

  前缀查询构造:

  private SolrQuery getSuggestQuery(String prefix, Integer limit) {

SolrQuery solrQuery = new SolrQuery();

StringBuilder sb = new StringBuilder();

sb.append(“suggest:").append(prefix).append("*");

solrQuery.setQuery(sb.toString());

solrQuery.addField("kw");

solrQuery.addField("kwfreq");

solrQuery.addSort("kwfreq", SolrQuery.ORDER.desc);

solrQuery.setStart(0);

solrQuery.setRows(limit);

return solrQuery;

}

  结果如下图:

  

  参考

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线