Lucene入门

一、介绍

Lucene是一款高性能的、可扩展的信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作。

简单的说,搜索就是搜寻、查找,在IT行业中就是指用户输入关键字,通过相应的算法,查询并返回用户所需要的信息。

传统的模糊查询方法在处理大数据的时候效率低、耗时长(select * from 表名 where 字段名 like ‘%关键字%’),所以lucene应运而生,也有了倒排索引的概念

二、倒排索引:

        又称为反向索引: 以字或者词,甚至是一句话一段话作为一个关键字进行索引, 每一个关键字都会对应着一个记录项, 记录项中记录了这个关键字出现在那些文档中, 在此文档的什么位置上

在实际的运用中,我们可以对数据库中原始的数据结构(左图),在业务空闲时事先根据左图内容,创建新的倒排索引结构的数据区域(右图)。用户有查询需求时,先访问倒排索引数据区域(右图),得出文档id后,通过文档id即可快速,准确的通过左图找到具体的文档内容。

Lucene、Solr、Elasticsearch关系

Lucene:底层的API,工具包

Solr:基于Lucene开发的企业级的搜索引擎产品

Elasticsearch:基于Lucene开发的企业级的搜索引擎产品

创建索引流程:

文档Document:数据库中一条具体的记录

字段Field:数据库中的每个字段

目录对象Directory:物理存储位置

写出器的配置对象:需要分词器和lucene的版本

三、Java API实现

API来实现对索引的增(创建索引)、删(删除索引)、改(修改索引)、查(搜索数据)。

1、IndexWriter:索引写入器对象,添加索引, 修改索引和删除索引。

                             需要传入DirectoryindexWriterConfig对象 ,即索引的目录和配置
2、FSDirectory:用来指定文件系统的目录, 将索引信息保存到磁盘上
      RAMDriectory: 内存目录, 将索引库信息存放到内存中
3、IndexWriterConfig: 索引写入器的配置类,需要传递Lucene的版本和分词器

4、QueryParser(查询解析器)

5、Query(查询对象,包含要查询的关键词信息)

6、IndexSearch(索引搜索对象,执行搜索功能)

7、TopDocs(查询结果对象)

8、 ScoreDoc(得分文档对象)

代码实现:

依赖:

<properties>
        <lunece.version>4.10.2</lunece.version>

    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- lucene核心库 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>${lunece.version}</version>
        </dependency>
        <!-- Lucene的查询解析器 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>${lunece.version}</version>
        </dependency>
        <!-- lucene的默认分词器库 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>4.10.1</version>
        </dependency>
        <!-- lucene的高亮显示 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-highlighter</artifactId>
            <version>${lunece.version}</version>
        </dependency>
        <!-- IK分词器 中文 -->
        <dependency>
            <groupId>com.janeluo</groupId>
            <artifactId>ikanalyzer</artifactId>
            <version>2012_u6</version>
        </dependency>
    </dependencies>

1、创建倒排索引

