搜索引擎技术系列教材 (一)- lucene - Lucene7 入门


步骤 1 : 关于JDK版本

至少使用JDK8版本,请下载JDK8或者更高版本: 下载以及配置JDK环境

步骤 2 : Lucene 概念

Lucene 这个开源项目,使得 Java开发人员可以很方便地得到像搜索引擎google baidu那样的搜索效果。

步骤 3 : 先运行,看到效果,再学习

老规矩,先下载下载区(点击进入)的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。 
运行TestLucene类,期望看到如图所示的效果。
一共是10条数据,通过关键字查询出来6条命中结果,不同的命中结果有不同的匹配度得分,比如第一条,命中都就很高,既有 护眼, 也有 带光源。 其他的命中度就比较低,没有护眼关键字的匹配,只有光源关键字的匹配。

步骤 4 : 模仿和排错

在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。 
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。 

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。 
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来 
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程

步骤 5 : Lucene 版本

当前使用的Lucene版本是截至2018.3.9最新版本 7.2.1

步骤 6 : jar 包

一系列需要的jar包都放在项目里了,直接使用就好了,包括兼容 lucene 7.2.1 的中文分词器

步骤 7 : TestLucene.java

这是TestLucene.java 的完整代码,后续会对代码详细讲解

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.ArrayList;

import java.util.List;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

 

        // 2. 索引

        List<String> productNames = new ArrayList<>();

        productNames.add("飞利浦led灯泡e27螺口暖白球泡灯家用照明超亮节能灯泡转色温灯泡");

        productNames.add("飞利浦led灯泡e14螺口蜡烛灯泡3W尖泡拉尾节能灯泡暖黄光源Lamp");

        productNames.add("雷士照明 LED灯泡 e27大螺口节能灯3W球泡灯 Lamp led节能灯泡");

        productNames.add("飞利浦 led灯泡 e27螺口家用3w暖白球泡灯节能灯5W灯泡LED单灯7w");

        productNames.add("飞利浦led小球泡e14螺口4.5w透明款led节能灯泡照明光源lamp单灯");

        productNames.add("飞利浦蒲公英护眼台灯工作学习阅读节能灯具30508带光源");

        productNames.add("欧普照明led灯泡蜡烛节能灯泡e14螺口球泡灯超亮照明单灯光源");

        productNames.add("欧普照明led灯泡节能灯泡超亮光源e14e27螺旋螺口小球泡暖黄家用");

        productNames.add("聚欧普照明led灯泡节能灯泡e27螺口球泡家用led照明单灯超亮光源");    

        Directory index = createIndex(analyzer, productNames);

 

        // 3. 查询器

        String keyword = "护眼带光源";

        Query query = new QueryParser("name", analyzer).parse(keyword);

         

        // 4. 搜索

        IndexReader reader = DirectoryReader.open(index);

        IndexSearcher searcher = new IndexSearcher(reader);

        int numberPerPage = 1000;

        System.out.printf("当前一共有%d条数据%n",productNames.size());

        System.out.printf("查询关键字是:\"%s\"%n",keyword);

        ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

 

        // 5. 显示查询结果

        showSearchResults(searcher, hits, query, analyzer);

        // 6. 关闭查询

        reader.close();

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)

            throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields = d.getFields();

            System.out.print((i + 1));

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

                System.out.print("\t" + d.get(f.name()));

            }

            System.out.println();

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

 

        for (String name : products) {

            addDoc(writer, name);

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, String name) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("name", name, Field.Store.YES));

        w.addDocument(doc);

    }

}

步骤 8 : 分词器

准备中文分词器,关于分词器更多概念在分词器概念 中有详细讲解,这里先使用

// 1. 准备中文分词器

IKAnalyzer analyzer = new IKAnalyzer();

步骤 9 : 创建索引

1. 首先准备10条数据
这10条数据都是字符串,相当于产品表里的数据
2. 通过createIndex方法,把它加入到索引当中

创建内存索引,为什么Lucene会比数据库快?因为它是从内存里查,自然就比数据库里快多了呀

Directory index = new RAMDirectory();


根据中文分词器创建配置对象

IndexWriterConfig config = new IndexWriterConfig(analyzer);


创建索引 writer

IndexWriter writer = new IndexWriter(index, config);


遍历那10条数据,把他们挨个放进索引里

for (String name : products) {

    addDoc(writer, name);

}


每条数据创建一个Document,并把这个Document放进索引里。 这个Document有一个字段,叫做"name"。 TestLucene.java 第49行创建查询器,就会指定查询这个字段

private static void addDoc(IndexWriter w, String name) throws IOException {

    Document doc = new Document();

    doc.add(new TextField("name", name, Field.Store.YES));

    w.addDocument(doc);

}

  • 准备数据
  • createIndex方法
  • addDoc方法

// 2. 索引

List<String> productNames = new ArrayList<>();

productNames.add("飞利浦led灯泡e27螺口暖白球泡灯家用照明超亮节能灯泡转色温灯泡");

productNames.add("飞利浦led灯泡e14螺口蜡烛灯泡3W尖泡拉尾节能灯泡暖黄光源Lamp");

