Lucene搜索技术

1:   搜索技术概述

萌芽:Archie,Gopher

起步:Robot(比如抢票器)与spider(网络爬虫)的出现

发展:excite,galaxy,yahoo

繁荣:infoseek,altaVista,Google和baidu

2:搜索技术的使用原理

通过网略爬虫向各个网站进行资源搜罗,把搜索到的资源进过筛选保存到快照库,用索引进行搜索查询。

倒排索引就像一本书的目录,记录关键单词在文档中出现的位置,在搜索时会节省大量时间.

lucene是一种搜索技术的实现方式,通过倒排索引技术,提高检索速度,实现快速搜索

索引的查看工具可以使用LukeAll

3:索引的创建

Lucene搜索技术

pom.xml引入坐标依赖:

   <dependencies>

      <!-- lucene核心库 -->

      <dependency>

         <groupId>org.apache.lucene</groupId>

         <artifactId>lucene-core</artifactId>

         <version>4.10.4</version>

      </dependency>

      <!-- Lucene的查询解析器 -->

      <dependency>

         <groupId>org.apache.lucene</groupId>

         <artifactId>lucene-queryparser</artifactId>

         <version>4.10.4</version>

      </dependency>

      <!-- lucene的默认分词器库 -->

      <dependency>

         <groupId>org.apache.lucene</groupId>

         <artifactId>lucene-analyzers-common</artifactId>

         <version>4.10.4</version>

      </dependency>

      <!-- lucene的高亮显示 -->

      <dependency>

         <groupId>org.apache.lucene</groupId>

         <artifactId>lucene-highlighter</artifactId>

         <version>4.10.4</version>

      </dependency>

      <!-- Junit单元测试 -->

      <dependency>

         <groupId>junit</groupId>

         <artifactId>junit</artifactId>

         <version>4.12</version>

         <scope>test</scope>

      </dependency>

 

      <dependency>

         <groupId>org.slf4j</groupId>

         <artifactId>slf4j-log4j12</artifactId>

         <version>1.7.25</version>

      </dependency>

      <!-- ik中文分词器 -->

      <dependency>

         <groupId>com.janeluo</groupId>

         <artifactId>ikanalyzer</artifactId>

         <version>2012_u6</version>

      </dependency>

   </dependencies>

测试代码:

publicclass LuceneTest {

 

   //创建索引

   @Test

   publicvoid testCreateIndex() throws Exception{

     

      //1.创建Document文档(相当于一条数据)-是一篇文章

      Document document=new Document();

      //在文档中加入Field字段

      //1)业务编号:参数1:字段名字,参数2:字段值,参数3:是否存储

      document.add(new LongField("id", 101, Store.YES));

      //2)标题:TextField自动分词

      document.add(new TextField("title", "世界很美好", Store.YES));

      //3)作者:StringField不分词

      document.add(new StringField("author", "小七", Store.YES));

      //4)邮箱:只存储和显示,不能搜索,默认是Store.YES

      document.add(new StoredField("email", "[email protected]"));

      //5)内容:自动分词存储并能搜索

      document.add(new TextField("content", "人人献出一点爱,这个世界将会更美好!  World !", Store.YES));

//6):备注

      document.add(new TextField("remark", "我是备注", Store.NO));    

     

      //2.准备索引写对象,写文档到索引

      //创建索引存放目录对象

      Directory directory=FSDirectory.open(new File("indexdir"));

      //索引的写的配置对象:参数1lucene的版本,参数2:分词器,这里使用内置分词

      IndexWriterConfig indexWriterConfig=new IndexWriterConfig(Version.LATEST, new StandardAnalyzer());

     

      //创建一个写的对象:参数1:索引存放目录对象,参数2:建立索引的配置对象

      IndexWriter indexWriter=new IndexWriter(directory, indexWriterConfig);

      //将上面准备的文档,写入索引中

      indexWriter.addDocument(document);

      //5)提交事务(可以省略)

//    indexWriter.commit();

      //6)释放资源

      indexWriter.close();

      directory.close();   

   }

}

  4. Analyzer分词器对象

