lucene的suggest(搜索提示功能的实现)

时间:2022-09-19 09:50:57

1.首先引入依赖

<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-suggest -->
<!-- 搜索提示 -->
<dependency>
  <groupId>org.apache.lucene</groupId>
  <artifactId>lucene-suggest</artifactId>
  <version>7.2.1</version>
</dependency>

2.既然要进行智能联想,那么我们需要为提供联想的数据建立一个联想索引(而不是使用原来的数据索引),既然要建立索引,那么我们需要知道建立索引的数据来源。我们使用一个扩展自InputIterator的类来定义数据来源。首先我们看看被扩展的类InputIterator

public interface InputIterator extends BytesRefIterator {
InputIterator EMPTY = new InputIterator.InputIteratorWrapper(BytesRefIterator.EMPTY); long weight(); BytesRef payload(); boolean hasPayloads(); Set<BytesRef> contexts(); boolean hasContexts(); public static class InputIteratorWrapper implements InputIterator {
private final BytesRefIterator wrapped; public InputIteratorWrapper(BytesRefIterator wrapped) {
this.wrapped = wrapped;
} public long weight() {
return 1L;
} public BytesRef next() throws IOException {
return this.wrapped.next();
} public BytesRef payload() {
return null;
} public boolean hasPayloads() {
return false;
} public Set<BytesRef> contexts() {
return null;
} public boolean hasContexts() {
return false;
}
}

weight():此方法设置某个term的权重,设置的越高suggest的优先级越高;

payload():每个suggestion对应的元数据的二进制表示,我们在传输对象的时候需要转换对象或对象的某个属性为BytesRef类型,相应的suggester调用lookup的时候会返回payloads信息;

hasPayload():判断iterator是否有payloads;

contexts():获取某个term的contexts,用来过滤suggest的内容,如果suggest的列表为空,返回null

hasContexts():获取iterator是否有contexts;

lucene suggest提供了几个InputIteratior的默认实现

BufferedInputIterator:对二进制类型的输入进行轮询;
DocumentInputIterator:从索引中被store的field中轮询;
FileIterator:从文件中每次读出单行的数据轮询,以\t进行间隔(且\t的个数最多为2个);
HighFrequencyIterator:从索引中被store的field轮询,忽略长度小于设定值的文本;
InputIteratorWrapper:遍历BytesRefIterator并且返回的内容不包含payload且weight均为1;
SortedInputIterator:二进制类型的输入轮询且按照指定的comparator算法进行排序;

3.既然指定了数据源,下一步就是如何建立suggest索引

RAMDirectory indexDir = new RAMDirectory();
StandardAnalyzer analyzer = new StandardAnalyzer();
AnalyzingInfixSuggester suggester = new AnalyzingInfixSuggester(indexDir, analyzer); // 创建索引,根据InputIterator的具体实现决定数据源以及创建索引的规则
suggester.build(new InputIterator{});

4.索引建立完毕即可在索引上进行查询,输入模糊的字符,Lucene suggest的内部算法会根据索引的建立规则提出suggest查询的内容。

private static void lookup(AnalyzingInfixSuggester suggester, String name,
String region) throws IOException {
HashSet<BytesRef> contexts = new HashSet<BytesRef>();
//使用Contexts域对suggest结果进行过滤
contexts.add(new BytesRef(region.getBytes("UTF8")));
//num决定了返回几条数据,参数四表明是否所有TermQuery是否都需要满足,参数五表明是否需要高亮显示
List<Lookup.LookupResult> results = suggester.lookup(name, contexts, 2, true, false);
System.out.println("-- \"" + name + "\" (" + region + "):");
for (Lookup.LookupResult result : results) {
System.out.println(result.key);//result.key中存储的是根据用户输入内部算法进行匹配后返回的suggest内容
}

5.下面提供一个实例说明完整的suggest索引创建,查询过程 
实体类

package com.cfh.study.lucence_test6;

import java.io.Serializable;

/**
* @Author: cfh
* @Date: 2018/9/17 10:18
* @Description: 用来测试suggest功能的pojo类
*/
public class Product implements Serializable {
/** 产品名称 */
private String name;
/** 产品图片 */
private String image;
/** 产品销售地区 */
private String[] regions;
/** 产品销售量 */
private int numberSold; public Product() {
} public Product(String name, String image, String[] regions, int numberSold) {
this.name = name;
this.image = image;
this.regions = regions;
this.numberSold = numberSold;
} public String getName() { return name;
} public void setName(String name) {
this.name = name;
} public String getImage() {
return image;
} public void setImage(String image) {
this.image = image;
} public String[] getRegions() {
return regions;
} public void setRegions(String[] regions) {
this.regions = regions;
} public int getNumberSold() {
return numberSold;
} public void setNumberSold(int numberSold) {
this.numberSold = numberSold;
}
}

指定数据源,这里的数据源是传入的一个product集合的迭代器,可以根据实际情况更换数据源为文件或者数据库等。

package com.cfh.study.lucence_test6;

import org.apache.lucene.search.suggest.InputIterator;
import org.apache.lucene.util.BytesRef; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set; /**
* @Author: cfh
* @Date: 2018/9/17 10:21
* @Description: 这个类是核心,决定了你的索引是如何创建的,决定了最终返回的提示关键词列表数据及其排序
*/
public class ProductIterator implements InputIterator {
private Iterator<Product> productIterator;
private Product currentProduct; ProductIterator(Iterator<Product> productIterator) {
this.productIterator = productIterator;
} /**
* 设置是否启用Contexts域
* @return
*/
public boolean hasContexts() {
return true;
} /**
* 是否有设置payload信息
*/
public boolean hasPayloads() {
return true;
} public Comparator<BytesRef> getComparator() {
return null;
} /**
* next方法的返回值指定的其实就是就是可能返回给我们的suggest的值的结果集合(LookUpResult.key),这里我们选择了商品名。
*/
public BytesRef next() {
if (productIterator.hasNext()) {
currentProduct = productIterator.next();
try {
//返回当前Project的name值,把product类的name属性值作为key
return new BytesRef(currentProduct.getName().getBytes("UTF8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Couldn't convert to UTF-8",e);
}
} else {
return null;
}
} /**
* 将Product对象序列化存入payload
* [这里仅仅是个示例,其实这种做法不可取,一般不会把整个对象存入payload,这样索引体积会很大,浪费硬盘空间]
*/
public BytesRef payload() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(currentProduct);
out.close();
return new BytesRef(bos.toByteArray());
} catch (IOException e) {
throw new RuntimeException("Well that's unfortunate.");
}
} /**
* 把产品的销售区域存入context,context里可以是任意的自定义数据,一般用于数据过滤
* Set集合里的每一个元素都会被创建一个TermQuery,你只是提供一个Set集合,至于new TermQuery
* Lucene底层API去做了,但你必须要了解底层干了些什么
*/
public Set<BytesRef> contexts() {
try {
Set<BytesRef> regions = new HashSet<BytesRef>();
for (String region : currentProduct.getRegions()) {
regions.add(new BytesRef(region.getBytes("UTF8")));
}
return regions;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Couldn't convert to UTF-8");
}
} /**
* 返回权重值,这个值会影响排序
* 这里以产品的销售量作为权重值,weight值即最终返回的热词列表里每个热词的权重值
* 怎么设计返回这个权重值,发挥你们的想象力吧
*/
public long weight() {
return currentProduct.getNumberSold();
}
}

最后当然是测试suggest的结果啦,可以看到我们根据product的name进行了suggest并使用product的region域对suggest结果进行了过滤

private static void lookup(AnalyzingInfixSuggester suggester, String name,
String region) throws IOException {
HashSet<BytesRef> contexts = new HashSet<BytesRef>();
//先根据region域进行suggest再根据name域进行suggest
contexts.add(new BytesRef(region.getBytes("UTF8")));
//num决定了返回几条数据,参数四表明是否所有TermQuery是否都需要满足,参数五表明是否需要高亮显示
List<Lookup.LookupResult> results = suggester.lookup(name, contexts, 2, true, false);
System.out.println("-- \"" + name + "\" (" + region + "):");
for (Lookup.LookupResult result : results) {
System.out.println(result.key);//result.key中存储的是根据用户输入内部算法进行匹配后返回的suggest内容
//从载荷(payload)中反序列化出Product对象(实际生产中出于降低内存占用考虑一般不会在载荷中存储这么多内容)
BytesRef bytesRef = result.payload;
ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(bytesRef.bytes));
Product product = null;
try {
product = (Product)is.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("product-Name:" + product.getName());
System.out.println("product-regions:" + product.getRegions());
System.out.println("product-image:" + product.getImage());
System.out.println("product-numberSold:" + product.getNumberSold());
}
System.out.println();
}

当然也可以参考原博主的github:study-lucene

原文博主:https://blog.csdn.net/m0_37556444/article/details/82734959

lucene的suggest(搜索提示功能的实现)的更多相关文章

