• 传智播客itcastbbs(六)



    (2009-04-29 20:40:15)
    标签:

    it

     

    Lucene:

    1. 举例: Eclipse的帮助;

    2. 建立索引:只能为文本类型的数据建立索引
     
     html(去掉标签), pdf等可以使用相应的工具转换为文本;
     
    3. 原理:字典
     key ---> recordNum
     ab ---> 5,7,10
     Document;  
      5
      7
      10

    4. 使用:

     4.1 建立java Project;
     4.2 添加*.jar;[没有配置文件]目前为2.4版;
      核心;分词器;高亮器;
     4.3 写代码测试;
      FirstTest
       StringindexPath = "c:/luceneDemoIndex/";
       Analyzeranalyzer = new StandardAnalyer();
      
       @Test
       testCreateIndex(){
        Stringtitle   = "xxx";
        Stringcontent  = "yyy";
        
        //1.建立索引    
        MaxFieldLengthmaxFieldLength = MaxFieldLength.LIMITED;
        
        //如果索引库不存在,则创建;
        IndexWriterindexWriter = new IndexWriter(indexPath, analyzer,maxFieldLength); 
        
        
        //文档,字段
        Documentdoc = new Document();
        doc.add(newField("title", title, Store.YES, Index.ANALYZED));
        doc.add(newField("content", content, Store.YES, Index.ANALYZED));
        
        indexWriter.addDocument(doc);
        
        
        //使用完一定要关闭
        indexWriter.close();  
       }
       
       
       voidtestSearch() {
       
        StringqueryString ="document";    
        IndexSearcherindexSearcher = new IndexSearcher(indexPath);
        
        StringdefaultFieldName = "content";
        QueryParserqueryParser = new QueryParser(defaultFieldName, analyzer);
        
        Query query  =queryParser.parse(queryString);   //查询条件
        Filter filter  =null;   //过滤条件
        int nDocs  =100;   //返回匹配的数目
        
        //返回结果
        TopDocstopDocs = indexSearcher.search(query, filter, nDocs);
        System.out.println("共有【"+topDocs.totalHits+"条匹配记录");
        
        List<Document>docs = newArrayList<Document>();
        for( ScoreDoc scoreDoc : topDocs.scoreDocs ) {
         intdocNum = scoreDoc.doc;   //文档在索引库中的编号
        
         Documentdoc = indexSearcher.doc(docNum); // 通过编号取出相应的文档
         
         doc.add(doc);
           
        indexSearcher.close();
        
        for( Document doc : docs ) {
         System.out.print("title:"+ doc.getField("title").stringValue());
         System.out.println("\tcontent:"+ doc.getField("content").stringValue());
         System.out.println("-------------------------------------------------");
        }
       }
      ------------------------------------------------------
      (1) 测试建立索引文件;
      (2) 对索引库的操作:
       创建;搜索;
       ----
       删除, 更新;
      (3) IndexDAO {
       
       // 创建索引
       create(Documentdoc);
       
       // 删除索引
       delete();
       
       // 更新索引
       update(Documentdoc);
       
       // 搜索
       search(StringqueryString, int first, int max);

       ----------实现过程
       StringindexPath = "c:/luceneDemoIndex/";
       Analyzeranalyzer = new StandardAnalyer();
        
       create(Documentdoc) {

        IndexWriterindexWriter = new IndexWriter(indexPath, analyzer,maxFieldLength); 
       
        try{
         indexWriter.addDocument(doc);
        }
        catch(e) {
        
        }
        finally{
         try{
          indexWriter.close();
        }
        
       }


       delete(Termterm) {
        //Termt = new Term("content", "document");
        IndexWriterindexWriter = null;
        
        try{
         indexWriter= new IndexWriter(indexPath, analyzer,maxFieldLength); 
         indexWriter.deleteDocuments(term); 
        }
        catch(e){}
        finally{
         indexWriter.close();
         
       }
       
       
       update(Termterm, Document doc) {
        //Termt = new Term("content", "document");
        IndexWriterindexWriter = null;
        
        try{
         indexWriter= new IndexWriter(indexPath, analyzer,maxFieldLength); 
         
         //先删除,再创建
         indexWriter.updateDocument(term,doc); 
        }
        catch(e){}
        finally{
         indexWriter.close();//try..catch
         
       }
       
       //支持分页的搜索;
       List<Document>/SearchResult
       search(StringqueryString, int first, int max) {
       
        IndexSearcherindexSearcher = new IndexSearcher(indexPath);
        
        StringdefaultFieldName = "content";
        QueryParserqueryParser = new QueryParser(defaultFieldName, analyzer);
        
        Query query  =queryParser.parse(queryString);   //查询条件
        Filter filter  =null;   //过滤条件
        int nDocs  = first +max;   //返回匹配的数目
        
        //返回结果
        TopDocstopDocs = indexSearcher.search(query, filter, nDocs);
        System.out.println("共有【"+topDocs.totalHits+"条匹配记录");
        
        List<Document>docs = newArrayList<Document>();
        intstart = first;
        intend   = Math.min(first+max,topDocs.totalHits);
        for( int i=start; i<end; i++ ) {
         intdocNum = scoreDoc.doc;   //文档在索引库中的编号
        
         Documentdoc = indexSearcher.doc(docNum); // 通过编号取出相应的文档
         
         docs.add(doc);
           
        indexSearcher.close();
        
        for( Document doc : docs ) {
         System.out.println("title:"+ doc.getField("title").stringValue());
         System.out.println("\tcontent:"+ doc.getField("content").stringValue());
         System.out.println("-----------------------------------------------------------------------------");
        }
        
        returnnew SearchResult(topDocs.totalHits, docs);
       }

      (4) 测试IndexDAO;
      
       testCreate(){
        Stringtitle="";
        Stringcontent="";
        
        Documentdoc = new Document();
        doc.add(newField("title", title, Store.YES, Index.ANALYZE));
        doc.add(newField("content", content, Store.YES, Index.ANALYZE));
        indexWrite.addDocment(doc);
       }
       
       
       testSearch(){
        sr= indexDAO.search(queryString, 0, 10);
        for(Docuemnt doc : sr.getDocs ) {
         for(Object obj : doc.getFields() ) {
          Fieldfield = ( Field )obj;
          syso(field.name(),field.stringValue);      
         }
        }
       }
       
       
       testDelete(){
        Termterm = new Term("content", "document");
        indexDAO.delete(term);
       }
       -------删除完之后,测试搜索就没有匹配记录
       
       testUpdate(){
        Termterm = new Term("content", "document");
        
        doc.add(newField());
        doc.add(newField());
        
        indexDAO.update(term,doc);   
       }
       ---------
       
      (5) 索引文件存放的位置:文件系统,内存;
       DirectoryTest(){}
       
       Directory dir= null;
       dir =FSDirectory.getDirectory(path); // 与前面的实现一样
       newRAMDirectory();  //在程序退出后,索引库就没了,速度快;   
       newIndexWriter(dir, analyzer, MaxFieldLength.LIMITED);
       
       
      (6) RAMDirectory,Directory结合使用: 既可以提供效率又可以保存
       3步操作;
       
       // 1.创建索引
       fsDir = FSDirectory.getDirectory(path);
       remDir = newRAMDirectory(fsDir); 
       
       // 2. 处理代码 :使用remDir创建索引
       IndexWriterramIndexWriter = new(remDir, ...);
       doc.add();
       doc.add();
       ...
       ramIndexWriter.close();
          
       // 3.把内存中的索引数据存到fsDir
       IndexWriterfsIndexWriter = new(fsDir, ...);
       fsIndexWriter.addIndexesNoOptimize(newDirectory[] {ramDir});
       fsIndexWriter.close();
       
       ---  remDir ->fsDir 是追加操作;
       --- 可以让remDir = new RAMDirectory(fsDir);
       --- new IndexWriter(..., IsCreate=false,...);
        IsCreate= true: 重新创建新的索引,老的索引就被删除了;

       --总结:
        索引可以放在2个位置;
        索引文件可以创建新的,也可以使用原有的,取决于Create=true;
        如果文件本身就不存在,就直接创建


    5. 相关度排序与Boost

      (1)匹配度:根据关键字在内容中出现次数,位置等等
      (2) 使用Boost影响相关度排序
         BoostTest
          增加了2个Document,其中1个"Doument"4次,1个2次;
          -- 实验看出出现次数影响排序,但是还有其他因素影响排序;
          -- 给排在后面的文档下面的操作
           doc2.setBoost(1.0f) -- 默认
           doc2.setBoost(2.0f);
           这样就可以让这个文档排在前面;
      (3) 还可以在Field中设置;
       标题中出现的关键字比内容中出现的重要;
       目前只在内容中查询;
       也可以再多个字段中查询;
       
       
    6. Analyzer:分词器
     
      英文与中文的不同;
      中文:
      a) 单字
      b) 二分法
      c) 词库
      
      分词测试:
       --标题内容使用中文;
       --DAO中set/getAnalyzer方法;
        可以在创建和搜素时设置分词器;
       --将搜索的方法写成工具类,直接使用。
       --先使用StandAnalyzer;
       -- 下面两句的区别 :测试的结果没有什么区别
        queryString= "content: 中国";(term, 中国)
        queryString= "中国";("中国"扯开成"中 国")
       
       ----------------------------------------------------------------
       分词器测试:
       ------------------------------------------------------------------
       --单字分词器测试
        nwStandardAnalyzer()
       
        Termterm = new Term("content", "中");
        Queryquery = new TermQuey();
        printSearchResult(indexDao.getDirectory(),query);
        
        创建索引时,使用的是单字分词,所以索引中没有"中国",
        使用中可以搜索到记录,而中国搜索则为0条记录;

        --二分法分词
        测试代码同上,
        创建索引时使用new CJKAnalyzer()
        中国,是中 -- 可以
        中  -- 无记录
        
        --词库分词器
        je分词器
        new MMAnalyzer();
        所有输入该词库的都可以搜索到;

    7. 高亮器 : 对搜索的结果使用高亮效果;

     (1) 测试: HighLighterTest() {
     
        dao.search();//复制并简单修改
        
        //何处设置高亮
        Formatterformatter = new SimpleHTMLFormatter("<fontcolor='blue'>","</font>");  //高亮器的原理
        Scorerscorer = new QueryScorer(query);
        Highlighterhighlighter = new Highlighter(formatter, scorer);
         
        //显示内容前
        Stringht = highlighter.getBestFragment(analyzer, "content",doc.get("content"));
        doc.getField("content").setValue(ht);
       }
       
       测试:content内容很长,搜索结果不是全部显示,而是显示出现关键字
       的部分;
       --------高亮器的另一个作用,显示关键字最多的地方的部分内容;
       可以设置显示的内容数目,方法如下
       Fragmenterfragmeter = new SimpleFragment(100);
       setTextFragmenter(fragmenter);
       
     (2)如果搜索结果为0,高亮器的结果返回null,而不是原始内容  
       if ( ht ==null ) {
        //subString 会出错
        maxEndIndex= doc.get("title").length();
        endIndex= Math.min(50, maxEndIndex);
       
        ht= doc.get("title").subString(0, endIndex);
        doc.getField("content").setValue(ht);
       

     

    8. 查询:

     (1) 查询一个字段
     
      Queryquery;  
      String queryString ="索引";
      QueryParser queryParser = newQueryParser("content", analyzer);
      query =queryParser.parser(queryString);
     
     (2) 查询多个字段
      QueryParser queryParser = newMultiFieldQueryParser(new String{field1, field2}, analyzer);
      query =queryParser.parser(queryString);
      
      ---为字段指定Boost值:构造函数后面增加一个字段的Boost属性Map;
      Map BoostMap;
      BoostMap.put("title",3f);
      BoostMap.put("conent",1f);
      QueryParser queryParser = newMultiFieldQueryParser(..., BoostMap);
      
      文章1: 标题中有"索引",内容中无;
      文章2: 标题中无"索引",内容中有;
      
      通过设置他们的Boost属性,测试他们的排序效果;
      
      (3) TermQuery()
        Term term =new Term("title", "测试");
        Query query= new TermQuery(term);
        syso:query;//== [title:测试]
        
      (4) RangeQuery()
        TermlowerTerm = new Term("size", "100");
        TermupperTerm = new Term("size", "250");
        Query query= new RangeQuery(lowerTerm, upperTerm, true);
        //不包括边界的【size:{100 TO 250}】或包括边界的【size:[100 TO 250]】
        //比较的时候是根据字符串,而不是数字;
        //(0200,1000) 准备数据和上述Term时都应这样写;
        //日期:yyyyMMddHHmmss
        --为2篇文章准备size字段数据的时候,不要分词;
       
       (5) PrefixQuery
        Term term = new Term("title","lu");    
        Query query = newPrefixQuery(term);
        syso:query // title:lu*,title:索*
        
       (6) WildcardQuery
        * :n个任意字符(n>=0)
        ? : 1个任意字符
        
        // 之前的版本通配符不能出现在第一个字符
        newWildcardQuery(term); 

       (7) BooleanQuery
        
          rangeQuery = new RangeQuery();
        termQuery  =new TermQuery();
        
        booleanQuery = newBooleanQuery();
        booleanQuery.add(rangeQuery,Occur.MUST);
        booleanQuery.add(rangeQuery,Occur.MUST);
        
        // + : 必须出现;
        // - : 必须不出现;
        // 无:可以出现可以不出现
        syso:booleanQuery //+size:[0100 TO 0250] + title:lucene
             // Occur是个枚举,MUST, MUSTNOT, SHOULD


         // 课堂演示:两个都必须出现
         +contents:李白 +name:黄鹤楼
       contents:李白AND name:黄鹤楼
       
       //课堂演示:1个都必须出现, 1个必须不出现
       contents:李白-name:黄鹤楼
       contents:李白NOT name:黄鹤楼
       
       //课堂演示:出现1个即可(默认)
       contents:李白name:黄鹤楼
       contents:李白OR name:黄鹤楼
       
       ====上面这个默认可以修改
       queryParser.setDefaultOperator(Operator.AND);
       queryParser.parser();

    9. 使用Lucene
     面向对象的Lucene的操作框架:  Compass
      (1) compass-2.2:
       jar + lucenejar;
      (2) 使用
       -- 1.cn.itcast.demo.bena.searchableArticle
         id,title, content, postTime, authorName
         
       -- 2.写映射文件或注解
         SearchableArticle @Searchable
         getId方法加上  @SearchableId
         getXXX方法加上 @SearchableProperty(store=Store.YES,index=Index.ANALYZED, boost=3f)
          // 根据需要设置
          
       -- 3.ArticleIndexDAO
         课堂上没有写接口,实际编程需要写;
         create(), delete(), update();
         QueryResult search(Query, first, max); // 返回可以分页的结果集
         QueryResult search(String, first, max);
         
       --- 4. 实现DAO: 同Hibernate
        CompassConfiguration cfg;
        cfg.setConnection("c:/compassIndex/"); // cfg.xml
        cfg.addClass(SearchableArticle.class); // hbm.xml
        
        Compass compass = cfg.buildCompass();
        CompassSession session = compass.openSession();
        CompassTransaction tx = session.beginTransaction;
        
        session.create(article);
        session.delete(session.get(SearchableArticle.class, id));
        session.save(article);
        
        
        tx.commit();
        session.close();     
         
        JdbcTemplate;
        CompassTemplate ct = new CompassTemplate();
        ct = new CompassTemplate(session);
        ct.save(article);
          
         
        
        CompassHitshits = session.find(queryString);
        for( CompassHit hit : hits ) {
         SearchableArticlearticle = (SearchableArticle)hit.data();
        }
        
        for( int i=first; i<end; i++) {
         SearchableArticlearticle = (SearchableArticle)hits.data(i);
         items.add(article);
        }

        returnnew QueryResult(hits.getLength(), items);
        
    ==== 从现在起,分页返回的结果不再只是内容列表,还要包括总记录数;

         关于id,创建的时候不检查,可以创建多个相同的id的索引;
         update的时候,会删除所有的相同id的内容,再将新的内容放入索引;
        
         高亮器的使用:
         //如果进行高亮的属性值中没有出现关键字,则返回null     
         String ht =hits.highlighter(i).fragment("content"); 
         if ( ht != null ) {
           article.setContent(ht);
         
         else {
           int endIndex = Math.min(100,article.length());
           String newValue =article.subString(0, endIndex);
           article.setContent(newValue);
         }
         
         items.add(article);
         
         高亮器的使用:改变前后缀;可以在文档的附录中找
         cfg.setSetting([highlighter].formatter.simple.pre,"<fontcolor='blue'>"); 

         更改分词器:创建索引和搜索 
         cfg.setSetting("...analyzer.defalut.type", CJK);
         默认是单字索引,这里用CJK;

     

        
          CompassQuery compassQuery =CompassQueryBuilder.queryString("查询语句").toQuery(); 
        
        //term查询
        compassQuery= queryBuilder.term(name, value);
        
        //范围查询
        compassQuery= queryBuiler.between();
        
        queryBuilder.lt/gt...
        
        //...
        
        
        CompassHitshits = compassQuery.hits();


  • 相关阅读:
    LDAP2-创建OU创建用户
    GNE: 4行代码实现新闻类网站通用爬虫
    为什么每一个爬虫工程师都应该学习 Kafka
    新闻网页通用抽取器GNEv0.04版更新,支持提取正文图片与源代码
    写了那么久的Python,你应该学会使用yield关键字了
    新闻类网页正文通用抽取器
    为什么Python 3.6以后字典有序并且效率更高?
    为什么你需要少看垃圾博客以及如何在Python里精确地四舍五入
    数据工程师妹子养成手记——数据库篇
    一行js代码识别Selenium+Webdriver及其应对方案
  • 原文地址:https://www.cnblogs.com/firecode/p/2460933.html
Copyright © 2020-2023  润新知