在Lucene索引的api中有个重要的对象叫Analyzer(分词器对象),该对象的主要作用是提供分词的算法,可以将文档中的字段的数据按照算法分词。Lucene内置英文分词器.



<!-- ik中文分词器 -->

      <dependency>

         <groupId>com.janeluo</groupId>

         <artifactId>ikanalyzer</artifactId>

         <version>2012_u6</version>

      </dependency>

@Test

   publicvoid testCreateIndexByIK() throws Exception{

     

      //1.创建Document文档(相当于一条数据)-是一篇文章

      Document document=new Document();

      //在文档中加入Field字段

      //1)业务编号:参数1:字段名字,参数2:字段值,参数3:是否存储

      document.add(new LongField("id", 102, Store.YES));

      //2)标题:TextField自动分词

      document.add(new TextField("title", "世界很美好2", Store.YES));

      //3)作者:StringField不分词

      document.add(new StringField("author", "小七2", Store.YES));

      //4)邮箱:只存储和显示,不能搜索

      document.add(new StoredField("email", "[email protected]"));

      //5)内容:

      document.add(new TextField("content", "人人献出一点爱,这个世界将会更美好!  World !", Store.YES));

      //6):备注

      document.add(new TextField("remark", "我是备注2", Store.NO));

     

     

      //2.准备索引写对象,写文档到索引

      //创建索引存放目录对象

      Directory directory=FSDirectory.open(new File("indexdir"));

      //索引的写的配置对象:参数1lucene的版本,参数2:分词器,这里使用内置分词(对中文只能一个一个分)

      IndexWriterConfig indexWriterConfig=new IndexWriterConfig(Version.LATEST, new IKAnalyzer());

     

      //创建一个写的对象:参数1:索引存放目录对象,参数2:建立索引的配置对象

      IndexWriter indexWriter=new IndexWriter(directory, indexWriterConfig);

      //将上面准备的文档,写入索引中

      indexWriter.addDocument(document);

      //事务提交(可省略,没啥用)

//    indexWriter.commit();

      //释放资源

      indexWriter.close();

     

   }

 

扩展词典和停用词典在更改时一定要UTF-8保存

5.索引创建的工具类

//工具类,获取Lucene操作对象

