日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Lucene 中的Tokenizer, TokenFilter学习

發布時間:2025/4/5 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Lucene 中的Tokenizer, TokenFilter学习 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://brandnewuser.iteye.com/blog/2305140

lucene中的TokenStream,TokenFilter之間關系 TokenStream是一個能夠在被調用后產生語匯單元序列的類,其中有兩個類型:Tokenizer和TokenFilter,兩者的不同在于TokenFilter中包含了一個TokenStream作為input,該input仍然可以為一種TokenFilter進行遞歸封裝,是一種組合模式;而Tokenzier接受一個Reader對象讀取字符并創建語匯單元,TokenFilter負責處理輸入的語匯單元,通過新增、刪除或者修改屬性的方式來產生新的語匯單元。

? 對照我們之前分析的同義詞TokenizerFactory相關配置,其數據流的過程如下: Java代碼??
  • java.io.Reader?->?com.chenlb.mmseg4j.solr.MMSegTokenizer?->?SynonymFilter?->?StopFilter?->?WordDelimiterFilter?->?LowerCaseFilter?->?RemoveDuplicatesTokenFilter??
  • ? 對于某些TokenFilter來說,在分析過程中對事件的處理順序非常重要。當指定過濾操作順序時,還應該考慮這樣的安排對于應用程序性能可能造成的影響。 在solr中,schema.xml(最新版本已經修改為managed-schema)的作用是告訴solr該如何對輸入的文檔進行索引。 http://www.liaozhida.net/solr/solr%E7%B3%BB%E5%88%97%E4%B8%83%E8%AF%A6%E8%A7%A3schema-xml%E7%89%B9%E6%80%A7.html 對于每個不同的field,需要設置其對應的數據類型,數據類型決定了solr如何去解釋每個字段,以及怎樣才能搜索到這個字段。在字段分析器中(field analyzers),指導solr怎樣對輸入的數據進行處理然后再構建出索引,類似于文本處理器或者文本消化器。 當一個document被索引或者檢索操作的時候,分析器Analyzer會審閱字段field的文本內容,然后生成一個token流,analyzer可以由多個tokenizer和filter組成;tokenizer可以將field字段的內容切割成單個詞或token,進行分詞處理;filters可以接收tokenizer分詞輸出的token流,進行轉化過濾處理,例如對詞元進行轉換(簡繁體轉換),舍棄無用詞元(虛詞謂詞)。tokenizer和filter一起組成一個管道或者鏈條,對輸入的文檔和輸入的查詢文本進行處理,一系列的tokenizer和filter被稱為分詞器analyzer,得到的結果被存儲成為索引字典用來匹配查詢輸入條件。 此外,我們還可以將索引分析器和查詢分析器分開,例如下面的字段配置的意思:對于索引,先經過一個基本的分析器,然后轉換為小寫字母,接著過濾掉不在keepword.txt中的詞,最后將剩下的詞元轉換為同義詞;對于查詢,先經過一個基本的分詞器,然后轉換為小寫字母就可以了。 Java代碼??
  • <fieldType?name="nametext"?class="solr.TextField">??
  • ??<analyzer?type="index">??
  • ????<tokenizer?class="solr.StandardTokenizerFactory"/>??
  • ????<filter?class="solr.LowerCaseFilterFactory"/>??
  • ????<filter?class="solr.KeepWordFilterFactory"?words="keepwords.txt"/>??
  • ????<filter?class="solr.SynonymFilterFactory"?synonyms="syns.txt"/>??
  • ??</analyzer>??
  • ??<analyzer?type="query">??
  • ????<tokenizer?class="solr.StandardTokenizerFactory"/>??
  • ????<filter?class="solr.LowerCaseFilterFactory"/>??
  • ??</analyzer>??
  • </fieldType>??
  • ? 在Lucene實戰一書中,詳解了如何從頭編寫一個同義詞Analyzer,通過改寫termAttribute以及positionIncrementAttribute的方式來達到實現同義詞的方式,不過由于書上的示例比較陳舊,而charTermAttribute不能達到修改同義詞元的目的(只能進行append),因此替換最終的目的沒有達到。 Java代碼??
  • public?class?SynonymFilter?extends?TokenFilter?{??
  • ??
  • ????private?static?final?String?TOKEN_TYPE_SYNONYM?=?"SYNONYM";??
  • ??
  • ????private?Stack<String>?synonymStack;??
  • ????private?SynonymEngine?synonymEngine;??
  • ????private?AttributeSource.State?current;??
  • ????private?final?CharTermAttribute?bytesTermAttribute;??
  • ????private?final?PositionIncrementAttribute?positionIncrementAttribute;??
  • ??
  • ????/**?
  • ?????*?Construct?a?token?stream?filtering?the?given?input.?
  • ?????*?
  • ?????*?@param?input?
  • ?????*/??
  • ????protected?SynonymFilter(TokenStream?input,?SynonymEngine?synonymEngine)?{??
  • ????????super(input);??
  • ????????this.synonymEngine?=?synonymEngine;??
  • ????????synonymStack?=?new?Stack<>();??
  • ??
  • ????????this.bytesTermAttribute?=?addAttribute(CharTermAttribute.class);??
  • ????????this.positionIncrementAttribute?=?addAttribute(PositionIncrementAttribute.class);??
  • ????}??
  • ??
  • ????@Override??
  • ????public?boolean?incrementToken()?throws?IOException?{??
  • ????????if?(!synonymStack.isEmpty())?{??
  • ????????????String?syn?=?synonymStack.pop();??
  • ????????????restoreState(current);??
  • ??
  • //????????????bytesTermAttribute.setBytesRef(new?BytesRef(syn.getBytes()));??
  • //????????????bytesTermAttribute.resizeBuffer(0);??
  • ????????????bytesTermAttribute.append(syn);??
  • ??
  • ????????????positionIncrementAttribute.setPositionIncrement(0);??
  • ????????????return?true;??
  • ????????}??
  • ??
  • ????????if?(!input.incrementToken())?{??
  • ????????????return?false;??
  • ????????}??
  • ??
  • ????????if?(addAliasesToStack())?{??
  • ????????????current?=?captureState();??
  • ????????}??
  • ??
  • ????????return?true;??
  • ????}??
  • ??
  • ????private?boolean?addAliasesToStack()?throws?IOException?{??
  • ????????String[]?synonyms?=?synonymEngine.getSynonyms(bytesTermAttribute.toString());??
  • ????????if?(synonyms?==?null)?{??
  • ????????????return?false;??
  • ????????}??
  • ????????for?(String?synonym?:?synonyms)?{??
  • ????????????synonymStack.push(synonym);??
  • ????????}??
  • ????????return?true;??
  • ????}??
  • }??
  • ? Analyzer,用于將tokenizer和filter串聯起來: Java代碼??
  • public?class?SynonymAnalyzer?extends?Analyzer?{??
  • ????@Override??
  • ????protected?TokenStreamComponents?createComponents(String?fieldName)?{??
  • ????????StandardTokenizer?source?=?new?StandardTokenizer();??
  • ????????return?new?TokenStreamComponents(source,?new?SynonymFilter(new?StopFilter(new?LowerCaseFilter(source),??
  • ????????????????new?CharArraySet(StopAnalyzer.ENGLISH_STOP_WORDS_SET,?true)),?new?TestSynonymEngine()));??
  • ????}??
  • }??
  • ? 我們定義一個簡易的同義詞匹配引擎: Java代碼??
  • public?interface?SynonymEngine?{??
  • ????String[]?getSynonyms(String?s)?throws?IOException;??
  • }??
  • ??
  • public?class?TestSynonymEngine?implements?SynonymEngine?{??
  • ??
  • ????public?static?final?Map<String,?String[]>?map?=?new?HashMap<>();??
  • ??
  • ????static?{??
  • ????????map.put("quick",?new?String[]{"fast",?"speedy"});??
  • ????}??
  • ??
  • ????@Override??
  • ????public?String[]?getSynonyms(String?s)?throws?IOException?{??
  • ????????return?map.get(s);??
  • ????}??
  • }??
  • ? 對最終結果進行測試: Java代碼??
  • public?static?void?main(String[]?args)?throws?IOException?{??
  • ????????SynonymAnalyzer?analyzer?=?new?SynonymAnalyzer();??
  • ????????TokenStream?tokenStream?=?analyzer.tokenStream("contents",?new?StringReader("The?quick?brown?fox"));??
  • ????????tokenStream.reset();??
  • ??
  • ????????CharTermAttribute?charTermAttribute?=?tokenStream.addAttribute(CharTermAttribute.class);??
  • ????????OffsetAttribute?offsetAttribute?=?tokenStream.addAttribute(OffsetAttribute.class);??
  • ????????PositionIncrementAttribute?positionIncrementAttribute?=??
  • ????????????????tokenStream.addAttribute(PositionIncrementAttribute.class);??
  • ????????TypeAttribute?typeAttribute?=?tokenStream.addAttribute(TypeAttribute.class);??
  • ??
  • ????????int?position?=?0;??
  • ????????while?(tokenStream.incrementToken())?{??
  • ????????????int?positionIncrement?=?positionIncrementAttribute.getPositionIncrement();??
  • ????????????if?(positionIncrement?>?0)?{??
  • ????????????????position?+=?positionIncrement;??
  • ????????????????System.out.println();??
  • ????????????????System.out.print(position?+?"?:?");??
  • ????????????}??
  • ??
  • ????????????System.out.printf("[%s?:?%d?->??%d?:?%s]",?charTermAttribute.toString(),?offsetAttribute.startOffset(),?offsetAttribute.endOffset(),??
  • ????????????????????typeAttribute.type());??
  • ????????}??
  • ? 測試出的結果,可以看出位置1的謂詞the已經被剔除,位置2處加入了較多的同義詞,由于使用的append,所以同義詞記在了一起。 Java代碼??
  • 2?:?[quick?:?4?->??9?:?<ALPHANUM>][quickspeedy?:?4?->??9?:?<ALPHANUM>][quickfast?:?4?->??9?:?<ALPHANUM>]??
  • 3?:?[brown?:?10?->??15?:?<ALPHANUM>]??
  • 4?:?[fox?:?16?->??19?:?<ALPHANUM>]??
  • ? Solr同義詞設置 Solr中的同義詞使用的是 SynonymFilterFactory 來進行加載的,我們需要在定義schema時,對某個字段設置同義詞時,可以使用: Java代碼??
  • <fieldtype?name="textComplex"?class="solr.TextField"?positionIncrementGap="100">??
  • ????????<analyzer?type="index">??
  • ????????????<tokenizer?class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory"?mode="complex"?dicPath="/Users/mazhiqiang/develop/tools/solr-5.5.0/server/solr/product/conf/dic"?/>??
  • ????????????<filter?class="solr.StopFilterFactory"?ignoreCase="false"?words="stopwords.txt"/>??
  • ????????????<filter?class="solr.WordDelimiterFilterFactory"/>??
  • ????????????<filter?class="solr.LowerCaseFilterFactory"/>??
  • ????????????<filter?class="solr.NGramFilterFactory"?minGramSize="1"?maxGramSize="20"/>??
  • ????????????<filter?class="solr.StandardFilterFactory"/>??
  • ????????</analyzer>??
  • ????????<analyzer?type="query">??
  • ????????????<tokenizer?class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory"?mode="complex"?dicPath="/Users/mazhiqiang/develop/tools/solr-5.5.0/server/solr/product/conf/dic"?/>??
  • ????????????<filter?class="solr.SynonymFilterFactory"?synonyms="synonyms.txt"?ignoreCase="true"?expand="true"/>??
  • ????????????<filter?class="solr.StopFilterFactory"?ignoreCase="false"?words="stopwords.txt"/>??
  • ????????????<filter?class="solr.WordDelimiterFilterFactory"/>??
  • ????????????<filter?class="solr.LowerCaseFilterFactory"/>??
  • ????????????<!--??<filter?class="solr.EdgeNGramFilterFactory"?minGramSize="1"?maxGramSize="20"/>?-->??
  • ????????????<filter?class="solr.RemoveDuplicatesTokenFilterFactory"/>??
  • ????????</analyzer>??
  • ????</fieldtype>??
  • ? 需要配置對應的 synonyms 屬性,指定 定義同義詞的配置文件,設置是否忽略大小寫等屬性。 而在加載同義詞時,對文件進行逐行讀取(使用LineNumberReader),對于每一行的數據,先使用 => 作為分隔符,同義詞在左右兩邊(左邊作為input,右邊作為output)都可以配置成多個,以逗號分隔,最后以笛卡爾積的形式將其放至map中。 Java代碼??
  • String?line?=?null;??
  • ????while?((line?=?in.readLine())?!=?null)?{??
  • ??????if?(line.length()?==?0?||?line.charAt(0)?==?'#')?{??
  • ????????continue;?//?ignore?empty?lines?and?comments??
  • ??????}??
  • ??
  • ??????//?TODO:?we?could?process?this?more?efficiently.??
  • ??????String?sides[]?=?split(line,?"=>");??
  • ??????if?(sides.length?>?1)?{?//?explicit?mapping??
  • ????????if?(sides.length?!=?2)?{??
  • ??????????throw?new?IllegalArgumentException("more?than?one?explicit?mapping?specified?on?the?same?line");??
  • ????????}??
  • ????????String?inputStrings[]?=?split(sides[0],?",");??
  • ????????CharsRef[]?inputs?=?new?CharsRef[inputStrings.length];??
  • ????????for?(int?i?=?0;?i?<?inputs.length;?i++)?{??
  • ??????????inputs[i]?=?analyze(unescape(inputStrings[i]).trim(),?new?CharsRefBuilder());??
  • ????????}??
  • ??
  • ????????String?outputStrings[]?=?split(sides[1],?",");??
  • ????????CharsRef[]?outputs?=?new?CharsRef[outputStrings.length];??
  • ????????for?(int?i?=?0;?i?<?outputs.length;?i++)?{??
  • ??????????outputs[i]?=?analyze(unescape(outputStrings[i]).trim(),?new?CharsRefBuilder());??
  • ????????}??
  • ????????//?these?mappings?are?explicit?and?never?preserve?original??
  • ????????for?(int?i?=?0;?i?<?inputs.length;?i++)?{??
  • ??????????for?(int?j?=?0;?j?<?outputs.length;?j++)?{??
  • ????????????add(inputs[i],?outputs[j],?false);??
  • ??????????}??
  • ????????}??
  • ? 所有的同義詞加載完成后,會生成一個SynonymMap,該map就被用來在全文檢索的過程中進行同義詞替換。 在我們對某個單詞進行查詢時,可以查詢到我們設置的字段query分析器結構,生成一個TokenizerChain對象,對應的Tokenizer為我們設置的分詞器,filters為我們設置的過濾器鏈條,會根據過濾器鏈條Chain進行


    ?
    ? 通過input的方式設置同義詞Filter,組成該鏈條結果。 Java代碼??
  • @Override??
  • ??protected?TokenStreamComponents?createComponents(String?fieldName)?{??
  • ????Tokenizer?tk?=?tokenizer.create();??
  • ????TokenStream?ts?=?tk;??
  • ????for?(TokenFilterFactory?filter?:?filters)?{??
  • ??????ts?=?filter.create(ts);??
  • ????}??
  • ????return?new?TokenStreamComponents(tk,?ts);??
  • ??}??
  • ? 而具體到每個FilterFactory,例如SynonymFilterFactory,都通過create方法來創建對應的Filter用于同義詞過濾。 Java代碼??
  • @Override??
  • ??public?TokenStream?create(TokenStream?input)?{??
  • ????//?if?the?fst?is?null,?it?means?there's?actually?no?synonyms...?just?return?the?original?stream??
  • ????//?as?there?is?nothing?to?do?here.??
  • ????return?map.fst?==?null???input?:?new?SynonymFilter(input,?map,?ignoreCase);??
  • ??}??
  • ? 創建一個SynonymFilter來進行最后真正的篩選,將同義詞進行替換,整體的類結構圖如下:

    ? lucene內置的Token lucene中除了內置的幾個Tokenizer,在solr中的field analyzer以及index中也得到了應用,下面就對這幾種filter進行測試,我們分析的文本為:Please email clark.ma@gmail.com by 09, re:aa-bb
    StandardAnalyzer 1 : [please : 0 ->? 6 : <ALPHANUM>] 2 : [email : 7 ->? 12 : <ALPHANUM>] 3 : [clark.ma : 13 ->? 21 : <ALPHANUM>] 4 : [gmail.com : 22 ->? 31 : <ALPHANUM>] 6 : [09 : 35 ->? 37 : <NUM>] 7 : [re:aa : 39 ->? 44 : <ALPHANUM>] 8 : [bb : 45 ->? 47 : <ALPHANUM>] 去除空格,標點符號,@;
    ClassicAnalyzer 1 : [please : 0 ->? 6 : <ALPHANUM>] 2 : [email : 7 ->? 12 : <ALPHANUM>] 3 : [clark.ma@gmail.com : 13 ->? 31 : <EMAIL>] 5 : [09 : 35 ->? 37 : <ALPHANUM>] 6 : [re : 39 ->? 41 : <ALPHANUM>] 7 : [aa : 42 ->? 44 : <ALPHANUM>] 8 : [bb : 45 ->? 47 : <ALPHANUM>] 能夠識別互聯網域名和email地址,
    LetterTokenizer 1 : [Please : 0 ->? 6 : word] 2 : [email : 7 ->? 12 : word] 3 : [clark : 13 ->? 18 : word] 4 : [ma : 19 ->? 21 : word] 5 : [gmail : 22 ->? 27 : word] 6 : [com : 28 ->? 31 : word] 7 : [by : 32 ->? 34 : word] 8 : [re : 39 ->? 41 : word] 9 : [aa : 42 ->? 44 : word] 10 : [bb : 45 ->? 47 : word] 丟棄掉所有的非文本字符
    KeywordTokenizer 1 : [Please email clark.ma@gmail.com by 09, re:aa-bb : 0 ->? 47 : word] 將整個文本當做一個詞元
    LowerCaseTokenizer 1 : [please : 0 ->? 6 : word] 2 : [email : 7 ->? 12 : word] 3 : [clark : 13 ->? 18 : word] 4 : [ma : 19 ->? 21 : word] 5 : [gmail : 22 ->? 27 : word] 6 : [com : 28 ->? 31 : word] 7 : [by : 32 ->? 34 : word] 8 : [re : 39 ->? 41 : word] 9 : [aa : 42 ->? 44 : word] 10 : [bb : 45 ->? 47 : word] 對其所有非文本字符,過濾空格,標點符號,將所有的大寫轉換為小寫
    NGramTokenizer 可以定義最小minGramSize(default=1), 最大切割值maxGramSize(default=2),生成的詞元較多。 假設minGramSize=2, maxGramSize=3,輸入abcde,輸出:ab abc abc bc bcd cd cde 讀取字段并在給定范圍內生成多個token
    PathHierachyTokenizer c:\my document\filea\fileB,new PathHierarchyTokenizer('\\', '/') 1 : [c: : 0 ->? 2 : word][c:/my document : 0 ->? 14 : word][c:/my document/filea : 0 ->? 20 : word][c:/my document/filea/fileB : 0 ->? 26 : word] 使用新的文件目錄符去代替文本中的目錄符
    PatternTokenizer 需要兩個參數,pattern正則表達式,group分組。 pattern=”[A-Z][A-Za-z]*” group=”0″ 輸入: “Hello. My name is Inigo Montoya. You killed my father. Prepare to die.” 輸出: “Hello”, “My”, “Inigo”, “Montoya”, “You”, “Prepare” 進行正則表達式分組匹配
    UAX29URLEmailTokenizer 1 : [Please : 0 ->? 6 : <ALPHANUM>] 2 : [email : 7 ->? 12 : <ALPHANUM>] 3 : [clark.ma@gmail.com : 13 ->? 31 : <EMAIL>] 4 : [by : 32 ->? 34 : <ALPHANUM>] 5 : [09 : 35 ->? 37 : <NUM>] 6 : [re:aa : 39 ->? 44 : <ALPHANUM>] 7 : [bb : 45 ->? 47 : <ALPHANUM>] 去除空格和標點符號,但保留url和email連接
    Lucene內置的TokenFilter 過濾器能夠組成一個鏈表,每一個過濾器處理上一個過濾器處理過后的詞元,所以過濾器的排序很有意義,第一個過濾器最好能處理大部分常規情況,最后一個過濾器是帶有針對特殊性的。

    ClassicFilter“I.B.M. cat’s can’t” ==> “I.B.M”, “cat”, “can’t”經典過濾器,可以過濾無意義的標點,需要搭配ClassicTokenizer使用
    ApostropheFilter 1 : [abc : 0 ->? 3 : <ALPHANUM>] 2 : [I.B.M : 4 ->? 9 : <ALPHANUM>] 3 : [cat : 10 ->? 15 : <ALPHANUM>] 4 : [can : 16 ->? 21 : <ALPHANUM>] 省略所有的上撇號
    LowerCaseFilter 1 : [i.b.m : 0 ->? 5 : <ALPHANUM>] 2 : [cat's : 6 ->? 11 : <ALPHANUM>] 3 : [can't : 12 ->? 17 : <ALPHANUM>] 轉換成小寫
    TypeTokenFilter <filter class=”solr.TypeTokenFilterFactory” types=”email_type.txt” useWhitelist=”true”/> 如果email_type.txt設置為ALPHANUM,會保留該類型的所有分析結果,否則會被刪除掉 給定一個文件并設置成白名單還是黑名單,只有符合條件的type才能被保留
    TrimFilter?去掉空格
    TruncateTokenFilter 1 : [I.B : 0 ->? 5 : <ALPHANUM>] 2 : [cat : 6 ->? 11 : <ALPHANUM>] 3 : [can : 12 ->? 17 : <ALPHANUM>] 截取文本長度,左邊為prefixLength=3
    PatternCaptureGroupFilter可配置屬性pattern和preserve_original(是否保留原文)從輸入文本中保留能夠匹配正則表達式的
    PatternReplaceFilter??
    StopFilter?創建一個自定義的停詞詞庫列表,過濾器遇到停詞就直接過濾掉
    KeepWordFilter與StopFilter的含義正好相反?
    LengthFilter設置一個最小值min和最大值max為詞元的長度設置在一個固定范圍
    WordDelimiterFilter

    A:-符號 wi-fi 變成wi fi
    B:駝峰寫法 LoveSong 變成 love song 對應參數
    C:字母-數字 xiaomi100 變成 xiaomi 100
    D:–符號 like–me 變成 like me
    E:尾部的’s符號 mother’s 變成 mother
    F:-符號 wi-fi 變成 wifi 于規則A不同的是沒有分成兩個詞元
    G:-符號,數字之間 400-884586 變成 400884586
    H:-符號 無論字母還是數字,都取消-符號 wi-fi-4 變成wifi4

    其他參數
    splitOnCaseChange=”1″ 默認1,關閉設為0 規則B
    generateWordParts=”1″ 默認1 ,對應規則AB
    generateNumberParts=”1″ 默認1 對應規則F
    catenateWords=”1″ 默認0 對應規則A
    splitOnNumerics=”1″ 默認1,關閉設0 規則C
    stemEnglishPossessive 默認1,關閉設0 規則E
    catenateNumbers=”1″ 默認0 對應規則G
    catenateAll=”1″ 默認0 對應規則 H
    preserveOriginal=”1″ 默認0 對詞元不做任何修改 除非有其他參數改變了詞元 protected=”protwords.txt” 指定這個單詞列表的單詞不被修改
    通過分隔符分割單元

    轉載于:https://www.cnblogs.com/davidwang456/articles/10470938.html

    總結

    以上是生活随笔為你收集整理的Lucene 中的Tokenizer, TokenFilter学习的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。