productNames.add("雷士照明 LED灯泡 e27大螺口节能灯3W球泡灯 Lamp led节能灯泡");

productNames.add("飞利浦 led灯泡 e27螺口家用3w暖白球泡灯节能灯5W灯泡LED单灯7w");

productNames.add("飞利浦led小球泡e14螺口4.5w透明款led节能灯泡照明光源lamp单灯");

productNames.add("飞利浦蒲公英护眼台灯工作学习阅读节能灯具30508带光源");

productNames.add("欧普照明led灯泡蜡烛节能灯泡e14螺口球泡灯超亮照明单灯光源");

productNames.add("欧普照明led灯泡节能灯泡超亮光源e14e27螺旋螺口小球泡暖黄家用");

productNames.add("聚欧普照明led灯泡节能灯泡e27螺口球泡家用led照明单灯超亮光源");    

Directory index = createIndex(analyzer, productNames);

private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {

    Directory index = new RAMDirectory();

    IndexWriterConfig config = new IndexWriterConfig(analyzer);

    IndexWriter writer = new IndexWriter(index, config);

 

    for (String name : products) {

        addDoc(writer, name);

    }

    writer.close();

    return index;

}

private static void addDoc(IndexWriter w, String name) throws IOException {

    Document doc = new Document();

    doc.add(new TextField("name", name, Field.Store.YES));

    w.addDocument(doc);

}

步骤 10 : 创建查询器

根据关键字 护眼带光源,基于 "name" 字段进行查询。 这个 "name" 字段就是在创建索引步骤里每个Document的 "name" 字段,相当于表的字段名

String keyword = "护眼带光源";

Query query = new QueryParser("name", analyzer).parse(keyword);

步骤 11 : 执行搜索

接着就执行搜索:
创建索引 reader:

IndexReader reader = DirectoryReader.open(index);


基于 reader 创建搜索器:

IndexSearcher searcher = new IndexSearcher(reader);


指定每页要显示多少条数据:

int numberPerPage = 1000;


执行搜索

ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

// 4. 搜索

IndexReader reader = DirectoryReader.open(index);

IndexSearcher searcher = new IndexSearcher(reader);

int numberPerPage = 1000;

System.out.printf("当前一共有%d条数据%n",productNames.size());

System.out.printf("查询关键字是:\"%s\"%n",keyword);

ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

步骤 12 : 显示查询结果

每一个ScoreDoc[] hits 就是一个搜索结果,首先把他遍历出来

for (int i = 0; i < hits.length; ++i) {

ScoreDoc scoreDoc= hits[i];


然后获取当前结果的docid, 这个docid相当于就是这个数据在索引中的主键

int docId = scoreDoc.doc;


再根据主键docid,通过搜索器从索引里把对应的Document取出来

Document d = searcher.doc(docId);


接着就打印出这个Document里面的数据。 虽然当前Document只有name一个字段,但是代码还是通过遍历所有字段的形式,打印出里面的值,这样当Docment有多个字段的时候,代码就不用修改了,兼容性更好点。
scoreDoc.score 表示当前命中的匹配度得分,越高表示匹配程度越高

List<IndexableField> fields = d.getFields();

System.out.print((i + 1));

System.out.print("\t" + scoreDoc.score);

for (IndexableField f : fields) {

        System.out.print("\t" + d.get(f.name()));

}

private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)

        throws Exception {

    System.out.println("找到 " + hits.length + " 个命中.");

    System.out.println("序号\t匹配度得分\t结果");

    for (int i = 0; i < hits.length; ++i) {

        ScoreDoc scoreDoc= hits[i];

        int docId = scoreDoc.doc;

        Document d = searcher.doc(docId);

        List<IndexableField> fields = d.getFields();

        System.out.print((i + 1));

        System.out.print("\t" + scoreDoc.score);

        for (IndexableField f : fields) {

            System.out.print("\t" + d.get(f.name()));

        }

        System.out.println();

    }

}

步骤 13 : 运行结果

如图所示,一共是10条数据,通过关键字查询出来6条命中结果,不同的命中结果有不同的匹配度得分,比如第一条,命中都就很高,既有 护眼, 也有 带光源。 其他的命中度就比较低,没有护眼关键字的匹配,只有光源关键字的匹配。

步骤 14 : 和 like 的区别

like 也可以进行查询,那么使用lucene 的方式有什么区别呢? 主要是两点:
1. 相关度
通过观察运行结果,可以看到不同相关度的结果都会查询出来,但是使用 like,就做不到这一点了
2. 性能
数据量小的时候,like 也会有很好的表现,但是数据量一大,like 的表现就差很多了。 在接下来的教程里会演示对 14万条数据 的查询

步骤 15 : 思路图

现在通过自己做了一遍 Lucene了,有了感性的认识,接着来整理一下做 Lucene的思路。
1. 首先搜集数据
数据可以是文件系统,数据库,网络上,手工输入的,或者像本例直接写在内存上的
2. 通过数据创建索引
3. 用户输入关键字
4. 通过关键字创建查询器
5. 根据查询器到索引里获取数据
6. 然后把查询结果展示在用户面前