  1. solr入门之參考淘宝搜索提示功能优化拼音加汉字搜索功能

    首先看一下从淘宝输入搜索keyword获取到的一些数据信息: 第一张:使用拼音的全程来查询 能够看到提示的是匹配的转换的拼音的方式,看最后一个提示项 这里另一个在指定分类文件夹下搜索的功能,难道后台还 ...

  2. jquery实现百度类似搜索提示功能(AJAX应用)

    有时候觉得百度那个输入内容就有提示的工具很神奇,它究竟是怎么做到的呢?以前在一个进销存系统中也做过这么个功能,但是远远不及百度的功能强大,百度可以输入首字母,关键字拼音,或关键字都可以匹配,有时在想, ...

  3. jQuery&period;Autocomplete实现自动完成功能-搜索提示功能

    $(function(){ var availableTags=["ads","abc","acc"]; $("#tags&quo ...

  4. AutoCompleteTextView实现搜索提示功能的实现

    AutoCompleteTextView和EditText组件类似,都可以输入文本.但AutoCompleteTextView组件可以和一个字符串数组或List对象绑定,当用户输入两个及以上字符时,系 ...

  5. C&num; WinForm 技巧:COMBOBOX搜索提示

    comboBox和textBox支持内置的搜索提示功能, 在form的InitializeComponent()中添加如下语句:   this.comboBox1.AutoCompleteCustom ...

  6. 讨论asp&period;net通过机器cookie仿百度(google)实现搜索input搜索提示弹出框自己主动

    为实现自己主动弹出通过用户输入关键词相关的搜索结果,在这里,我举两个解决方案,对于两个不同的方案. 常用的方法是建立一个用户数据库中查找关系表.然后输入用户搜索框keyword异步调用数据表中的相关数 ...

  7. 浅谈asp&period;net通过本机cookie仿百度(google)实现搜索input框自己主动弹出搜索提示

    对于通过用户输入关键词实现自己主动弹出相关搜索结果,这里本人给两种解决方式,用于两种不同的情形. 常见方法是在数据库里建一个用户搜索关系表,然后通过用户搜索框输入的keyword异步调用数据表中的相关 ...

  8. Jquery实现搜索框提示功能

    博客的前某一篇文章中http://www.cnblogs.com/hEnius/p/2013-07-01.html写过一个用Ajax来实现一个文本框输入的提示功能.最近在一个管理项目的项目中,使用后发 ...

  9. JQuery&plus;AJAX实现搜索文本框的输入提示功能

    平时使用谷歌搜索的时候发现只要在文本框里输入部分单词或字母,下面马上会弹出一个相关信息的内容框可供选择.感觉这个功能有较好的用户体验,所以也想在自己的网站上加上这种输入提示框. 实现的原理其实很简单, ...

随机推荐

  1. ARM大学计划全球经理到访华清远见,深入交流教育合作

    来源:华清远见嵌入式学院 10月20日,ARM大学计划全球经理Khaled Benkrid,高级内容主编洪川博士在ARM大学计划亚太经理陈炜博士的陪同下到访华清远见,就最新嵌入式技术.ARM处理器在教 ...

  2. DataTable&period;Compute&lpar;&rpar;用法

    DataTable.Compute()用法 2010-04-07 11:28 一.DataTable.Compute()方法說明如下 作用:          计算用来传递筛选条件的当前行上的给定表达 ...

  3. Android学习笔记之SoftReference软引用&period;&period;&period;

    PS:其实这一篇和上一篇很类似,都是为了解决内存不足(OOM)这种情况的发生... 学习内容: 1.对象的引用类....   最近也是通过项目中知道了一些东西,涉及到了对象的引用类,对象的引用类分为多 ...

  4. JS Math 类库介绍

    下面介绍下随机生成数的常用几个API JS 随机数生成 : 在JavaScript , 提供了生成随机数的API, Math.random() 1.Math.random() : 随机生成小数 . 生 ...

  5. C&num;后台发送HTTP请求

    using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using Syst ...

  6. ado vb6

    http://www.cnblogs.com/ywb-lv/articles/2343444.html http://*.com/questions/3334102/use-t ...

  7. &lbrack;编织消息框架&rsqb;&lbrack;netty源码分析&rsqb;13 ByteBuf 实现类CompositeByteBuf职责与实现

    public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements Iterable<ByteBuf ...

  8. Spring 之BeanFactory&lpar;转&rpar;

    BeanFactory是Spring的“心脏”.它就是Spring IoC容器的真面目. Spring使用BeanFactory来实例化.配置和管理Bean.但是,在大多数情况我们并不直接使用Bean ...

  9. idea启动多个tomcat失败

    Intellij idea中,为在本地调试两个系统之间的调用,配置两个本地tomcat server,设置不同的端口号,如8081和8082,Deploy中加入两个系统各自的Artifact xxx: ...

  10. Solidity&lpar;address的四个方法&rpar;

    address的四个方法send,call,callcode,delegatecall 例子:发送以太币的send方法//下面是send方法,涉及到以太币的情况可能用到payable,senddemo ...