Lucene入门学习

参考博客: http://blog.csdn.net/ayi_5788/article/category/6348409

分页: http://blog.csdn.net/hu948162999/article/details/41209699

1、 什么是中文分词

  学过英文的都知道,英文是以单词为单位的,单词与单词之间以空格或者逗号句号隔开。而中文则以字为单位,字又组成词,字和词再组成句子。所以对于英文,我们可以简单以空格判断某个字符串是否为一个单词,比如I love China,love 和 China很容易被程序区分开来;但中文“我爱中国”就不 一样了,电脑不知道“中国”是一个词语还是“爱中”是一个词语。把中文的句子切分成有意义的词,就是中文分词,也称切词。我爱中国,分词的结果是:我 爱 中国。

  目前中文分词还是一个难题———对于需要上下文区别的词以及新词(人名、地名等)很难完美的区分。国际上将同样存在分词问题的韩国、日本和中国并称为CJK(Chinese Japanese Korean),对于CJK这个代称可能包含其他问题,分词只是其中之一。

2、 中文分词的实现

  Lucene中对中文的处理是基于自动切分的单字切分,或者二元切分。除此之外,还有最大切分(包括向前、向后、以及前后相结合)、最少切分、全切分等等。

  Lucene自带了几个分词器WhitespaceAnalyzer, SimpleAnalyzer, StopAnalyzer, StandardAnalyzer, ChineseAnalyzer, CJKAnalyzer等。前面三个只适用于英文分词,StandardAnalyzer对可最简单地实现中文分词,即二分法,每个字都作为一个词,比如:”北京天安门” ==> “北京 京天 天安 安门”。这样分出来虽然全面,但有很多缺点,比如,索引文件过大,检索时速度慢等。ChineseAnalyzer是按字分的,与StandardAnalyzer对中文的分词没有大的区别。 CJKAnalyzer是按两字切分的, 比较武断,并且会产生垃圾Token,影响索引大小。以上分词器过于简单,无法满足现实的需求,所以我们需要实现自己的分词算法

  这样,在查询的时候,无论是查询”北京” 还是查询”天安门”,将查询词组按同样的规则进行切分:”北京”,”天安安门”,多个关键词之间按与”and”的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。

  基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低,但对于中小型应用来说,基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多,而对于英文,索引文件一般只有原文件的30%-40%不同。

  目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法,大家可以在Google查关键词”wordsegment search”能找到更多相关的资料。

  =============================分割线===============================

  由于Lucene不同的版本差距较大,,此系列教程打算把3.5版本,4.5版本,5.0版本都给出个例子,方便大家学习,也方便自己复习。

  注:由于Lucene5.0版本是基于JDK1.7开发的,所以想学习的同学请配置1.7及以上的版本。故测试Lucene 6.1.0也适用Lucene 5.0中的代码。Lucene 6.1.0最低要求也是JDK1.7.

  创建索引可分为主要的几步,我自己试验过,不同的版本间会有些不同,但是跟着如下的几大步骤一步一步写,问题不会太大。

  1、创建Directory

  2、创建IndexWriter

  3、创建Document对象

  4、为Document添加Field

  5、通过IndexWriter添加文档到索引中

  搜索可分为如下几步:

  1、创建Directory

  2、创建IndexReader

  3、根据IndexReader创建IndexSearch

  4、创建搜索的Query

  5、根据searcher搜索并且返回TopDocs

  6、根据TopDocs获取ScoreDoc对象

  7、根据searcher和ScoreDoc对象获取具体的Document对象

  8、根据Document对象获取需要的值

  我们向Document添加Field可以有更多的设置,那么都是什么意思呢?

  name:字段名,很容易理解

  value:字段值,也很容易理解

  store和index怎么解释,下面就来看一下这两个选项的可选值:

  Field.Store.YES或者NO(存储域选项)

  设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原

  设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完全还原

  Field.Index(索引选项)

  Index.ANALYZED:进行分词和索引,适用于标题、内容等

  Index.NOT_ANALYZED:进行索引,但是不进行分词,如果身份证号,姓名,ID等,适用于精确搜索

  Index.ANALYZED_NOT_NORMS:进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和权值等信息

  Index.NOT_ANALYZED_NOT_NORMS:即不进行分词也不存储norms信息

  Index.NO:不进行索引

  lucene4.5例子:

  