搜索引擎技术系列教材 (二)- lucene - Lucene7可以用的中文分词器 IKAnalyzer6.5.0.jar

更多内容,点击了解: https://how2j.cn/k/search-engine/search-engine-analyzer/1673.html


 

步骤 1 : 分词器概念

分词器指的是搜索引擎如何使用关键字进行匹配,如 入门 中的关键字:护眼带光源。 如果使用like,那么%护眼带光源%,匹配出来的结果就是要么全匹配,要不都不匹配。
而使用分词器,就会把这个关键字分为 护眼,带,光源 3个关键字,这样就可以找到不同相关程度的结果了。

步骤 2 : IKAnalyzer6.5.0.jar

IKAnalyzer 这个分词器很久都没有维护了,也不支持Lucene7。 IKAnalyzer6.5.0.jar 这个是修改之后的的,可以支持Lucene7的jar, 在下载区(点击进入)下载(已经包含在右上角的lucene.rar中了)。

步骤 3 : 代码演示 TestAnalyzer

如代码所示,使用IKAnalyzer 中文分词器就会把 护眼带光源 分为这么如图所示的3个小关键字进行匹配

package com.how2java;

 

import java.io.IOException;

 

import org.apache.lucene.analysis.TokenStream;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestAnalyzer {

 

    public static void main(String[] args) throws IOException {

         

        IKAnalyzer analyzer = new IKAnalyzer();

        TokenStream ts= analyzer.tokenStream("name", "护眼带光源");

        ts.reset();

        while(ts.incrementToken()){

            System.out.println(ts.reflectAsString(false));

        }

    }

}

搜索引擎技术系列教材 (三)- lucene - 高亮显示


更多内容,点击了解: https://how2j.cn/k/search-engine/search-engine-hightlight/1677.html

步骤 1 : 先运行,看到效果,再学习

老规矩,先下载下载区(点击进入)的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。 
如图所示,查询结果里会高亮标记关键字。第一条是匹配度最高的,所有分词都匹配到了,其他的只匹配了少部分分词。

步骤 2 : 模仿和排错

在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。 
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。 

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。 
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来 
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程

步骤 3 : TestLucene

70,71行 81,82行 增加高亮显示

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.ArrayList;

import java.util.List;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

 

        // 2. 索引

        List<String> productNames = new ArrayList<>();

        productNames.add("飞利浦led灯泡e27螺口暖白球泡灯家用照明超亮节能灯泡转色温灯泡");

        productNames.add("飞利浦led灯泡e14螺口蜡烛灯泡3W尖泡拉尾节能灯泡暖黄光源Lamp");

        productNames.add("雷士照明 LED灯泡 e27大螺口节能灯3W球泡灯 Lamp led节能灯泡");

        productNames.add("飞利浦 led灯泡 e27螺口家用3w暖白球泡灯节能灯5W灯泡LED单灯7w");

        productNames.add("飞利浦led小球泡e14螺口4.5w透明款led节能灯泡照明光源lamp单灯");

        productNames.add("飞利浦蒲公英护眼台灯工作学习阅读节能灯具30508带光源");

        productNames.add("欧普照明led灯泡蜡烛节能灯泡e14螺口球泡灯超亮照明单灯光源");

        productNames.add("欧普照明led灯泡节能灯泡超亮光源e14e27螺旋螺口小球泡暖黄家用");

        productNames.add("聚欧普照明led灯泡节能灯泡e27螺口球泡家用led照明单灯超亮光源");    

        Directory index = createIndex(analyzer, productNames);

 

        // 3. 查询器

        String keyword = "护眼带光源";

        Query query = new QueryParser("name", analyzer).parse(keyword);

         

        // 4. 搜索

        IndexReader reader = DirectoryReader.open(index);

        IndexSearcher searcher = new IndexSearcher(reader);

        int numberPerPage = 1000;

        System.out.printf("当前一共有%d条数据%n",productNames.size());

        System.out.printf("查询关键字是:\"%s\"%n",keyword);

        ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

 

        // 5. 显示查询结果

        showSearchResults(searcher, hits, query, analyzer);

        // 6. 关闭查询

        reader.close();

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)

            throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields = d.getFields();

            System.out.print((i + 1));

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

                TokenStream tokenStream = analyzer.tokenStream(f.name(), new StringReader(d.get(f.name())));

                String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));               

                System.out.print("\t" + fieldContent);

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

 

        for (String name : products) {

            addDoc(writer, name);

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, String name) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("name", name, Field.Store.YES));

        w.addDocument(doc);

    }

}

步骤 4 : 运行结果

运行结果是html代码,为了正常显示,复制到一个html文件里,打开就可以看到效果了

搜索引擎技术系列教材 (四)- lucene - 向Lucene中导入14万条产品数据

更多内容,点击了解: https://how2j.cn/k/search-engine/search-engine-14k/1674.html


 

步骤 1 : 14万条数据

在前面的 入门 里是用了10条记录来测试,实际情况肯定是不会只有10条记录了,所以为了模仿真实环境,花了很多精力,四处搜刮来了14万条天猫的产品数据,接下来我们就会把这14万条记录加入到 Lucene,然后观察搜索效果。
这14万条记录放在下载区(点击进入) 140k_products.rar,其解析办法在后续会讲解

