Lucene总结(二):使用Lucene创建索引总结

上一篇讲到了Lucene的检索原理以及简单介绍如何创建索引。

这一篇从Lucene的添加、删除、更新、文档域加权来对构建索引进行总结。

准备所需jar包和数据

新建了一个maven工程,pom.xml如下:

<dependencies>
        <!-- lucene核心包 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- lucene查询解析包 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- lucene解析器包 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>5.3.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

在工程中新建一个junit测试类IndexTest1.java,在类中准备一下用来测试的数据,如下:

//用来测试的数据
private String ids[] = {"1", "2", "3"}; 
private String dates[] = {"yesterday", "today", "tomorrow"};
private String descs[] = {
    "It was a rainy day yesterday",
    "It's cloudy today",
    "Tomorrow is sunny."};

可以将上面三个数组不同位置上对应的字符串看成一个文档,如对数组位置为0的,这个文档就是,id域为“1”,date域为”yesterday”,desc域就是”It was a rainy day yesterday”。因此就会有三个文档,下面对文档进行分析。

添加文档

添加文档其实就是创建索引,首先使用上一篇讲到的创建索引的方法,首先创建一个写索引,然后通过这个对象去添加文档,每个文档就是一个Lucene的Document,于是可以在继续在IndexTest1.java中添加:

public class IndexingTest1 {

    private Directory dir; //存放索引的位置

    //用来测试的数据
    private String ids[] = {"1", "2", "3"}; 
    private String dates[] = {"yesterday", "today", "tomorrow"};
    private String descs[] = {
        "It was a rainy day yesterday",
        "It's cloudy today",
        "Tomorrow is sunny."};


    //生成索引
    @Test
    public void index() throws Exception{
        IndexWriter writer = getWriter();
        for(int i = 0; i < ids.length; i++){
            Document document = new Document();
            document.add(new StringField("id", ids[i], Field.Store.YES));
            document.add(new StringField("date", dates[i], Field.Store.YES));
            document.add(new TextField("desc", descs[i], Field.Store.YES));
            writer.addDocument(document);
        }
        writer.close(); //close了才真正写到文档中
    }

    //获取IndexWriter实例
    public IndexWriter getWriter() throws Exception{
        dir = FSDirectory.open(Paths.get("D:\\resource"));
        Analyzer analyzer = new StandardAnalyzer();//标准分词器,会自动去掉空格啊,is a the等单词
        IndexWriterConfig  config = new IndexWriterConfig(analyzer);//将标准分词器配到写索引的配置中
        IndexWriter writer = new IndexWriter(dir,config);//实例化写索引对象
        return writer;
    }
}

从上面的代码可以看出,每个数组元素都是一个filed域。文档添加好了域之后,就添加到写索引的实例writer中写入。也就是先创建一个文件,再在文件中添加信息,在使用写索引写入索引库中。
如图,就有了这样的索引文件的产生:
这里写图片描述

读取文档

读取文档的话需要IndexReader对象,初始化的时候要传入读取文档所在的路径,也就是之前的D:\resource。

public void testIndexReader() throws Exception {
    dir = FSDirectory.open(Paths.get("D:\\resource"));
    IndexReader reader = DirectoryReader.open(dir);
    System.out.println("最大文档数:" + reader.maxDoc());
    System.out.println("实际文档数:" + reader.numDocs());
    reader.close();
}

删除文档

删除文档有两种方式。一种是在合并前删除,另一种是在合并后删除

  1. 合并前删除指的是并没有真正删除这个文档,只是在这个文档上做一个标记而已;
  2. 合并后删除指的是真正删掉了这个文档了。

使用场景
如果一个项目比较大,并发访问的人数比较多,删除肯定会对性能有影响,这个时候就可以用合并前删除,先不删,只是标记一下这个文档属于已删除的文档,等到访问量比较小的时候,再统一删除。同样的,如果数据量不大,删除操作也不怎么影响性能,那么直接删除,也就是合并后删除。

同样的在之前那个例子上添加如下代码:

//合并前删除
    @Test
    public void testDeleteBefore() throws Exception{
        IndexWriter writer = getWriter();
        System.out.println("删除前有" + writer.numDocs() + "个文档");
        writer.deleteDocuments(new Term("id", "1")); //删除id=1对应的文档
        writer.commit(); //提交删除,并没有真正删除
        System.out.println("删除后最大文档数:" + writer.maxDoc());
        System.out.println("删除后实际文档数:" + writer.numDocs());
        writer.close();
    }

    //合并后删除
    @Test
    public void testDeleteAfter() throws Exception {
        IndexWriter writer = getWriter();
        System.out.println("删除前有" + writer.numDocs() + "个文档");
        writer.deleteDocuments(new Term("id", "1")); //删除id=1对应的文档
        writer.forceMergeDeletes(); //强制合并(强制删除),没有索引了
        writer.commit(); //提交删除,真的删除了
        System.out.println("删除后最大文档数:" + writer.maxDoc());
        System.out.println("删除后实际文档数:" + writer.numDocs());
        writer.close();
    }

