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.Autocomplete实现自动完成功能-搜索提示功能

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

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

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

  5. C# WinForm 技巧:COMBOBOX搜索提示

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

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

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

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

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

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

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

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

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

随机推荐

  1. [python]Generators

    generators(生成器)是python提供的一种机制,可以让函数一边循环一边计算,通常函数是一遍执行,而生成器可以在执行中间交出变量,下次调用时从交出变量的地方重新开始,这种机制通过yield关 ...

  2. python 对象转字典

    从数据库中取出的数数据是 对象类型的,不能直接展示出来,需要转成字典类型,然后转成json 字符串,传给前端: data = {} data.update(obj.__dict__) print(da ...

  3. mvn多环境下的配置

    在应用中,我们经常会遇到本地,测试和生产3种不同的环境,因此需要去配置不同的application. 定义resources: <resources> <resource> & ...

  4. MySQL中基于mysqldump和二进制日志log-bin进行逻辑备份以及基于时间点的还原

    本文出处:http://www.cnblogs.com/wy123/p/6956464.html 本文仅模拟使用mysqldump和log-bin二进制日志进行简单测试,仅作为个人学习笔记,可能离实际 ...

  5. tensorflow 1.9 ,bazel 0.15.0,源码编ERROR, Skipping, '//tensorflow/tools/pip_package:build_pip_package',error loading packageCuda Configuration Error, Cannot find libdevice.10.bc under /usr/local/cuda-8.0

    最近在看tensorflow 移动端部署,需要编译源码才支持,所以又拾起来了编译这项老工作,其中遇到问题: bazel build --cxxopt="-D_GLIBCXX_USE_CXX1 ...

  6. bazel build //tensorflow/examples/android:tensorflow_demo报错: fatal error: 'cuda_runtime.h' file not found

    In file included from ./third_party/eigen3/unsupported/Eigen/CXX11/Tensor:1:external/eigen_archive/u ...

  7. python入门(八):文件操作

    1.数据的保存: 1) 内存:常用的变量都是在内存里面的 缺点:关机或进程死掉数据丢失 解决方法:将数据保存至文件中 2 )文件:文本内容.二进制的文件内容 3 )数据库:保存    2.读文件: 1 ...

  8. OpenCV常用数据类型

    Point 二维点坐标(x,y) typedef Point3_<int> Point3i; typedef Point3_<float> Point3f; typedef P ...

  9. MariaDB ColumnStore初探(1):安装、使用及测试

    相信大家在对接BI数据报表部门有很深刻的体验,高大上的复杂SQL关联JOIN十几张表在InnoDB里跑起来,会让你酸爽到死.它的出现正是解决这个问题,DBA能不能轻松愉快地玩耍,就要靠它了,“神州行我 ...

  10. react组件回顶部

    在挂载更新里面判断滚动条的距离(滚动条不能overflow: auto 踩坑) componentDidMount(){ window.addEventListener('scroll' , ()=& ...