步骤 2 : 关于数据库

本来应该先把这14万条记录保存进数据库,然后再从数据库中取出来的,不过考虑到不是每个同学都有JDBC基础,以及放进数据库的繁琐,和14万条数据从数据库里读取出来的时间消耗,就改成直接从文件里读取出来,然后转换为泛型是Product的集合的形式,相当于从数据库里读取出来了,不过会快很多。
有兴趣的同学可以自己把这些数据放进数据库里,并且使用 like 的方式看看查询性能如何。

步骤 3 : 先运行,看到效果,再学习

老规矩,先下载下载区(点击进入)的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。 
执行TestLuceue之后,会花大概20秒左右时间为这14万条记录建立索引,然后输入不同的关键字查询出不同的结果来。

步骤 4 : 模仿和排错

在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。 
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。 

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。 
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来 
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程

步骤 5 : 140k_products.txt

首先下载 140k_products.rar,并解压为140k_products.txt, 然后放在项目目录下。 这个文件里一共有14万条产品记录。

步骤 6 : Product.java

准备实体类来存放产品信息

package com.how2java;

 

public class Product {

 

    int id;

    String name;

    String category;

    float price;

    String place;

 

    String code;

    public int getId() {

        return id;

    }

    public void setId(int id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public String getCategory() {

        return category;

    }

    public void setCategory(String category) {

        this.category = category;

    }

    public float getPrice() {

        return price;

    }

    public void setPrice(float price) {

        this.price = price;

    }

    public String getPlace() {

        return place;

    }

    public void setPlace(String place) {

        this.place = place;

    }

 

    public String getCode() {

        return code;

    }

    public void setCode(String code) {

        this.code = code;

    }

    @Override

    public String toString() {

        return "Product [id=" + id + ", name=" + name + ", category=" + category + ", price=" + price + ", place="

                + place + ", code=" + code + "]";

    }

 

}

步骤 7 : ProductUtil.java

准备工具类,把140k_products.txt 文本文件,转换为泛型是Product的集合

package com.how2java;

 

import java.awt.AWTException;

import java.io.File;

import java.io.IOException;

import java.util.ArrayList;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

 

import org.apache.commons.io.FileUtils;

     

public class ProductUtil {

     

    public static void main(String[] args) throws IOException, InterruptedException, AWTException {

 

        String fileName = "140k_products.txt";

         

        List<Product> products = file2list(fileName);

         

        System.out.println(products.size());

             

    }

 

    public static List<Product> file2list(String fileName) throws IOException {

        File f = new File(fileName);

        List<String> lines = FileUtils.readLines(f,"UTF-8");

        List<Product> products = new ArrayList<>();

        for (String line : lines) {

            Product p = line2product(line);

            products.add(p);

        }

        return products;

    }

     

    private static Product line2product(String line) {

        Product p = new Product();

        String[] fields = line.split(",");

        p.setId(Integer.parseInt(fields[0]));

        p.setName(fields[1]);

        p.setCategory(fields[2]);

        p.setPrice(Float.parseFloat(fields[3]));

        p.setPlace(fields[4]);

        p.setCode(fields[5]);

        return p;

    }

 

}

步骤 8 : TestLucene.java

在入门中 TestLucene.java 的基础上进行修改。 主要做了两个方面的修改:
1. 索引的增加,以前是10条数据,现在是14万条数据
注: 因为数据量比较大, 所以加入到索引的时间也比较久,请耐心等待。
2. Document以前只有name字段,现在有6个字段
3. 查询关键字从控制台输入,这样每次都可以输入不同的关键字进行查询。 因为索引建立时间比较久,采用这种方式,可以建立一次索引,进行多次查询,否则的话,每次使用不同的关键字,都要耗时建立索引,测试效率会比较低

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.List;

import java.util.Scanner;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

        // 2. 索引

        Directory index = createIndex(analyzer);

 

        // 3. 查询器

         

        Scanner s = new Scanner(System.in);

         

        while(true){

            System.out.print("请输入查询关键字:");

            String keyword = s.nextLine();

            System.out.println("当前关键字是:"+keyword);

            Query query = new QueryParser( "name", analyzer).parse(keyword);

 

            // 4. 搜索

            IndexReader reader = DirectoryReader.open(index);

            IndexSearcher searcher=new IndexSearcher(reader);

            int numberPerPage = 10;

            ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

             

            // 5. 显示查询结果

            showSearchResults(searcher, hits,query,analyzer);

            // 6. 关闭查询

            reader.close();

        }

         

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields= d.getFields();

            System.out.print((i + 1) );

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

 