//创建只有一条文档的文件
    @Test
    public void simpleCreate() {
        try {
        //1 创建文档对象
            Document ducument = new Document();
            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
            ducument.add(new StringField("id", "1", Field.Store.YES));
            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
            ducument.add(new TextField("title", "谷歌地图之父跳槽facebook", Field.Store.YES));

        //2 创建存储目录,本地
            FSDirectory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
        //3 创建分词器
            StandardAnalyzer analyzer = new StandardAnalyzer();
        //4 创建索引写入器的配置对象
            IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);

        //5 创建索引写入器对象
            IndexWriter indexWriter = new IndexWriter(directory, config);
        //6 将文档交给索引写入器
            indexWriter.addDocument(ducument);
        //7 提交
            indexWriter.commit();
        //8 关闭
            indexWriter.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //创建多个文档的文件
    @Test
    public void manyCreate() {
        try {
            //1 创建文档对象集合
            Collection<Document> documents = new ArrayList<Document>();

            Document ducument = new Document();
            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
            ducument.add(new StringField("id", "1", Field.Store.YES));
            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
            ducument.add(new TextField("title", "谷歌地图之父跳槽facebook", Field.Store.YES));
            documents.add(ducument);

            Document ducument2 = new Document();
            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
            ducument2.add(new StringField("id", "1", Field.Store.YES));
            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
            ducument2.add(new TextField("title", "谷歌地图之父加盟FaceBook", Field.Store.YES));
            documents.add(ducument2);

            Document ducument3 = new Document();
            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
            ducument3.add(new StringField("id", "1", Field.Store.YES));
            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
            ducument3.add(new TextField("title", "谷歌地图创始人拉斯离开谷歌加盟Facebook", Field.Store.YES));
            documents.add(ducument3);

            Document ducument4 = new Document();
            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
            ducument4.add(new StringField("id", "1", Field.Store.YES));
            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
            ducument4.add(new TextField("title", "谷歌地图之父跳槽Facebook与Wave项目取消有关", Field.Store.YES));
            documents.add(ducument4);

            Document ducument5 = new Document();
            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
            ducument5.add(new StringField("id", "1", Field.Store.YES));
            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
            ducument5.add(new TextField("title", "谷歌地图之父拉斯加盟社交网站Facebook", Field.Store.YES));
            documents.add(ducument5);

            //2 创建存储目录,本地
            FSDirectory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
            //3 创建分词器
            StandardAnalyzer analyzer = new StandardAnalyzer();
//            IKAnalyzer ikAnalyzer = new IKAnalyzer();
            //4 创建索引写入器的配置对象
            IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
            //如果有数据,覆盖
            config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);

            //5 创建索引写入器对象
            IndexWriter indexWriter = new IndexWriter(directory, config);
            //6 将文档交给索引写入器
            indexWriter.addDocuments(documents);
            //7 提交
            indexWriter.commit();
            //8 关闭
            indexWriter.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

2、查询索引

//索引查询代码
    @Test
    public void search() {
        try {
            //1 创建查询文件
             Directory dir = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
            //2 索引读取工具
            DirectoryReader reader = DirectoryReader.open(dir);
            //搜索工具
            IndexSearcher indexSearcher = new IndexSearcher(reader);

            // 创建查询解析器,参数:查询的字段的名称,分词器
            QueryParser queryParser = new QueryParser("title", new IKAnalyzer());  //new StandardAnalyzer()
            //查询对象
            Query query = queryParser.parse("谷歌");

            //搜索工具查询,参数:查询对象,最大结果条数
            //结果topdoc:按照匹配度排名得分前N名的文档信息(包含查询到的总条数信息、所有符合条件的文档的编号信息)
            TopDocs search = indexSearcher.search(query, 10);

            //获取总条数
            System.out.println("一共有:"+search.totalHits+"条数据");

            // 获取得分文档对象(ScoreDoc)数组.SocreDoc中包含:文档的编号、文档的得分
            ScoreDoc[] scoreDocs = search.scoreDocs;
            for(ScoreDoc scoreDoc : scoreDocs){
                //文档编号
                int id=scoreDoc.doc;
                //文档编号对应文档
                Document doc = reader.document(id);

                System.out.println("id: "+doc.get("id")+"   title: "+doc.get("title"));
            // 取出文档得分
            System.out.println("得分: " + scoreDoc.score);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

3高亮显示

   // 高亮显示
    @Test
    public void testHighlighter() throws Exception {
        // 目录对象
        Directory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
        // 创建读取工具
        IndexReader reader = DirectoryReader.open(directory);
        // 创建搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);

        QueryParser parser = new QueryParser("title", new StandardAnalyzer());
        Query query = parser.parse("谷歌");

        // 格式化器
        Formatter formatter = new SimpleHTMLFormatter("<body>", "</body>");
        QueryScorer scorer = new QueryScorer(query);
        // 准备高亮工具
        Highlighter highlighter = new Highlighter(formatter, scorer);
        // 搜索
        TopDocs topDocs = searcher.search(query, 10);
        System.out.println("本次搜索共" + topDocs.totalHits + "条数据");

        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            // 获取文档编号
            int docID = scoreDoc.doc;
            Document doc = reader.document(docID);
            System.out.println("id: " + doc.get("id"));

            String title = doc.get("title");
            // 用高亮工具处理普通的查询结果,参数:分词器,要高亮的字段的名称,高亮字段的原始值
            String hTitle = highlighter.getBestFragment(new StandardAnalyzer(), "title", title);

            System.out.println("title: " + hTitle);
            // 获取文档的得分
            System.out.println("得分:" + scoreDoc.score);
        }
    }

4、排序

    // 排序
    @Test
    public void testSortQuery() throws Exception {
        // 目录对象
        Directory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
        // 创建读取工具
        IndexReader reader = DirectoryReader.open(directory);
        // 创建搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);

        QueryParser parser = new QueryParser("title", new StandardAnalyzer());
        Query query = parser.parse("谷歌");

        // 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
        Sort sort = new Sort(new SortField("id", SortField.Type.LONG, true));
        // 搜索
        TopDocs topDocs = searcher.search(query, 10,sort);
        System.out.println("本次搜索共" + topDocs.totalHits + "条数据");

        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            // 获取文档编号
            int docID = scoreDoc.doc;
            Document doc = reader.document(docID);
            System.out.println("id: " + doc.get("id"));
            System.out.println("title: " + doc.get("title"));
        }
    }

5、分页

    // 分页
    @Test
    public void testPageQuery() throws Exception {
        // 实际上Lucene本身不支持分页。因此我们需要自己进行逻辑分页。我们要准备分页参数:
        int pageSize = 2;// 每页条数
        int pageNum = 3;// 当前页码
        int start = (pageNum - 1) * pageSize;// 当前页的起始条数
        int end = start + pageSize;// 当前页的结束条数(不能包含)

        // 目录对象
        Directory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene" ));
        // 创建读取工具
        IndexReader reader = DirectoryReader.open(directory);
        // 创建搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);

        QueryParser parser = new QueryParser("title", new StandardAnalyzer());
        Query query = parser.parse("谷歌");

        // 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
        Sort sort = new Sort(new SortField("id", SortField.Type.LONG, false));
        // 搜索数据,查询0~end条
        TopDocs topDocs = searcher.search(query, end,sort);
        int count = topDocs.totalHits;
        System.out.println("本次搜索共" + count + "条数据"+"当前页: "+pageNum);

        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (int i = start; i < end; i++) {
                if(i == count ){
                    return;
                }
                ScoreDoc scoreDoc = scoreDocs[i];
                // 获取文档编号
                int docID = scoreDoc.doc;
                Document doc = reader.document(docID);
                System.out.println("id: " + doc.get("id"));
                System.out.println("title: " + doc.get("title"));
            }
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值