<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    我的Blog我做主^_^

    走向一條通往JAVA的不歸路...

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      64 隨筆 :: 68 文章 :: 77 評論 :: 0 Trackbacks

    Lucene是apache組織的一個用java實現全文搜索引擎的開源項目。其功能非常的強大,api也很簡單。總得來說用Lucene來進行建立和搜索與操作數據庫是差不多的,Document可以看作是數據庫的一行記錄,Field可以看作是數據庫的字段。用lucene實現搜索引擎就像用JDBC實現連接數據庫一樣簡單。

         值得一提的是:200661Lucene2.0發布,它與以前廣泛應用和介紹的Lucene 1.4.3并不兼容。 有了很大的改進和優化,這里只介紹的是Lucene 2.0

    Lucene2.0的下載地址是 http://apache.justdn.org/lucene/java/

    大家先看一個例子,通過這個例子來對lucene的一個大概的認識。 一個Junit測試用例:(為了讓代碼清晰好看,我們將異常都拋出)

    a)    這是一個建立文件索引的例子

    public void testIndexHello() throws IOException{
    Date date1 = new Date();
    //可以說是創建一個新的寫入工具
    //第一個參數是要索引建立在哪個目錄里
    //第二個參數是新建一個文本分析器,這里用的是標準的大家也可以自己寫一個
    //第三個參數如果是true,在建立索引之前先將c:\\index目錄清空。
    IndexWriter writer = new IndexWriter("c:\\index",new StandardAnalyzer(),true);
    //這個是數據源的文件夾
    File file = new File("c:\\file");
    /**
    * 例子主要是對C:\\file目錄下的文件的內容建立索引,將文件路徑作為搜索內容的附屬
    */
    if(file.isDirectory()){
    String[] fileList = file.list();
    for (int i = 0; i < fileList.length; i++){
    //建立一個新的文檔,它可以看作是數據庫的一行記錄
    Document doc = new Document();
    File f = new File(file, fileList[i]);
    Reader reader = new BufferedReader(new FileReader(f));
    doc.add(new Field("file",reader));//為doument添加field
    doc.add(new Field("path",f.getAbsolutePath(),Field.Store.YES,Field.Index.NO));
    writer.addDocument(doc);
    }
    }
    writer.close();//這一步是必須的,只有這樣數據才會被寫入索引的目錄里
    Date date2 = new Date();
    System.out.println("用時"+(date2.getTime()-date1.getTime())+"毫秒");
    }
    

    注意:因為建立索引本來就是費時,所以說最后輸出的用時會比較長,請不要奇怪。

    b)一個通過索引來全文檢索的例子

    public void HelloSearch() throws IOException, ParseException{
    //和上面的IndexWriter一樣是一個工具
    IndexSearcher indexSearcher = new IndexSearcher("c:\\index");
    QueryParser queryParser = new QueryParser("file",new StandardAnalyzer());
    //new StandardAnalyzer()這是一個分詞器
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    //這個地方Query是抽象類大家也注意一下,下面會講到的
    Query query = queryParser.parse(br.readLine());
    Hits hits = indexSearcher.search(query);
    Document doc = null;
    System.out.print("正搜索................");
    for (int i = 0; i < hits.length(); i++){
    doc = hits.doc(i);
    System.out.println("內容是:"+doc.get("file"));//注意這里輸出的是什么
    System.out.println("文件的路徑是:" + doc.get("path"));
    }
    }
    通過上面的兩個例子應該可以看出Lucene還是比較簡單的。 
    運行一下上面的兩個例子,大家可能會說怎么doc.get(“file”);返回的是空呢,我們馬上會講到。

    下面講一下索引的建立
         其實從上面的例子就可以看出建立索引就用到Document,IndexWriter,Field。 最簡單的步驟就是:
         首先分別new 一個Document,IndexWriter,Field,然后用Doument.add()方法加入Field.其次用IndexWrtier.addDocument()
    方法加入
    Document。 最后調用一下IndexWriter.close()方法關閉輸入索引,這一步非常的重要只有調用這個方法索引才會被
    寫入索引的目錄里,而這是被很多初學的人所忽略的。 Document沒有什么好介紹的,把它的作用看成數據庫中的一行記錄就行。
    Field是一個比較重要的也是比較復雜的,看一下它的構造函數有5個:

    Field(String name, byte[] value, Field.Store store)
    Field(String name, Reader reader)
    Field(String name, Reader reader, Field.TermVector termVector)
    Field (String name, String value, Field.Store store, Field.Index index)
    Field (String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector)

    在Field中有三個內部類:Field.Index,Field.Store,Field.termVector,而構造函數也用到了它們。
    注意:termVector是Lucene 1.4新增的,它提供一種向量機制來進行模糊查詢,這個不常用。它們的不同的組合,在全文檢索
    中有著不同的作用。看看下面的表吧:

    Field.Index

     

    Field.Store

     

    說明

     

    TOKENIZED(分詞)

     

    YES

     

    被分詞索引且存儲

     

    TOKENIZED

    NO

    被分詞索引但不存儲

     

    NO

    YES

    這是不能被搜索的,它只是被搜索內容的附屬物。如URL等

     

    UN_TOKENIZED

    YES/NO

    不被分詞,它作為一個整體被搜索,搜一部分是搜不出來的

     

    NO

    NO

    沒有這種用法

     

    而對于Field (String name, Reader reader)
    Field (String name, Reader reader, Field.TermVector termVector)
           他們是Field.Index.TOKENIZED和Field.Store.NO的。這就是為什么我們在上面的例子中會出現文章的內容為 null了。因為它只是被索引了,而并沒有被存儲下來。如果一定要看到文章的內容的話可以通過文章的路徑得到。畢竟文章的路徑是作為搜索的附屬物被搜索出來了。而我們在Web開發的時候一般是將大數據放在數據庫中,不會放在文件系統中,更不會放在索引目錄里,因為它太大了操作會加大服務器的負擔

    下面介紹一下IndexWriter:
    它就是一個寫入索引的寫入器,它的任務比較簡單:
    1.用addDocument()將已經準備好寫入索引的document們加入
    2.調用close()將索引寫入索引目錄

    先看一下它的構造函數:
    IndexWriter (Directory d, Analyzer a, boolean create)
    IndexWriter (File path, Analyzer a, boolean create)
    IndexWriter (String path, Analyzer a, boolean create)

    可見構造它需要一個索引文件目錄,一個分析器(一般用標準的這個),最后一個參數是標識是否清空索引目錄
    它有一些設置參數的功能如:設置Field的最大長度
    看個例子:

    public void IndexMaxField() throws IOException {
    IndexWriter indexWriter= new IndexWriter("c:\\index",new StandardAnalyzer(),true);
    Document doc1 = new Document();
    doc1.add(new Field("name1","程序員之家",Field.Store.YES,Field.Index.TOKENIZED));
    Document doc2 = new Document();
    doc2.add(new Field("name2","Welcome to the Home of
                 programers",Field.Store.YES,Field.Index.TOKENIZED));
    indexWriter.setMaxFieldLength(5);
    indexWriter.addDocument(doc1);
    indexWriter.setMaxFieldLength(3);
    indexWriter.addDocument(doc1);
    indexWriter.setMaxFieldLength(0);
    indexWriter.addDocument(doc2);
    indexWriter.setMaxFieldLength(3);
    indexWriter.addDocument(doc2);
    indexWriter.close();
    }
    public void SearcherMaxField() throws ParseException, IOException {
    Query query = null;
    Hits hits = null;
    IndexSearcher indexSearcher= null;
    QueryParser queryParser= null;
    queryParser = new QueryParser("name1",new StandardAnalyzer());
    query = queryParser.parse("程序員");
    indexSearcher= new IndexSearcher("c:\\index");
    hits = indexSearcher.search(query);
    System.out.println("您搜的是:程序員");
    System.out.println("找到了"+hits.length()+"個結果");
    System.out.println("它們分別是:");
    for (int i = 0; i < hits.length(); i++){
    Document doc = hits.doc(i);
    System.out.println(doc.get("name1"));
    }
    query = queryParser.parse("程序員之家");
    indexSearcher= new IndexSearcher("c:\\index");
    hits = indexSearcher.search(query);
    System.out.println("您搜的是:程序員之家");
    System.out.println("找到了"+hits.length()+"個結果");
    System.out.println("它們分別是:");
    for (int i = 0; i < hits.length(); i++){
    Document doc = hits.doc(i);
    System.out.println(doc.get("name1"));
    }
    queryParser = new QueryParser("name2",new StandardAnalyzer());
    query = queryParser.parse("Welcome");
    indexSearcher= new IndexSearcher("c:\\index");
    hits = indexSearcher.search(query);
    System.out.println("您搜的是:Welcome");
    System.out.println("找到了"+hits.length()+"個結果");
    System.out.println("它們分別是:");
    for (int i = 0; i < hits.length(); i++){
    Document doc = hits.doc(i);
    System.out.println(doc.get("name2"));
    }
    query = queryParser.parse("the");
    indexSearcher= new IndexSearcher("c:\\index");
    hits = indexSearcher.search(query);
    System.out.println("您搜的是:the");
    System.out.println("找到了"+hits.length()+"個結果");
    System.out.println("它們分別是:");
    for (int i = 0; i < hits.length(); i++){
    Document doc = hits.doc(i);
    System.out.println(doc.get("name2"));
    }
    query = queryParser.parse("home");
    indexSearcher= new IndexSearcher("c:\\index");
    hits = indexSearcher.search(query);
    System.out.println("您搜的是:home");
    System.out.println("找到了"+hits.length()+"個結果");
    System.out.println("它們分別是:");
    for (int i = 0; i < hits.length(); i++) {
    Document doc = hits.doc(i);
    System.out.println(doc.get("name2"));
    }
    }
    
    總結一下:
    1.設置Field的長度限制只是限制了搜索。如果用了Field.Store.YES的話還是會
    全部被保存進索引目錄里的。

    2.為什么搜the沒有搜出來呢?是因為lucene分析英文的時候不會搜索the to of 等無用的詞(搜這些詞是無意義的)。

    3.New StandardAnlayzer()對于英文的分詞是按空格和一些無用的詞,而中文呢是全部的單個的字。

    4.設置Field的最大長度是以0開頭和數組一樣。

    大家還可以試一下別的,以便加深一下印象

    到現在我們已經可以用lucene建立索引了
    下面介紹一下幾個功能來完善一下:
    1.索引格式
          其實索引目錄有兩種格式,一種是除配置文件外,每一個Document獨立成為一個文件(這種搜索起來會影響速度)。另一種是全部Document成一個文件,這樣屬于復合模式就快了。

    2.索引文件可放的位置:
        索引可以存放在兩個地方1.硬盤,2.內存。放在硬盤上可以用FSDirectory(),放在內存的用RAMDirectory()不過一關機就沒了。
    FSDirectory.getDirectory (File file, boolean create)
    FSDirectory.getDirectory(String path, boolean create)兩個工廠方法返回目錄
    New RAMDirectory() 就直接可以,再和IndexWriter(Directory d, Analyzer a, boolean create) 一配合就行了
    如:
    IndexWrtier indexWriter = new IndexWriter(FSDirectory.getDirectory(“c:\\index”,true),new StandardAnlyazer(),true);
    IndexWrtier indexWriter = new IndexWriter(new RAMDirectory(),new StandardAnlyazer(),true);

    3.索引的合并
        這個可用IndexWriter.addIndexes(Directory[] dirs) 將目錄加進去
    來看個例子:

    public void UniteIndex() throws IOException{
    IndexWriter writerDisk = new IndexWriter(FSDirectory.getDirectory("c:\\indexDisk",
    true),new StandardAnalyzer(),true);
    Document docDisk = new Document();
    docDisk.add(new Field("name","程序員之家",Field.Store.YES,Field.Index.TOKENIZED));
    writerDisk.addDocument(docDisk);
    RAMDirectory ramDir = new RAMDirectory();
    IndexWriter writerRam = new IndexWriter(ramDir,new StandardAnalyzer(),true);
    Document docRam = new Document();
    docRam.add(new Field("name","程序員雜志",Field.Store.YES,Field.Index.TOKENIZED));
    writerRam.addDocument(docRam);
    writerRam.close();//這個方法非常重要,是必須調用的
    writerDisk.addIndexes(new Directory[]{ramDir});
    writerDisk.close();
    }
    public void UniteSearch() throws ParseException, IOException{
    QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());
    Query query = queryParser.parse("程序員");
    IndexSearcher indexSearcher =new IndexSearcher("c:\\indexDisk");
    Hits hits = indexSearcher.search(query);
    System.out.println("找到了"+hits.length()+"結果");
    for(int i=0;i< hits.length();i++){
    Document doc = hits.doc(i);
    System.out.println(doc.get("name"));
    }
    }
    這個例子是將內存中的索引合并到硬盤上來. 
    注意:合并的時候一定要將被合并的那一方的IndexWriter的close()方法調用。
    4.對索引的其它操作:
    IndexReader類是用來操作索引的,它有對Document,Field的刪除等操作。

    下面一部分的內容是:全文的搜索
    全文的搜索主要是用:IndexSearcher,Query,Hits,Document(都是Query的子類),有的時候用QueryParser
    主要步驟:
    1.new QueryParser(Field字段,new 分析器)
    2.Query query = QueryParser.parser(“要查詢的字串”);這個地方我們可以用反射api看一下query究竟是什么類型
    3.new IndexSearcher(索引目錄).search(query);返回Hits
    4.用Hits.doc(n);可以遍歷出Document
    5.用Document可得到Field的具體信息了。
    其實1 ,2兩步就是為了弄出個Query 實例,究竟是什么類型的看分析器了。
    拿以前的例子來說吧
    QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());
        Query query = queryParser.parse("程序員"); //這里返回的就是org.apache.lucene.search.PhraseQuery
    IndexSearcher indexSearcher =new IndexSearcher("c:\\indexDisk");
        Hits hits = indexSearcher.search(query);
    不管是什么類型,無非返回的就是Query的子類,我們完全可以不用這兩步直接new個Query的子類的實例就ok了,
    不過一般還是用這兩步因為它返回的是PhraseQuery這個是非常強大的query子類,它可以進行多字搜索。用QueryParser可以
    設置各個關鍵字之間的關系這個是最常用的了。
    IndexSearcher:
    其實IndexSearcher它內部自帶了一個IndexReader用來讀取索引的,IndexSearcher有個close()方法,這個方法不是用
    來關閉IndexSearcher的是用來關閉自帶的IndexReader。
    QueryParser呢可以用parser.setOperator()來設置各個關鍵字之間的關系,它可以自動通過空格從字串里面將關鍵字分離出來。
    注意:用QueryParser搜索的時候分析器一定的和建立索引時候用的分析器是一樣的。
    Query:
    可以看一個lucene2.0的幫助文檔有很多的子類:
    BooleanQuery, ConstantScoreQuery, ConstantScoreRangeQuery, DisjunctionMaxQuery, FilteredQuery,
    MatchAllDocsQuery, MultiPhraseQuery, MultiTermQuery, PhraseQuery, PrefixQuery, RangeQuery, SpanQuery, TermQuery
    各自有用法看一下文檔就能知道它們的用法了

    下面一部分講一下lucene的分析器:
    分析器是由分詞器和過濾器組成的,拿英文來說吧分詞器就是通過空格把單詞分開,過濾器就是把the,to,of等詞去掉不被搜索和索引。
    我們最常用的是StandardAnalyzer()它是lucene的標準分析器它集成了內部的許多的分析器。
    最后一部分了:lucene的高級搜索了
    1.排序
    Lucene有內置的排序用IndexSearcher.search(query,sort)但是功能并不理想。我們需要自己實現自定義的排序。
    這樣的話得實現兩個接口: ScoreDocComparator, SortComparatorSource
    用IndexSearcher.search(query,new Sort(new SortField(String Field,SortComparatorSource)));

    就看個例子吧:

    這是一個建立索引的例子:

    public void IndexSort() throws IOException {
    IndexWriter writer = new IndexWriter("C:\\indexStore",new StandardAnalyzer(),true);
    Document doc = new Document();
    doc.add(new Field("sort","1",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    doc = new Document();
    doc.add(new Field("sort","4",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    doc = new Document();
    doc.add(new Field("sort","3",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    doc = new Document();
    doc.add(new Field("sort","5",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    doc = new Document();
    doc.add(new Field("sort","9",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    doc = new Document();
    doc.add(new Field("sort","6",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    doc = new Document();
    doc.add(new Field("sort","7",Field.Store.YES,Field.Index.TOKENIZED));
    writer.addDocument(doc);
    writer.close();
    }
    下面是搜索的例子:
    public void SearchSort1() throws IOException, ParseException {
    IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");
    QueryParser queryParser = new QueryParser("sort",new StandardAnalyzer());
    Query query = queryParser.parse("4");
    Hits hits = indexSearcher.search(query);
    System.out.println("有"+hits.length()+"個結果");
    Document doc = hits.doc(0);
    System.out.println(doc.get("sort"));
    }
    public void SearchSort2() throws IOException, ParseException {
    IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");
    Query query = new RangeQuery(new Term("sort","1"),new Term("sort","9"),true);
    //這個地方前面沒有提到,它是用于范圍的Query可以看一下幫助文檔.
    Hits hits = indexSearcher.search(query,new Sort(new SortField("sort",new MySortComparatorSource())));
    System.out.println("有"+hits.length()+"個結果");
    for(int i=0;i< hits.length();i++){
    Document doc = hits.doc(i);
    System.out.println(doc.get("sort"));
    }
    }
    public class MyScoreDocComparator implements ScoreDocComparator {
    private Integer[]sort;
    public MyScoreDocComparator(String s,IndexReader reader, String fieldname)
    throws IOException{
    sort = new Integer[reader.maxDoc()];
    for(int i = 0;i< reader.maxDoc();i++){
    Document doc =reader.document(i);
    sort[i]=new Integer(doc.get("sort"));
    }
    }
    public int compare(ScoreDoc i, ScoreDoc j){
    if(sort[i.doc]>sort[j.doc])
    return 1;
    if(sort[i.doc]< sort[j.doc])
    return -1;
    return 0;
    }
    public int sortType(){
    return SortField.INT;
    }
    public Comparable sortValue(ScoreDoc i){
    // TODO 自動生成方法存根
    return new Integer(sort[i.doc]);
    }
    }
    public class MySortComparatorSource implements SortComparatorSource {
    private static final long serialVersionUID = -9189690812107968361L;
    public ScoreDocComparator newComparator(IndexReader reader, String fieldname)
    throws IOException{
    if(fieldname.equals("sort"))
    return new MyScoreDocComparator("sort",reader,fieldname);
    return null;
    }
    }
    SearchSort1()輸出的結果沒有排序,SearchSort2()就排序了。 

    2.多域搜索 MultiFieldQueryParser

    1.如果想輸入關鍵字而不想關心是在哪個Field里的就可以用MultiFieldQueryParser了。
    用它的構造函數即可后面的和一個Field一樣。
    MultiFieldQueryParser. parse (String[] queries, String[] fields, BooleanClause.Occur[] flags, Analyzer analyzer)
                                             

    第三個參數比較特殊這里也是與以前lucene1.4.3不一樣的地方,看一個例子就知道了。
    String[] fields = {"filename", "contents", "description"};
    BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD,
    BooleanClause.Occur.MUST,//在這個Field里必須出現的
    BooleanClause.Occur.MUST_NOT};//在這個Field里不能出現
    MultiFieldQueryParser.parse("query", fields, flags, analyzer);

    2.多索引搜索 MultiSearcher
    在構造的時候傳進去一個Searcher數組即可

    3.過濾器Filter

    看個例子:

    public void FilterTest() throws IOException, ParseException {
    IndexWriter indexWriter = new IndexWriter("C:\\FilterTest",new StandardAnalyzer(),true);
    Document doc = new Document();
    doc.add(new Field("name","程序員之家",Field.Store.YES,Field.Index.TOKENIZED));
    indexWriter.addDocument(doc);
    doc=new Document();
    doc.add(new Field("name","程序員雜志",Field.Store.YES,Field.Index.TOKENIZED));
    indexWriter.addDocument(doc);
    indexWriter.close();
    Query query = null;
    Hits hits = null;
    IndexSearcher indexSearcher = new IndexSearcher("C:\\FilterTest");
    QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());
    query = queryParser.parse("程序");
    hits = indexSearcher.search(query,new Filter(){
    @Override
    public BitSet bits(IndexReader reader) throws IOException{
    BitSet bit = new BitSet(reader.maxDoc());
    for(int i=0;i< reader.maxDoc();i++){
    if(reader.document(i).get("name").enth("雜志"))//將以“雜志”后綴的過濾掉
    continue;
    bit.set(i);ks
    }
    return bit;
    }
    });
    System.out.println(hits.length());
    for(int i=0;i< hits.length();i++){
    doc =hits.doc(i);
    System.out.println(doc.get("name"));
    }
    }
    這只是一個入門的文檔Lucene 2.0的內容還有很多,這里只是介紹了一部分,其它的可以看幫助文檔來學習。
    北天JAVA技術網(www.java114.com))


    posted on 2007-05-21 08:17 java_蟈蟈 閱讀(611) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 无码专区AAAAAA免费视频| 国内精自视频品线六区免费| 成人无码视频97免费| 99在线在线视频免费视频观看 | 亚洲精品无码人妻无码| 四虎国产精品免费永久在线| 成年女人男人免费视频播放| 亚洲AV永久无码区成人网站| 黄页网址在线免费观看| 4虎永免费最新永久免费地址| 亚洲资源在线视频| 人妻在线日韩免费视频| 国产v片免费播放| 亚洲综合av一区二区三区| 最近免费中文在线视频| 亚洲国产精彩中文乱码AV| 国产精品福利片免费看| 日韩一区二区三区免费体验| 亚洲中字慕日产2020| 日本免费一区二区久久人人澡| 亚洲午夜av影院| 黄色a三级三级三级免费看| 亚洲午夜福利精品无码| 污污网站18禁在线永久免费观看| 亚洲综合激情视频| 亚洲精品视频免费在线观看| 在线综合亚洲中文精品| 日韩在线免费视频| 亚洲va精品中文字幕| 国产精品国产午夜免费福利看| 亚洲夂夂婷婷色拍WW47 | 国产综合亚洲专区在线| 精品一区二区三区无码免费直播| 亚洲色图综合在线| 免费国产黄网站在线观看可以下载| 2019亚洲午夜无码天堂| 在线a亚洲v天堂网2019无码| 91成年人免费视频| 岛国精品一区免费视频在线观看| 亚洲一区中文字幕在线观看| 亚洲国产一区视频|