                if("name".equals(f.name())){

                    TokenStream tokenStream = analyzer.tokenStream(f.name(), newStringReader(d.get(f.name())));

                    String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));

                    System.out.print("\t"+fieldContent);

                }

                else{

                    System.out.print("\t"+d.get(f.name()));

                }

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

        String fileName = "140k_products.txt";

        List<Product> products = ProductUtil.file2list(fileName);

        int total = products.size();

        int count = 0;

        int per = 0;

        int oldPer =0;

        for (Product p : products) {

            addDoc(writer, p);

            count++;

            per = count*100/total;

            if(per!=oldPer){

                oldPer = per;

                System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);

            }

             

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, Product p) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));

        doc.add(new TextField("name", p.getName(), Field.Store.YES));

        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));

        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));

        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));

        doc.add(new TextField("code", p.getCode(), Field.Store.YES));

        w.addDocument(doc);

    }

}

搜索引擎技术系列教材 (五)- lucene - 分页查询

更多内容,点击了解: https://how2j.cn/k/search-engine/search-engine-page/1675.html


 

步骤 1 : 先运行,看到效果,再学习

老规矩,先下载下载区(点击进入)的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。

步骤 2 : 模仿和排错

在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。 
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。 

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。 
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来 
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程

步骤 3 : 两种方式

分页查询是很常见的需求,比如要查询第10页,每页10条数据。
Lucene 分页通常来讲有两种方式:
第一种是把100条数据查出来,然后取最后10条。 优点是快,缺点是对内存消耗大。
第二种是把第90条查询出来,然后基于这一条,通过searchAfter方法查询10条数据。 优点是内存消耗小,缺点是比第一种更慢

步骤 4 : 第一种

private static ScoreDoc[] pageSearch1(Query query, IndexSearcher searcher, int pageNow, int pageSize)

        throws IOException {

    TopDocs topDocs = searcher.search(query, pageNow*pageSize);

     System.out.println("查询到的总条数\t"+topDocs.totalHits);

     ScoreDoc [] alllScores = topDocs.scoreDocs;

 

     List<ScoreDoc> hitScores = new ArrayList<>();

     

      

     int start = (pageNow -1)*pageSize ;

     int end = pageSize*pageNow;

     for(int i=start;i<end;i++)

         hitScores.add(alllScores[i]);

     

     

     ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[]{});

    return hits;

}


一共查出 pageNow*pageSize条,然后取最后pageSize条

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.ArrayList;

import java.util.List;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.TopDocs;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

        // 2. 索引

        Directory index = createIndex(analyzer);

 

        // 3. 查询器

         

            String keyword = "手机";

            System.out.println("当前关键字是:"+keyword);

            Query query = new QueryParser( "name", analyzer).parse(keyword);

 

            // 4. 搜索

            IndexReader reader = DirectoryReader.open(index);

            IndexSearcher searcher=new IndexSearcher(reader);

            int pageNow = 1;

            int pageSize = 10;

             

            ScoreDoc[] hits = pageSearch1(query, searcher, pageNow, pageSize);

             

            // 5. 显示查询结果

            showSearchResults(searcher, hits,query,analyzer);

            // 6. 关闭查询

            reader.close();

         

    }

 

    private static ScoreDoc[] pageSearch1(Query query, IndexSearcher searcher, int pageNow, int pageSize)

            throws IOException {

        TopDocs topDocs = searcher.search(query, pageNow*pageSize);

         System.out.println("查询到的总条数\t"+topDocs.totalHits);

         ScoreDoc [] alllScores = topDocs.scoreDocs;

 

         List<ScoreDoc> hitScores = new ArrayList<>();

         

         int start = (pageNow -1)*pageSize ;

         int end = pageSize*pageNow;

         for(int i=start;i<end;i++)

             hitScores.add(alllScores[i]);

         

         ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[]{});

        return hits;

    }

     

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields= d.getFields();

            System.out.print((i + 1) );

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

 

                if("name".equals(f.name())){

                    TokenStream tokenStream = analyzer.tokenStream(f.name(), newStringReader(d.get(f.name())));

                    String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));

                    System.out.print("\t"+fieldContent);

                }

                else{

                    System.out.print("\t"+d.get(f.name()));

                }

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

        String fileName = "140k_products.txt";

        List<Product> products = ProductUtil.file2list(fileName);

        int total = products.size();

        int count = 0;

        int per = 0;

        int oldPer =0;

        for (Product p : products) {

            addDoc(writer, p);

            count++;

            per = count*100/total;

            if(per!=oldPer){

                oldPer = per;

                System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);

            }

             

            if(per>10)

                break;

             

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, Product p) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));

        doc.add(new TextField("name", p.getName(), Field.Store.YES));

        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));

        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));

        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));

        doc.add(new TextField("code", p.getCode(), Field.Store.YES));

        w.addDocument(doc);

    }

}

步骤 5 : 第二种

private static ScoreDoc[] pageSearch2(Query query, IndexSearcher searcher, int pageNow, int pageSize)

            throws IOException {

        int start = (pageNow - 1) * pageSize;

        if(0==start){

            TopDocs topDocs = searcher.search(query, pageNow*pageSize);

            return topDocs.scoreDocs;

        }

        // 查询数据, 结束页面自前的数据都会查询到,但是只取本页的数据

        TopDocs topDocs = searcher.search(query, start);

        //获取到上一页最后一条

         

        ScoreDoc preScore= topDocs.scoreDocs[start-1];

 

        //查询最后一条后的数据的一页数据

        topDocs = searcher.searchAfter(preScore, query, pageSize);

        return topDocs.scoreDocs;

         

    }