测试结果如下:
合并前删除测试:
这里写图片描述
合并后删除测试:
这里写图片描述
在这里,测试的时候要注意,完成合并前删除测试之后,将路径上的所有索引都删除,重新调用上面的index方法重新生成一下,再去测试合并后删除,因为之前删掉一个了,会影响后面的测试。

更新文档

更新文档,就是新建一个Document对象,然后按照前面设置的字段自己再设置个新的,然后更新原来的文档。
在上面的例子上添加:

//更新文档测试
    @Test
    public void updateTest() throws Exception{
        IndexWriter writer = getWriter();
        Document document = new Document();
        document.add(new StringField("id", ids[1], Field.Store.YES));
        document.add(new StringField("date", "today update", Field.Store.YES));
        document.add(new TextField("desc", "Today is a good day", Field.Store.NO));
        writer.updateDocument(new Term("id", ids[1]),document);
        writer.close();
        System.out.println(document.getField("desc"));
    }

结果如下:
这里写图片描述

文档域加权

这个其实有点可以理解,比如你想在一个博客上查一个单词,很多篇文档都会有这个单词,但是你希望标题含有这个单词的优先排在前面,或者希望某位作者写的文档放在前面一点,那么你就可以使用加权。

这里的代码我用了网上的一个例子,因为不想想文档具体的内容。就是有四个人写了四篇有关Java的文章,希望匹配将作者c写的放在最前面。因此新建了一个IndexTest2类。

//准备数据,五个人各写了一篇文章,希望先出来C的文章,因此给C加权
    private String ids[] = {"1", "2", "3", "4"};
    private String authors[] = {"A", "B", "C", "D"};
    private String positions[] = {"a", "b", "c", "d"};
    private String titles[] = {"Java is a good language.", "Java is a cross platform language", "Java powerful", "You should learn java"};
    private String contents[] = {
            "If possible, use the same JRE major version at both index and search time.",
            "When upgrading to a different JRE major version, consider re-indexing. ",
            "Different JRE major versions may implement different versions of Unicode.",
            "For example: with Java 1.4, `LetterTokenizer` will split around the character U+02C6."
    };

同样的生成索引,区别在于进行了加权操作。
public class IndexTest2 {

private Directory directory;

//准备数据,五个人各写了一篇文章,希望先出来C的文章,因此给C加权
private String ids[] = {"1", "2", "3", "4"};
private String authors[] = {"A", "B", "C", "D"};
private String positions[] = {"a", "b", "c", "d"};
private String titles[] = {"Java is a good language.", "Java is a cross platform language", "Java powerful", "You should learn java"};
private String contents[] = {
        "If possible, use the same JRE major version at both index and search time.",
        "When upgrading to a different JRE major version, consider re-indexing. ",
        "Different JRE major versions may implement different versions of Unicode.",
        "For example: with Java 1.4, `LetterTokenizer` will split around the character U+02C6."
};

@Test
public void index() throws Exception {
    IndexWriter indexWriter = getWriter();
    for (int i = 0; i < ids.length; i++) {
        Document document = new Document();
        document.add(new StringField("id", ids[i], Field.Store.YES));
        document.add(new StringField("author", authors[i], Field.Store.YES));
        document.add(new StringField("position", positions[i], Field.Store.YES));

        //这部分就是加权操作了,对title这个Field进行加权,因为等会要查这个Field
        TextField field = new TextField("title", titles[i], Field.Store.YES);
        //先判断这个人是不是c,如果是就加权
        if ("c".equals(positions[i])) {
            field.setBoost(1.5f); //加权操作,默认为1,1.5表示加权了,小于1就降权了
        }

        document.add(field);
        document.add(new TextField("content", contents[i], Field.Store.NO));
        indexWriter.addDocument(document); //添加文档
    }
    indexWriter.close();
}
//获取IndexWriter实例
public IndexWriter getWriter() throws Exception{
    directory = FSDirectory.open(Paths.get("D:\\masterSpring\\code\\chapter18\\src\\resource"));
    Analyzer analyzer = new StandardAnalyzer();
    IndexWriterConfig config = new IndexWriterConfig(analyzer);
    IndexWriter writer = new IndexWriter(directory,config);
    return writer;
}
//测试加权的等下写。
}

上面就是创建索引的过程。

`if ("c".equals(positions[i])) {
    field.setBoost(1.5f); 
}

这句话就是给作者是C的文档加权了,默认为1,大于1表示加权了,小于1就降权了
结果如下。
这里写图片描述
可以看见C排在了第一位,如果没有上面的那段加权代码,那么匹配出来的顺序是Lucene中自己的一个算法,可以认为是Lucene默认的顺序,具体是怎样的就不探讨了,我们可以自己根据需求自己设定一下权重。


希望对你有帮助,如有疑问或见解,欢迎提出,共同进步。

猜你喜欢

转载自blog.csdn.net/RebelHero/article/details/80200416