Lucene基础

来源:互联网 发布:办公室 植物 知乎 编辑:程序博客网 时间:2024/05/16 13:46

最近有空就研究研究搜索引擎,写个博客记一下笔记,巩固基础
一.Lucene概述
Lucene是高性能的的信息搜索库,他是一个类库,提供了一套简单而强大的API,我们可以利用它完成文本索引和搜索功能。
Lucene最主要的两个功能是提供建立索引和搜索功能,当然还有好多其他的强大的功能,比如高亮显示等等,我在一本书上看到一个图挺好,照着画个简易版,帮助大家理解:
这里写图片描述
可以主要的过程是我们先根据具体的需要建立索引,这个过程的实现还是很复杂的,这里建立的索引是一种倒排索引,好在Lucene为我们屏蔽了这一切,提供非常人性化的api,我们直接用就好了。索引建立好了之后,用户搜索的时候输入的内容我们给他封装成一个query对象,根据这个对象,在索引库中搜索然后将搜索结果返回给用户。
二.建立索引
1.如果我们要搜索大量的文件,然后找出其中包含的某个词或短语的文件,初级的方法就是顺序扫描每个文件,这样的效率是很低的,对于大量的文件,我们通常采用倒排索引的办法,看一个图:
这里写图片描述
我们首先建立针对文本的索引,将文本内容转换能够进行快速搜索的格式,我们可以将索引看做是一种数据结构,通过搜索他我们就能够得到与他关联的全部文章。
2.Lucene文件结构
index:索引 一个索引放在一个文件夹中
segment: 段 一个索引中可以有很多段,段与段之间是独立的,添加新的文档可能产生新的段,不同的段可以合并成一个段
document:文档是创建索引的基本单位,不同的文档保存在不同的段,一个段包含不同的文档
field:域 一个文档包含不同的信息类型
term:词是索引的基本单位,是经过词法分析和语言处理后的数据
3.Lucene建立索引的三个步骤
将原始文档转换成文本,分析文本,将分析好的文本保存到索引中
这里写图片描述
4.索引过程核心类
先直观感受下他们之间关系:
IndexWriter:写索引,是索引过程的核心组件,这个类负责创建新索引或打开已有索引,或则向索引中添加删除更新被索引文档的信息,他可以对索引文件进行写入,不能读取或搜索索引。
Directory:抽象类,子类负责具体指定索引的存储路径,然后传给indexwriter的构造方法
Analyzer:文本分析器,文本文件被索引之前,需要其处理
Document:为一个包含多个Field对象的容器
Field:包含可能被索引的文本内容的类,每个域包含一个域名和域值
这里写图片描述
5.demo分析
看了官方给的demo,跟自己写的一比,真的是自愧不如,大神就是大神,下面用大神的代码给出例子