首先是边界条件,如果是第一页,就直接查询了。
如果不是第一页,那么就取start-1那一条,然后再根据它通过searchAfter 来查询

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.ArrayList;

import java.util.List;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.TopDocs;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

        // 2. 索引

        Directory index = createIndex(analyzer);

 

        // 3. 查询器

         

            String keyword = "手机";

            System.out.println("当前关键字是:"+keyword);

            Query query = new QueryParser( "name", analyzer).parse(keyword);

 

            // 4. 搜索

            IndexReader reader = DirectoryReader.open(index);

            IndexSearcher searcher=new IndexSearcher(reader);

            int pageNow = 1;

            int pageSize = 10;

             

            ScoreDoc[] hits = pageSearch2(query, searcher, pageNow, pageSize);

             

            // 5. 显示查询结果

            showSearchResults(searcher, hits,query,analyzer);

            // 6. 关闭查询

            reader.close();

         

    }

 

    private static ScoreDoc[] pageSearch1(Query query, IndexSearcher searcher, int pageNow, int pageSize)

            throws IOException {

        TopDocs topDocs = searcher.search(query, pageNow*pageSize);

         System.out.println("查询到的总条数\t"+topDocs.totalHits);

         ScoreDoc [] alllScores = topDocs.scoreDocs;

 

         List<ScoreDoc> hitScores = new ArrayList<>();

         

         int start = (pageNow -1)*pageSize ;

         int end = pageSize*pageNow;

         for(int i=start;i<end;i++)

             hitScores.add(alllScores[i]);

         

         ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[]{});

        return hits;

    }

     

    private static ScoreDoc[] pageSearch2(Query query, IndexSearcher searcher, int pageNow, int pageSize)

            throws IOException {

         

        int start = (pageNow - 1) * pageSize;

        if(0==start){

            TopDocs topDocs = searcher.search(query, pageNow*pageSize);

            return topDocs.scoreDocs;

        }

        // 查询数据, 结束页面自前的数据都会查询到,但是只取本页的数据

        TopDocs topDocs = searcher.search(query, start);

        //获取到上一页最后一条

         

        ScoreDoc preScore= topDocs.scoreDocs[start-1];

 

        //查询最后一条后的数据的一页数据

        topDocs = searcher.searchAfter(preScore, query, pageSize);

        return topDocs.scoreDocs;

         

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields= d.getFields();

            System.out.print((i + 1) );

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

 

                if("name".equals(f.name())){

                    TokenStream tokenStream = analyzer.tokenStream(f.name(), newStringReader(d.get(f.name())));

                    String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));

                    System.out.print("\t"+fieldContent);

                }

                else{

                    System.out.print("\t"+d.get(f.name()));

                }

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

        String fileName = "140k_products.txt";

        List<Product> products = ProductUtil.file2list(fileName);

        int total = products.size();

        int count = 0;

        int per = 0;

        int oldPer =0;

        for (Product p : products) {

            addDoc(writer, p);

            count++;

            per = count*100/total;

            if(per!=oldPer){

                oldPer = per;

                System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);

            }

             

            if(per>10)

                break;

             

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, Product p) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));

        doc.add(new TextField("name", p.getName(), Field.Store.YES));

        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));

        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));

        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));

        doc.add(new TextField("code", p.getCode(), Field.Store.YES));

        w.addDocument(doc);

    }

}

搜索引擎技术系列教材 (六)- lucene - 索引删除和更新

更多内容,点击了解: https://how2j.cn/k/search-engine/search-engine-delete-update/1676.html


 

步骤 1 : 索引删除和更新

索引建立好了之后,还是需要维护的,比如新增,删除和维护。 新增就是建立索引的过程,这里就不表了,本教材主要讲索引的删除和更新。
索引里的数据,其实就是一个一个的Document 对象,那么本文就是介绍如何删除和更新这些Documen对象。

步骤 2 : 先说没有删除前的情形

直接使用14万条数据 里的代码,不过使用不一样的查询语句。
如图所示,通过关键字 “鞭" 可以查询到一条id是51173的数据。

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.List;

import java.util.Scanner;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.index.Term;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

        // 2. 索引

        Directory index = createIndex(analyzer);

 

        // 3. 查询器

         

        Scanner s = new Scanner(System.in);

         

        while(true){

            System.out.print("请输入查询关键字:");

            String keyword = s.nextLine();

            System.out.println("当前关键字是:"+keyword);

            Query query = new QueryParser( "name", analyzer).parse(keyword);

 

            // 4. 搜索

            IndexReader reader = DirectoryReader.open(index);

            IndexSearcher searcher=new IndexSearcher(reader);

            int numberPerPage = 10;

            ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

             

            // 5. 显示查询结果

            showSearchResults(searcher, hits,query,analyzer);

            // 6. 关闭查询

            reader.close();

        }

         

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields= d.getFields();

            System.out.print((i + 1) );

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

 

                if("name".equals(f.name())){

                    TokenStream tokenStream = analyzer.tokenStream(f.name(), newStringReader(d.get(f.name())));

                    String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));

                    System.out.print("\t"+fieldContent);

                }

                else{

                    System.out.print("\t"+d.get(f.name()));

                }

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

        String fileName = "140k_products.txt";

        List<Product> products = ProductUtil.file2list(fileName);

        int total = products.size();

        int count = 0;

        int per = 0;

        int oldPer =0;

        for (Product p : products) {

            addDoc(writer, p);

            count++;

            per = count*100/total;

            if(per!=oldPer){

                oldPer = per;

                System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);

            }

             

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, Product p) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));

        doc.add(new TextField("name", p.getName(), Field.Store.YES));

        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));

        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));

        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));

        doc.add(new TextField("code", p.getCode(), Field.Store.YES));

        w.addDocument(doc);

    }

}