publicclass LuceneUtils {

   privatestaticfinal String INDEX_DIR="indexdir";//索引文件存放目录

   privatestatic Directory directory;//索引文件存放目录对象

   privatestatic Analyzer analyzer;//分词器

   privatestatic IndexWriter indexWriter;//索引写对象,单例

   privatestatic IndexReader indexReader;//索引读对象

  

  

   static{

      try {

         //初始化索引文件存放目录对象

         directory=FSDirectory.open(new File(INDEX_DIR));

         //初始化IK分词器

         analyzer = new IKAnalyzer();

         //初始化索引的写配置对象

         IndexWriterConfig indexWriterConfig=new IndexWriterConfig(Version.LATEST, analyzer);

         //初始化索引的写对象

         indexWriter=new IndexWriter(directory, indexWriterConfig);

         // 虚拟机退出时关闭

         Runtime.getRuntime().addShutdownHook(new Thread(){

            @Override

            publicvoid run() {

                System.out.println("--------关闭IndexWriter....");

                try {

                   //关闭写对象

                   indexWriter.close();

                   directory.close();

                } catch (IOException e) {

                   e.printStackTrace();

                }

               

                System.out.println("--------关闭IndexWriter成功....");

            }

         });

        

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

  

   /**

    * 获取IndexWriter

    * @return

    */

   publicstatic IndexWriter getIndexWriter(){

      returnindexWriter;

   }

6.  索引的基本查询

//各种常见查询规则测试

       @Test

       publicvoidtestManyQuery() throwsException{

           //准备查询的关键字

           String keyword="我是好人";

           //准备Query(查询条件对象)

           //Query

           //创建一个查询解析器(查询条件创建对象-QueryBuilder对象)

           //参数1:要查询的字段的名字,必须有才行

           //参数2:分词器,在查询的时候,先将关键词分词,再搜索

//                QueryParserqueryParser = new QueryParser("content",new IKAnalyzer());

//                QueryParserqueryParser = new QueryParser("content",new StandardAnalyzer());

           //解析(分词))后的查询对象

//                Query query =queryParser.parse(keyword);

           //词条精确匹配

           Query query=null;

           //规则1:精确匹配

           //参数1:要匹配词条的字段的名称,参数2:关键字

           query = new TermQuery(new Term("content",keyword));

           //规则2:通配符匹配,在词条精确匹配的基础上,支持了通配符

           //*:0或N个字符,   ?:代表1个字符

           query = new WildcardQuery(new Term("content","*"+keyword+"*"));

           //规则3:模糊匹配,相似度匹配,默认对英文支持较好,容错最多2个(和字符位置也有关系)

           query=newFuzzyQuery(newTerm("content","daa"));

           //4)数值范围

           //参数分别:字段的名字,最小值,最大值,是否包含最小值边界,是否包含最大值边界

           query=NumericRangeQuery.newLongRange("id", 102L, 103L, true, true);

           //5)查询所有,忽略所有条件

           query=newMatchAllDocsQuery();

           //6)组合规则

    //     BooleanQuery booleanQuery = newBooleanQuery();

    //     Query query1 = new TermQuery(newTerm("content", keyword));

    //     Query query2 = new WildcardQuery(newTerm("content","*"+keyword+"*"));

    //     //参数1:查询的具体策略,参数2:组合策略

    //     booleanQuery.add(query1, Occur.MUST_NOT);

    //     booleanQuery.add(query2, Occur.MUST);

    //     //调用工具类

    //     LuceneUtils.printTopDocsByQuery(booleanQuery,Integer.MAX_VALUE);

           LuceneUtils.printTopDocsByQuery(query, Integer.MAX_VALUE);

       }

             

              //修改

              @Test

              publicvoidtestUpdate() throwsException{

                 

                  //1.创建Document文档(相当于一条数据)-是一篇文章

                  Document document=newDocument();

                  //在文档中加入Field字段

                  //1)业务编号:参数1:字段名字,参数2:字段值,参数3:是否存储

                  document.add(new LongField("id", 102, Store.YES));

                  //2)标题:TextField自动分词

                  document.add(new TextField("title", "如何做好人4",Store.YES));

                  //3)作者:StringField不分词

                  document.add(new StringField("author", "小七4",Store.YES));

                  //4)邮箱:只存储和显示,不能搜索

                  document.add(new StoredField("email", "[email protected]"));

                  //5)内容:

                  document.add(new TextField("content", "我是一个好人4,Goodman!", Store.YES));

                        

                 

                  IndexWriter indexWriter = LuceneUtils.getIndexWriter();

                  //参数1:要修改的词条的依据

                  //参数2:要修改后的文档

                  //bug:如果业务主键数据类型不是字符串,可能会出错,因此建议业务中主键存放到索引中的时候,都转成字符串

                  indexWriter.updateDocument(new Term("id", "102"),document);

              }

             

              //删除

              //如果是用Term删除,要求ID也必须是字符串类型!

              @Test

              publicvoidtestDelete() throwsException{

                  IndexWriter indexWriter = LuceneUtils.getIndexWriter();

                 

                  //1) 根据词条进行删除

//                indexWriter.deleteDocuments(newTerm("id", "102"));

                  //2) 根据query对象删除,如果ID是数值类型,那么我们可以用数值范围查询锁定一个具体的ID

//                Query query =NumericRangeQuery.newLongRange("id", 102L, 103L, true, true);

//                indexWriter.deleteDocuments(query);

                 

                  //3) 删除所有

                  indexWriter.deleteAll();

//                // 提交

//                indexWriter.commit();

 

              }