public class IndexFiles {    private IndexFiles() {    }    public static void main(String[] args) {        //声明要被索引的文档路径和要建立索引的目录路径        String usage = "java org.apache.lucene.demo.IndexFiles [-index INDEX_PATH] [-docs DOCS_PATH] [-update]\n\nThis indexes the documents in DOCS_PATH, creating a Lucene indexin INDEX_PATH that can be searched with SearchFiles";        String indexPath = "index";        String docsPath = null;        //是否新建索引        boolean create = true;        //根据用户输入的参数设置路径及建立方式        for(int i = 0; i < args.length; ++i) {            if ("-index".equals(args[i])) {                indexPath = args[i + 1];                ++i;            } else if ("-docs".equals(args[i])) {                docsPath = args[i + 1];                ++i;            } else if ("-update".equals(args[i])) {                create = false;            }        }        //如果文档路径为空,终止程序        if (docsPath == null) {            System.err.println("Usage: " + usage);            System.exit(1);        }        //建立文档对象        File docDir = new File(docsPath);        //如果文档对象不存在或不可读,终止程序        if (!docDir.exists() || !docDir.canRead()) {            System.out.println("Document directory '" + docDir.getAbsolutePath() + "' does not exist or is not readable, please check the path");            System.exit(1);        }        Date start = new Date();        try {            //给出良好的用提示            System.out.println("Indexing to directory '" + indexPath + "'...");            //建立索引            Directory dir = FSDirectory.open(new File(indexPath));            //建立分词器,输入与jar对应的版本,我的jar是新版本所以这里不匹配==            Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);            //Indexwriter的配置信息,将分词器传入构造方法中            IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_40, analyzer);            //根据用户输入的方式设置打开方式            if (create) {                iwc.setOpenMode(OpenMode.CREATE);            } else {                iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);            }            //建立Indexwriter,需要传入directory 对象和配置信息            IndexWriter writer = new IndexWriter(dir, iwc);            //具体创建过程            indexDocs(writer, docDir);            //关闭            writer.close();            Date end = new Date();            System.out.println(end.getTime() - start.getTime() + " total milliseconds");        } catch (IOException var12) {            System.out.println(" caught a " + var12.getClass() + "\n with message: " + var12.getMessage());        }    }    static void indexDocs(IndexWriter writer, File file) throws IOException {        //判断文档是否可读        if (file.canRead()) {            //如果是目录,对里面的文件依次建立索引,迭代调用本方法            if (file.isDirectory()) {                String[] files = file.list();                if (files != null) {                    for(int i = 0; i < files.length; ++i) {                        indexDocs(writer, new File(file, files[i]));                    }                }            } else {                FileInputStream fis;                try {                    fis = new FileInputStream(file);                } catch (FileNotFoundException var9) {                    return;                }                try {                    //根据具体的需求创建域对象                    Document doc = new Document();                    Field pathField = new StringField("path", file.getPath(), Store.YES);                    doc.add(pathField);                    doc.add(new LongField("modified", file.lastModified(), Store.NO));                    doc.add(new TextField("contents", new BufferedReader(new InputStreamReader(fis, "UTF-8"))));                   //根据具体的打开方式创建或更新                    if (writer.getConfig().getOpenMode() == OpenMode.CREATE) {                        System.out.println("adding " + file);                        writer.addDocument(doc);                    } else {                        System.out.println("updating " + file);                        writer.updateDocument(new Term("path", file.getPath()), doc);                    }                } finally {                    fis.close();                }            }        }    }}

就是很简单的建立索引的过程,必要的地方我都写了注释,应该都能看的懂,研读大神的代码还是有收获的:
一般简单的建立索引相信一般人都会写,但是最主要的是程序是否有很强的健壮性,是否有有良好的用户体验,面向对象的编程思想,对后期的维护可拓展性是否良好等等,这些都是需要我们不断加强体会的,针对本demo,多次判断文件是否存在,是否可读,并且根据用户具体的输入来编写不同的建立方式等等,这些都是我们应该学习的,当然这还是需要大量的练习和经验的。所以,特别推荐大家有空多研读现在开源框架等的源码,一定很有收获
三.搜索索引
1.搜索过程核心类
IndexSeacher: 可以看成以只读方式打开索引的类,用于搜索由IndexWriter类创建的索引
Term:搜索功能的基本单元,Term对象包含一对字符串元素:域名和单词
Query:有许多子类,最常用的是TermQuery
TermQuery:最基本的查询类型,用于匹配指定域中包含特定项的文档
TopDocs:是一个简单的指针容器,指针一般指向前N个排名的搜索结果
2.demo
这里我自己写了个简单的例子,很好理解

public class IndexSearchTest {    public static void main(String[] argcs){        Directory directory = null;        try {            //建立索引            directory = FSDirectory.open(new File("D://Lucene/test"));            //创建Reader对象            DirectoryReader dReader = DirectoryReader.open(directory);            //创建indexSearcher对象            IndexSearcher indexSearcher = new IndexSearcher(dReader);            Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_43);            //用queryparser对象解析用户输入的内容            QueryParser queryParser = new QueryParser(Version.LUCENE_43,"content",analyzer);           //通过queryparser得到query            Query query = queryParser.parse("t");            //获得topdocs对象,这个对象包含得到的结果信息            TopDocs topDocs = indexSearcher.search(query,10);            //遍历输出            if(topDocs!=null){                System.out.println("total:"+topDocs.totalHits);                for (int i = 0; i <topDocs.scoreDocs.length ; i++) {                    Document document = indexSearcher.doc(topDocs.scoreDocs[i].doc);                    System.out.println(document.get("id"));                }            }            directory.close();            dReader.close();        } catch (IOException e) {            e.printStackTrace();        } catch (ParseException e) {            e.printStackTrace();        }    }}