步骤 3 : 删除索引

删除id=51173的Document之后,如图所示,再搜索鞭字,就查询不到结果了。
删除关键代码如下,通过 Term对象删除

//删除id=51173的数据

IndexWriterConfig config = new IndexWriterConfig(analyzer);

IndexWriter indexWriter = new IndexWriter(index, config);

indexWriter.deleteDocuments(new Term("id", "51173"));

indexWriter.commit();

indexWriter.close();

  • 代码片段
  • TestLucene.java

//删除id=51173的数据

IndexWriterConfig config = new IndexWriterConfig(analyzer);

IndexWriter indexWriter = new IndexWriter(index, config);

indexWriter.deleteDocuments(new Term("id", "51173"));

indexWriter.commit();

indexWriter.close();

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.List;

import java.util.Scanner;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.index.Term;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

        // 2. 索引

        Directory index = createIndex(analyzer);

 

        // 3. 查询器

         

        Scanner s = new Scanner(System.in);

         

        //删除id=51173的数据

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter indexWriter = new IndexWriter(index, config);

        indexWriter.deleteDocuments(new Term("id", "51173"));

        indexWriter.commit();

        indexWriter.close();

         

        while(true){

            System.out.print("请输入查询关键字:");

            String keyword = s.nextLine();

            System.out.println("当前关键字是:"+keyword);

            Query query = new QueryParser( "name", analyzer).parse(keyword);

 

            // 4. 搜索

            IndexReader reader = DirectoryReader.open(index);

            IndexSearcher searcher=new IndexSearcher(reader);

            int numberPerPage = 10;

            ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

             

            // 5. 显示查询结果

            showSearchResults(searcher, hits,query,analyzer);

            // 6. 关闭查询

            reader.close();

        }

         

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields= d.getFields();

            System.out.print((i + 1) );

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

 

                if("name".equals(f.name())){

                    TokenStream tokenStream = analyzer.tokenStream(f.name(), newStringReader(d.get(f.name())));

                    String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));

                    System.out.print("\t"+fieldContent);

                }

                else{

                    System.out.print("\t"+d.get(f.name()));

                }

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

        String fileName = "140k_products.txt";

        List<Product> products = ProductUtil.file2list(fileName);

        int total = products.size();

        int count = 0;

        int per = 0;

        int oldPer =0;

        for (Product p : products) {

            addDoc(writer, p);

            count++;

            per = count*100/total;

            if(per!=oldPer){

                oldPer = per;

                System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);

            }

             

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, Product p) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));

        doc.add(new TextField("name", p.getName(), Field.Store.YES));

        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));

        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));

        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));

        doc.add(new TextField("code", p.getCode(), Field.Store.YES));

        w.addDocument(doc);

    }

}

步骤 4 : 更多删除

还可以按照如下方法来删除索引,API 很明显,就不做代码示例了

DeleteDocuments(Query query):根据Query条件来删除单个或多个Document

DeleteDocuments(Query[] queries):根据Query条件来删除单个或多个Document

DeleteDocuments(Term term):根据Term来删除单个或多个Document

DeleteDocuments(Term[] terms):根据Term来删除单个或多个Document

DeleteAll():删除所有的Document

步骤 5 : 更新索引

如图所示,更新索引后,再用鞭查询,得到的结果是查出了更新之后的数据。 更新的关键代码:

// 更新索引

IndexWriterConfig config = new IndexWriterConfig(analyzer);

IndexWriter indexWriter = new IndexWriter(index, config);

Document doc = new Document();

doc.add(new TextField("id", "51173", Field.Store.YES));

doc.add(new TextField("name", "神鞭,鞭没了,神还在", Field.Store.YES));

doc.add(new TextField("category", "道具", Field.Store.YES));

doc.add(new TextField("price", "998", Field.Store.YES));

doc.add(new TextField("place", "南海群岛", Field.Store.YES));

doc.add(new TextField("code", "888888", Field.Store.YES));

 

indexWriter.updateDocument(new Term("id", "51173"), doc );

indexWriter.commit();

indexWriter.close();

package com.how2java;

 

import java.io.IOException;

import java.io.StringReader;

import java.util.List;

import java.util.Scanner;

 

import org.apache.lucene.analysis.TokenStream;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.TextField;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

import org.apache.lucene.index.IndexableField;

import org.apache.lucene.index.Term;

