java实现热搜_搜索推荐系统根据⽤户搜索频率(热搜)排序之前写的三叉树,有点⼉简单,并不能满⾜实际项⽬的需要。先简单分析⼀下solr中搜索推荐系统的核⼼算法。
wiki中有关于solr的搜索推荐的详细描述,但是核⼼算法需要⾃⼰查看源代码。关于wiki上的解读,之前做了⼀次简单的翻译,根据此⽂档,详细研读了源代码,先把核⼼思想呈现出来。
基本流程如下:当⽤户输⼊搜索词语前缀时,通过前端调⽤solr的suggest,到Suggeser对象,Suggester根据匹配的field从主索引库中读取field下⾯的terms,来构建dictionry,由于主索引库中的terms是经过合并和排序的,索引在构建三叉树的时候,省去了⽤pinyin4j 组件进⾏排序的过程。接下来,就是通过对字典的折中处理,来实现⾃平衡的三叉树,以提⾼检索效率。三叉树构建完之后,进⾏前缀匹配查询,搜索出所有符合要求的词元,然后加⼊到优先级队列中,构建有限容量的堆,调整堆顶的值为最⼩。之所以Lucene⾃⼰写了PriorityQueue,⽽不⽤jdk⾃⾝的,是因为jdk的PriorityQueue,容量可以扩展的,他会把所有匹配出来的词元都加进去,然后输出top N词元,这明显是内存浪费。之前的⼀篇关于从海量数据中,查出top N数据的博客中,已经阐述了堆排序的思想,不赘述。最后通过优先级队列输出结果。
Suggester---Lookup----LookupImpl(TSTLookup、JaSpellLookup、FSTLookup),之前研读的是TSTLookup。排序的核⼼思想是:构建完字典之后,得到Dictionry对象,由Dictionary对象得到InputIter
ator,对字典进⾏扫描读取,能读取到两个变量:⼀个为term,另⼀个为term的权重,排序⽤的。对字典扫描结束后,把terms和weight分别加载到两个list中,以便插⼊三叉树中。那么,三叉树节点对象的设计,就很重要了。封装以下属性:storedChar、val(weight)、token(最后节点存储的term成词)。插⼊的具体逻辑,⾃⼰对上次的写的三叉树,进⾏了改进,代码如下:
package aryTree;
/**
* 三叉树节点
* @author TongXueQiang
* @date 2016/03/12
* @since JDK 1.7
*/
public class TernaryNode {
public char storedChar;//节点存储的单个字符
public String token;//最后⼀个节点存储的term(成词)
public TernaryNode leftNode,centerNode,rightNode;//⼦节点
public TernaryNode (char storedChar) {
this.storedChar = storedChar;
}
}
package aryTree;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/
**
* ⾃定义三叉树
*
* @author TongXueQiang
* @date 2016/03/12
* @since JDK 1.7
*/
public class TernaryTree {
// 根节点,不存储字符
private static TernaryNode root = new TernaryNode('\0');
/**
* 向树中插⼊字符
*
* @param TernaryNode
* @param word
* @return
*/
public void insert(String word) {
root = insert(root, word, 0);
}
public TernaryNode insert(TernaryNode currentTernaryNode, String word, int index) {
if (word == null || word.length() < index) {
return currentTernaryNode;
}
char[] charArray = CharArray();
if (currentTernaryNode == null) {
currentTernaryNode = new TernaryNode(charArray[index]);
}
if (currentTernaryNode.storedChar > charArray[index]) {
currentTernaryNode.leftNode = insert(currentTernaryNode.leftNode, word, index);
} else if (currentTernaryNode.storedChar < charArray[index]) {
currentTernaryNode.rightNode = insert(currentTernaryNode.rightNode, word, index);
} else {
if (index != word.length() - 1) {
}
}
return currentTernaryNode;
}
/**
* 查以指定前缀开头的所有字符串
*
* @param prefix
* @return
*/
public List prefixCompletion(String prefix) {
// ⾸先查前缀的最后⼀个节点
TernaryNode TernaryNode = findNode(prefix);
return prefixCompletion(TernaryNode);
}
public List prefixCompletion(TernaryNode p) {
List suggest = new ArrayList();
if (p == null) return suggest;
if ((p.centerNode == null) && (p.token == null)) return suggest; if ((p.centerNode == null) && (p.token != null)) {
suggest.add(p);
return suggest;
}
if (p.token != null) {
suggest.add(p);
}
p = p.centerNode;
Stack s = new Stack();
s.push(p);
while (!s.isEmpty()) {
TernaryNode top = s.pop();
if (ken != null) {
suggest.add(top);
}
if (Node != null) {
s.Node);
}
if (top.leftNode != null) {
s.push(top.leftNode);
}
if (top.rightNode != null) {
s.push(top.rightNode);
}
}
return suggest;
}
/**
* 查前缀的下⼀个centerTernaryNode,作为searchPrefix的开始节点
*
* @param prefix
* @return
*/
public TernaryNode findNode(String prefix) {
return findNode(root, prefix, 0);
}
private TernaryNode findNode(TernaryNode TernaryNode, String prefix, int index) { if (prefix == null || prefix.equals("")) {
return null;
}
if (TernaryNode == null) {
return null;
}
char[] charArray = CharArray();
// 如果当前字符⼩于当前节点存储的字符,查左节点
if (charArray[index] < TernaryNode.storedChar) {
return findNode(TernaryNode.leftNode, prefix, index);
}
// 如果当前字符⼤于当前节点存储的字符,查右节点
else if (charArray[index] > TernaryNode.storedChar) {
return findNode(TernaryNode.rightNode, prefix, index);
} else {// 相等
// 递归终⽌条件
if (index !=charArray.length - 1) {
return Node, prefix, ++index);
}
java网课推荐}
return TernaryNode;
}
}
改进后的三叉树,前缀匹配查后得到结果不再是简单的字符串,⽽是节点对象,⾥⾯封装了匹配的term和weight值。接下来,把得到的suggest加⼊得到优先级队列中,得到升序的结果。测试代码如下:
package st;
import java.util.Stack;
import chinese.utility.utils.PriorityQueue;
/**
* 输出topN,⽤Lucene的PriorityQueue,思路和之前写过的堆排序类似,队列中容量为N,
* 当向队列中插⼊的时候,调整堆,让堆顶元素最⼩,当超过容量的时候,在插⼊的时候,如果插⼊
* 的元素⽐堆顶元素⼤,则替换之,如果⼩,废弃之。最后按升序排列输出topN.要实现lessThan
* ⽅法,确定⽐较的项⽬,⽐如输⼊token的权重weight.
* @author hadoop
* @date 2016/03/11
*/
public class MyPriorityQueue extends PriorityQueue {
public MyPriorityQueue(int num) {
super(num);
}
@Override
protected boolean lessThan(Object a, Object b) {
return (Integer)a < (Integer)b;
}
public static void main(String[] f) {
MyPriorityQueue priQueue = new MyPriorityQueue(3);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论