import java.io.File;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
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.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

public class IndexUtil {
    private static final String[] ids = { "1", "2", "3" };
    private static final String[] authors = { "Darren", "Tony", "Grylls" };
    private static final String[] titles = { "Hello World", "Hello Lucene", "Hello Java" };
    private static final String[] contents = { "Hello World, I am on my way", "Today is my first day to study Lucene",
            "I like Java" };

    /**
     * 建立索引
     */
    public static void index() {
        IndexWriter indexWriter = null;
        try {
            // 1、创建Directory
            Directory directory = FSDirectory.open(new File("F:/test/lucene/index"));

            // 2、创建IndexWriter
            Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_45);
            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_45, analyzer);
            indexWriter = new IndexWriter(directory, config);

            int size = ids.length;
            for (int i = 0; i < size; i++) {
                // 3、创建Document对象
                Document document = new Document();
                // 看看四个参数的意思

                // 4、为Document添加Field
                /**
                 * Create field with String value.
                 *
                 * @param name
                 *            field name
                 * @param value
                 *            string value
                 * @param type
                 *            field type
                 * @throws IllegalArgumentException
                 *             if either the name or value is null, or if the field‘s type is neither indexed() nor
                 *             stored(), or if indexed() is false but storeTermVectors() is true.
                 * @throws NullPointerException
                 *             if the type is null
                 *
                 *             public Field(String name, String value, FieldType type)
                 */

                /**
                 * 注意:这里与3.5版本不同,原来的构造函数已过时
                 */

                /**
                 * 注:这里4.5版本使用FieldType代替了原来的Store和Index,不同的Field预定义了一些FieldType
                 *
                 */
                // 对ID存储,但是不分词也不存储norms信息
                FieldType idType = TextField.TYPE_STORED;
                idType.setIndexed(false);
                idType.setOmitNorms(false);
                document.add(new Field("id", ids[i], idType));

                // 对Author存储,但是不分词
                FieldType authorType = TextField.TYPE_STORED;
                authorType.setIndexed(false);
                document.add(new Field("author", authors[i], authorType));

                // 对Title存储,分词
                document.add(new Field("title", titles[i], StringField.TYPE_STORED));

                // 对Content不存储,但是分词
                document.add(new Field("content", contents[i], TextField.TYPE_NOT_STORED));

                // 5、通过IndexWriter添加文档到索引中
                indexWriter.addDocument(document);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (indexWriter != null) {
                    indexWriter.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 搜索
     */
    public static void search() {
        DirectoryReader indexReader = null;
        try {
            // 1、创建Directory
            Directory directory = FSDirectory.open(new File("F:/test/lucene/index"));
            // 2、创建IndexReader
            /**
             * 注意Reader与3.5版本不同:
             *
             * 所以使用DirectoryReader
             *
             * @Deprecated public static DirectoryReader open(final Directory directory) throws IOException { return
             *             DirectoryReader.open(directory); }
             */
            indexReader = DirectoryReader.open(directory);
            // 3、根据IndexReader创建IndexSearch
            IndexSearcher indexSearcher = new IndexSearcher(indexReader);
            // 4、创建搜索的Query
            // 使用默认的标准分词器
            Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_45);

            // 在content中搜索Lucene
            // 创建parser来确定要搜索文件的内容,第二个参数为搜索的域
            QueryParser queryParser = new QueryParser(Version.LUCENE_45, "content", analyzer);
            // 创建Query表示搜索域为content包含Lucene的文档
            Query query = queryParser.parse("Lucene");

            // 5、根据searcher搜索并且返回TopDocs
            TopDocs topDocs = indexSearcher.search(query, 10);
            // 6、根据TopDocs获取ScoreDoc对象
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (ScoreDoc scoreDoc : scoreDocs) {
                // 7、根据searcher和ScoreDoc对象获取具体的Document对象
                Document document = indexSearcher.doc(scoreDoc.doc);
                // 8、根据Document对象获取需要的值
                System.out.println("id : " + document.get("id"));
                System.out.println("author : " + document.get("author"));
                System.out.println("title : " + document.get("title"));
                /**
                 * 看看content能不能打印出来,为什么?
                 */
                System.out.println("content : " + document.get("content"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (indexReader != null) {
                    indexReader.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}
时间: 2024-08-06 20:50:42

Lucene入门学习的相关文章

Lucene.net入门学习系列(1)

Lucene.net入门学习系列(1)-分词 Lucene.net入门学习系列(2)-创建索引 Lucene.net入门学习系列(3)-全文检索 这几天在公司实习的时候闲的蛋疼,翻了一下以往的教程和博客,看到了Lucene.net.原本想学着写一个系列的博文,由于本人水平有限,一直找不到适合的内容来写,干脆就写一个简单的Lucene.net系列文章吧.希望和大家一起学习,一起进步,有什么写错了或者有什么建议欢迎提出来. 一.引言 先说一说什么是Lucene.net.Lucene.net是Luce

Lucene第一个入门学习例子

看Lucene in Action的时候,练习的一个入门例子. 在使用Lucene进行文本内容搜索前,需要先对指定的目录下的文件进行建立索引,代码如下: import java.io.File; import java.io.FileFilter; import java.io.FileReader; import java.io.IOException; import org.apache.lucene.analysis.standard.StandardAnalyzer; import or

Retrofit 入门学习

Retrofit 入门学习官方RetrofitAPI 官方的一个例子 public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } 这些注解都有一个参数 value,用来配置其路径,比如示例中的 users/{user}/repos, 我们还注意到在构造 Retrofit

JavaSE入门学习21:Java面向对象之接口(interface)(二)

一接口实现的多态 在上一篇博文:JavaSE入门学习20:Java面向对象之接口(interface)(一)中提到了接口的实现存在多态性,那么 这一篇主要就要分析接口实现的多态. 实例一 Test.java源文件代码: public class Test{ public static void main(String[] args){ //实现接口Singer Singer s1 = new Student("Amy"); s1.sing(); s1.sleep(); s1.study

[Todo] Nodejs学习及Spider实验(包括php入门学习、React入门学习)

/Users/baidu/Documents/Data/Interview/Web-Server开发 深入浅出Node.js-f46c http://blog.csdn.net/u012273376/article/details/52736906 利用nodejs做爬虫 http://www.runoob.com/nodejs/nodejs-callback.html nodejs学习之路 http://www.runoob.com/php/php-tutorial.html php学习之路

JavaSE入门学习24:Java面向对象补充

一Java中的Object类 Object类是所有Java类的父类,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认 继承Object类. public class Person{ // } //等价于 public class Person extends Object{ // } Object类中的方法,适合所有子类. 1)toString()方法 在Object类中定义有public String toString()方法,其返回值是String类型,描述当前对

js入门学习

在HTML,CSS学习的最后,开始入门学习javascript, 首先就是去了解它的用途 作用:使web页面能够与用户交互,作为控制浏览器给网页增添活力的方法 定义:它是一种脚本语言,作用于浏览器.和java没有半毛钱关系,主要是当时Netscape公司为了增加影响力改了名,,(是不是感觉很无耻) ps:区分程序的一种方式:1.客户端程序(client-side),像javascript就是,我觉得HTML,CSS应该也是. 2.服务器端程序(server-side),java,perl,php

mybatis入门学习

一.mybaits简介 java程序中常用的数据库框架. 二.mybait入门学习 1.环境 创建一个java或者javaweb项目:我是创建了一个structs项目,helloworld 2.添加相应的jar包

angularjs入门学习【指令篇】

一.首先我们来了解下指令API 属性 含义 restrict 申明标识符在模版中作为元素,属性,类,注释或组合,如何使用 priority 设置模版中相对于其他标识符的执行顺序 Template 指定一个字符串式的内嵌模版,如果你指定了模版是一个URL,那么是不会使用的 tempateUrl 指定URL加载的模版,如果你已经指定了内嵌的模版字符串,那么它不会使用的 Replace 如果为真,替换当前元素,如果是假或未指定,拼接到当前元素 Transclude 移动一个标识符的原始字节带你到一个新