import org.apache.lucene.queryparser.classic.QueryParser;

import org.apache.lucene.search.IndexSearcher;

import org.apache.lucene.search.Query;

import org.apache.lucene.search.ScoreDoc;

import org.apache.lucene.search.highlight.Highlighter;

import org.apache.lucene.search.highlight.QueryScorer;

import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.wltea.analyzer.lucene.IKAnalyzer;

 

public class TestLucene {

 

    public static void main(String[] args) throws Exception {

        // 1. 准备中文分词器

        IKAnalyzer analyzer = new IKAnalyzer();

        // 2. 索引

        Directory index = createIndex(analyzer);

 

        // 3. 查询器

         

        // 更新索引

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter indexWriter = new IndexWriter(index, config);

        Document doc = new Document();

        doc.add(new TextField("id", "51173", Field.Store.YES));

        doc.add(new TextField("name", "神鞭,鞭没了,神还在", Field.Store.YES));

        doc.add(new TextField("category", "道具", Field.Store.YES));

        doc.add(new TextField("price", "998", Field.Store.YES));

        doc.add(new TextField("place", "南海群岛", Field.Store.YES));

        doc.add(new TextField("code", "888888", Field.Store.YES));

         

        indexWriter.updateDocument(new Term("id", "51173"), doc );

        indexWriter.commit();

        indexWriter.close();

         

        Scanner s = new Scanner(System.in);

        while(true){

            System.out.print("请输入查询关键字:");

            String keyword = s.nextLine();

            System.out.println("当前关键字是:"+keyword);

            Query query = new QueryParser( "name", analyzer).parse(keyword);

 

            // 4. 搜索

            IndexReader reader = DirectoryReader.open(index);

            IndexSearcher searcher=new IndexSearcher(reader);

            int numberPerPage = 10;

            ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;

             

            // 5. 显示查询结果

            showSearchResults(searcher, hits,query,analyzer);

            // 6. 关闭查询

            reader.close();

        }

         

    }

 

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer) throws Exception {

        System.out.println("找到 " + hits.length + " 个命中.");

 

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");

        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

 

        System.out.println("找到 " + hits.length + " 个命中.");

        System.out.println("序号\t匹配度得分\t结果");

        for (int i = 0; i < hits.length; ++i) {

            ScoreDoc scoreDoc= hits[i];

            int docId = scoreDoc.doc;

            Document d = searcher.doc(docId);

            List<IndexableField> fields= d.getFields();

            System.out.print((i + 1) );

            System.out.print("\t" + scoreDoc.score);

            for (IndexableField f : fields) {

 

                if("name".equals(f.name())){

                    TokenStream tokenStream = analyzer.tokenStream(f.name(), newStringReader(d.get(f.name())));

                    String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));

                    System.out.print("\t"+fieldContent);

                }

                else{

                    System.out.print("\t"+d.get(f.name()));

                }

            }

            System.out.println("<br>");

        }

    }

 

    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {

        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(analyzer);

        IndexWriter writer = new IndexWriter(index, config);

        String fileName = "140k_products.txt";

        List<Product> products = ProductUtil.file2list(fileName);

        int total = products.size();

        int count = 0;

        int per = 0;

        int oldPer =0;

        for (Product p : products) {

            addDoc(writer, p);

            count++;

            per = count*100/total;

            if(per!=oldPer){

                oldPer = per;

                System.out.printf("索引中,总共要添加 %d 条记录,当前添加进度是: %d%% %n",total,per);

            }

             

        }

        writer.close();

        return index;

    }

 

    private static void addDoc(IndexWriter w, Product p) throws IOException {

        Document doc = new Document();

        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));

        doc.add(new TextField("name", p.getName(), Field.Store.YES));

        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));

        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));

        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));

        doc.add(new TextField("code", p.getCode(), Field.Store.YES));

        w.addDocument(doc);

    }

}

 

©著作权归作者所有:来自51CTO博客作者wx5c4afeea27343的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. SpringBoot持久层支持 - Mybatis-xml方式
  2. J2EE Listener 监听器教程
  3. 史上最全maven教程
  4. springboot整合 elasticsearch 做 增删改查 CRUD以及分页
  5. 如何在 阿里云 申请 ssl 免费证书
  6. 史上最详细的Intellij IDEA开发教程
  7. Springboot 如何做前后端分离?
  8. 如何设置 Centos7 为固定ip地址(详细教程)
  9. 变量系列教材 (八)- 什么是Java的表达式

随机推荐

  1. Thinking in C++ 第一卷阅读全书笔记重点
  2. C/C++区别有哪些?很多人都不知道的比较方
  3. 原来斐波拉契数列还有这种写法,你知道吗?
  4. C++_STL常用容器总结:对组pair中关联容器
  5. c/c++字符串函数是什么类型和它是如何转
  6. 详细介绍C# 中 ASP.NET Web API 的 ROC
  7. CSS选择器有哪些?CSS选择器优先级判定
  8. 必学!C++实现多态机制满足的基本条件条件
  9. 最新总结C语言中关于指针等相关理解和使
  10. 疑惑解答: CSS中背景图片的background-pos