传智播客--itcastbbs(六)

来源:互联网 发布:tensorflow 算法 编辑:程序博客网 时间:2024/05/17 01:05


(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();


原创粉丝点击