• Lucene入门


    1.需求

    2.创建索引

      2.1实现步骤

      2.2.创建lucene项目

      2.3代码实现

    3.查询索引

      3.1实现步骤

      3.2代码实现

    4.分析器

      4.1标准分析器的分词效果

      4.2IKAnalyzer分析器的分词效果

    5.索引库的维护

      5.1Field域的属性

      5.2索引库的添加、删除、修改

    6.Lucene索引库查询

      6.1TermQuery

      6.2数据查询和使用QueryParser查询

    项目目录:

    1.需求

    实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来。还可以根据中文词语进行查询,并且需要支持多个条件查询。

    本案例中的原始内容就是磁盘上的文件,如下图:

    2.创建索引

    2.1实现步骤

    第一步:创建一个java工程,并导入jar包。

    第二步:创建一个indexwriter对象。

    1)指定索引库的存放位置Directory对象

    2)指定一个IndexWriterConfig对象。

    第二步:创建document对象。

    第三步:创建field对象,将field添加到document对象中。

    第四步:使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。

    第五步:关闭IndexWriter对象。

    2.2创建lucene项目

     

    2.3代码实现

    public class LuceneFirst {
    
        //创建索引
        @Test
        public void createIndex() throws Exception {
            //1.创建一个directory对象,指定索引库保存的位置
            //把索引库保存在内存中(不建议这样做)
            //Directory directory = new RAMDirectory();
            //把索引库保存在在磁盘中,指定路径名
            Directory directory = FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath());
            //2.基于directory对象创建一个indexwriter对象
            IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
            //3.读取磁盘上的文件,对应每个文件创建一个文档对象
            File dir = new File("D:\BaiduNetdiskDownload\lucene\02.参考资料\searchsource");
            File[] files = dir.listFiles();
            for (File f : files) {
                //取文件名
                String filename = f.getName();
                //文件路径名
                String filepath = f.getPath();
                //文件内容
                String filecontent = FileUtils.readFileToString(f, "utf-8");
                //文件大小
                long filesize = FileUtils.sizeOf(f);
                //创建Field
                //参数1:域的名称 ,参数2:域的内容 , 参数3:是否存储
                Field fieldName = new TextField("name", filename, Field.Store.YES);
                //Field fieldPath = new TextField("path",filepath,Field.Store.YES);
                //StoreField仅做存储,其下内容不做分词和索引
                Field fieldPath = new StoredField("path", filepath);
                Field fieldContent = new TextField("content", filecontent, Field.Store.YES);
                //Field fieldSize = new TextField("size", filesize + "", Field.Store.YES);//+""是为了将long类型数据转成字符串
                Field fieldSizeValue = new LongPoint("size",filesize);//LongPoint用作运算,如数据范围查询
                Field fieldSizeStore = new StoredField("size",filesize);
                //创建文档对象
                Document document = new Document();
                //向文档对象中添加域
                document.add(fieldName);
                document.add(fieldPath);
                document.add(fieldContent);
                //document.add(fieldSize);
                document.add(fieldSizeValue);
                document.add(fieldSizeStore);
                //5.把文档对象写入索引库
                indexWriter.addDocument(document);
    
            }
            //6.关闭索引库
            indexWriter.close();
        }
    }

    D:\IDEA\IdeaProjects\lucene\temp\index为索引库的路径。

    注意:new IndexWriterConfig()里面为空时,默认使用标准分词器 StandardAnalyzer,也可以自定义分析器,如:new IndexWriterConfig(new IKAnalyzer());

    创建索引完成后,索引库内出现如下文件:

    3.查询索引

    3.1实现步骤

    第一步:创建一个Directory对象,也就是索引库存放的位置。

    第二步:创建一个indexReader对象,需要指定Directory对象。

    第三步:创建一个indexsearcher对象,需要指定IndexReader对象

    第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。

    第五步:执行查询。

    第六步:返回查询结果。遍历查询结果并输出。

    第七步:关闭IndexReader对象

    3.2代码实现

    @Test
        public void searchIndex() throws Exception {
            //1.创建一个directory对象,指定索引库的位置
            Directory directory = FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath());
            //2.创建一个indexReader对象
            IndexReader indexReader = DirectoryReader.open(directory);
            //3.创建一个indexSearcher对象,构造方法中的参数indexReader对象
            IndexSearcher indexSearcher = new IndexSearcher(indexReader);
            //4.创建一个query对象,TermQuery
            //参数1:指定查询的域 参数2:指定要查找的关键词
            //Query query = new TermQuery(new Term("content", "spring"));
            Query query = new TermQuery(new Term("name", "apache")); 
    //5.执行查询,得到一个TopDocs对象 //参数1:查询对象 参数2:查询结果返回的最大记录数 TopDocs topDocs = indexSearcher.search(query, 10); //6.取查询结果的总记录数 System.out.println("查询总记录数:" + topDocs.totalHits); //7.取文档列表 ScoreDoc[] scoreDocs = topDocs.scoreDocs; //8.打印文档中的 内容 for (ScoreDoc doc : scoreDocs) { //取文档 int docId = doc.doc; //根据id取文档对象 Document document = indexSearcher.doc(docId); System.out.println(document.get("name")); System.out.println(document.get("path")); System.out.println(document.get("size")); //System.out.println(document.get("content")); System.out.println("------------------寂寞的分割线---------------"); } //9.关闭indexreader对象 indexReader.close(); }

    查询结果:

    4.分析器

    4.1标准分析器的分词效果

        @Test
        public void testTokenStream() throws Exception {
            //1.创建一个analyzer对象,standardAnalyzer对象
            Analyzer analyzer = new StandardAnalyzer();//标准分析器只适用于英文文档的分析
           //2.使用分析器对象的tokenStream方法获得一个tokenStream对象
            TokenStream tokenStream = analyzer.tokenStream("", "The spring framework provides a comprehensive programming and configuration model.");
           //3.向tokenStream对象中设置有一个引用,相当于一个指针
            CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
            //4.调用tokenStream对象的reset方法,如果不调用抛异常
            tokenStream.reset();
            //5.使用while循环遍历tokenStream对象
            while (tokenStream.incrementToken()) {
                System.out.println(charTermAttribute.toString());
            }
            //6.关闭tokenStream对象
            tokenStream.close();
        }

    analyzer.tokenStream("", "The spring framework provides a comprehensive programming and configuration model.");

    其中第一个参数添加域名,这里我们不添加,第二个参数:标色语句为待分词的内容。

    分词效果为:

     4.2IKAnalyzer分析器的分词效果

        @Test
        public void testTokenStream() throws Exception {
            //1.创建一个analyzer对象,standardAnalyzer对象
            //Analyzer analyzer = new StandardAnalyzer();//标准分析器只是用于英文文档的分析
            Analyzer analyzer1 = new IKAnalyzer();//该分析器适用于中文分词
            //2.使用分析器对象的tokenStream方法获得一个tokenStream对象
            //TokenStream tokenStream = analyzer.tokenStream("", "The spring framework provides a comprehensive programming and configuration model.");
            TokenStream tokenStream = analyzer1.tokenStream("","今天奶奶给我买了我最爱吃的喜之郎果冻");
            //3.向tokenStream对象中设置有一个引用,相当于一个指针
            CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
            //4.调用tokenStream对象的reset方法,如果不调用抛异常
            tokenStream.reset();
            //5.使用while循环遍历tokenStream对象
            while (tokenStream.incrementToken()) {
                System.out.println(charTermAttribute.toString());
            }
            //6.关闭tokenStream对象
            tokenStream.close();
        }

    注意: IKAnalyzer分析器适用于无BOM UTF-8 编码文档。也就是说禁止使用windows记事本编辑扩展词典文件。

    分词效果:

    如果使用标准分析器对中文进行分词,则会变成:

    这样是不便于搜索查询的!

    5.索引库的维护

    5.1Field域的属性

    是否分析:是否对域的内容进行分词处理。前提是我们是否要对域的内容进行查询。

    是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。

    比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。

    是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取

    比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

    是否存储的标准:是否要将内容展示给用户

     5.2索引库的添加、删除、修改

    代码实现:

    public class IndexManager {
    
        private IndexWriter indexWriter;
    
        @Before
        public void init() throws Exception{
            //创建一个indexWriter对象,需要使用使用IkAnalyzer作为分析器
            indexWriter =
                    new IndexWriter(FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath()),
                            new IndexWriterConfig(new IKAnalyzer()));
        }
    
        //添加索引
        @Test
        public void addDocument() throws Exception{
            //创建一个document对象
            Document document = new Document();
            //向document对象中添加域
            document.add(new TextField("name","新添加的文件", Field.Store.YES));
            document.add(new TextField("content","新添加的文件内容",Field.Store.NO));
            //不需要创建索引的就使用StoreField存储
            document.add(new StoredField("path","D:\IDEA\IdeaProjects\lucene\temp\index"));
            //把文档写入索引库
            indexWriter.addDocument(document);
            //关闭索引库
            indexWriter.close();
        }
    
    
    =================================================
        
    //删除索引 //删除全部索引 @Test public void deleteAllDocument() throws Exception{ //删除全部文档 indexWriter.deleteAll(); //关闭索引库 indexWriter.close(); } //根据查询条件删除索引 @Test public void deleteDocumentByQuery() throws Exception{ indexWriter.deleteDocuments(new Term("name","apache")); indexWriter.close(); } ================================================= //修改索引库,原理就是先删除后添加 @Test public void updateDocument() throws Exception{ //创建一个新的文档对象 Document document = new Document(); //向文档对象中添加域 document.add(new TextField("name","更新后的文档",Field.Store.YES)); document.add(new TextField("name1","更新后的文档1",Field.Store.YES)); document.add(new TextField("name2","更新后的文档2",Field.Store.YES)); //更新操作,先删除再添加 indexWriter.updateDocument(new Term("name","apache"),document); //关闭索引库 indexWriter.close(); } }

    6.Lucene索引库查询

    对要搜索的信息创建Query查询对象,Lucene会根据Query查询对象生成最终的查询语法,类似关系数据库Sql语法一样Lucene也有自己的查询语法,比如:“name:lucene”表示查询Field的name为“lucene”的文档信息。

             可通过两种方法创建查询对象:

             1)使用Lucene提供Query子类

             2)使用QueryParse解析查询表达式

    6.1TermQuery

    TermQuery,通过项查询,TermQuery不使用分析器所以建议匹配不分词的Field域查询,比如订单号、分类ID号等。

    指定要查询的域和要查询的关键词。

    在上文3.2代码实现中有用到TermQuery查询关键词:

     6.2数据范围查询 和 使用QueryParser查询

    public class SearchIndex {
    
        private IndexReader indexReader;
        private IndexSearcher indexSearcher;
    
        @Before
        public void init() throws Exception{
            indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\IDEA\IdeaProjects\lucene\temp\index").toPath()));
            indexSearcher = new IndexSearcher(indexReader);
        }
    
        //数据范围查询
        @Test
        public void testRangeQuery() throws Exception{
            //创建一个Query对象,当Field的类为LongPoint时可用于数据范围查询,上文2.3代码实现中就创建了LongPoint类的Field
            Query query = LongPoint.newRangeQuery("size",0,100);
            printResult(query);
        }
    
        private void printResult(Query query) throws Exception{
            //执行查询
            TopDocs topDocs = indexSearcher.search(query,10);
            System.out.println("总记录数:"+topDocs.totalHits);
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (ScoreDoc doc:scoreDocs){
                //取文档id
                int docId = doc.doc;
                //根据id取文档对象
                Document document = indexSearcher.doc(docId);
                System.out.println(document.get("name"));
                System.out.println(document.get("path"));
                System.out.println(document.get("size"));
                System.out.println("----------------分割线---------------");
            }
            indexReader.close();
        }
    
        //使用QueryParser查询    
        @Test
        public void testQueryParser() throws Exception{
            //创建一个queryParser对象,两个参数
            //参数1:设置默认搜索的域  参数2:分析器对象
            QueryParser queryParser = new QueryParser("name",new IKAnalyzer());
            //使用queryParser对象创建一个query对象
            Query query = queryParser.parse("lucene是一个Java开发的全文搜索工具包");
            //执行查询
            printResult(query);
        }
    }

    使用queryParser时需要注意:

      通过QueryParser也可以创建Query,QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询。Query对象执行的查询语法可通过System.out.println(query);查询。

    需要使用到分析器。建议创建索引时使用的分析器和查询索引时使用的分析器要一致。

  • 相关阅读:
    springboot的自动配置
    tomcat8.5和redis实现session共享
    GitHub提交时出错,提示需要验证邮箱verify email
    vue3可拖拽容器宽度
    vue解决iOS10-11、vant部分版本ImagePreview点击预览图片无法缩放回去的问题
    使用vue自定义指令限制input输入内容为正整数
    判断当前时间是否超出预约(配送)时间
    vue选择地址字母联动
    手机号码中间四位*号隐藏(别的方法有的机型不适配)
    前端面试题
  • 原文地址:https://www.cnblogs.com/churujianghudezai/p/12696588.html
Copyright © 2020-2023  润新知