åããŸããŠãä»å¹ŽåºŠæ°åå
¥ç€Ÿã® mako _makokã§ããæè¿å®å®¶ã«åž°ã£ãŠæ°Žæé€šã§ãã³ã®ã³ãèŠãŠããŸããã ä»å㯠å
šææ€çŽ¢ãšã³ãžã³ ã®ã³ã¢æ©èœã®äžã€ã§ããAnalyzerã«ã€ããŠæžããŠãããããšæããŸãã ã¯ããã« æ€çŽ¢ãšã³ãžã³ã®ä»çµã¿ Analyzerãšã¯ åæºå Char filter Tokenizer Token filter ãããã« ã¯ããã« ç§ã¯çŸåšãå人çã« å
šææ€çŽ¢ãšã³ãžã³ åŠç¿ãããŠããŸãã 以åãŸã§ã¯è«žäºæ
ã§ Apache Solrããã£ãŠããã®ã§ããã以äžã®çç±ããElasticsearchã®åŠç¿ã«åãæ¿ããŸããã ã·ã§ã¢ãšããã«äŒŽãããã¥ã¡ã³ãã®å
å® Kibanaãã¯ãããšããElastic Stackã®ååš ã¯ãšãªã®æžãæ¹èŠãããããæãã«ã¯ãšãªæžããã Apache Solråã³Elasticsearchã§ã¯ Apache Lucene ãã OSS ã® å
šææ€çŽ¢ ã©ã€ãã©ãªãã³ã¢ã«ãªã£ãŠãããŸãã Lucene ã«ã¯ Analyzer ãšããæ©èœãããã å
šææ€çŽ¢ãšã³ãžã³ ã«ãããŠéåžžã«éèŠãªæ©èœã§ãã ä»åã¯å®éã«Elasticsearchã§Analyzerãèšå®ããªãããAnalyzerã®ä»çµã¿ãèŠãŠãããããšæããŸãã æ€çŽ¢ãšã³ãžã³ ã®ä»çµã¿ ãŸãã¯ç°¡åã« æ€çŽ¢ãšã³ãžã³ ã®ä»çµã¿ã説æããŸãã æ€çŽ¢ãšã³ãžã³ ã§ã¯ãããããããã¥ã¡ã³ãã®ã€ã³ããã¯ã¹ãäœæããŠããããããžã¯ãšãªãæãããããšããã«ããããããã®ãããããããšããã®ãåºæ¬ã«ãªããŸãã Lucene ã§ã¯ 転眮ã€ã³ããã¯ã¹ ãšããæ¹åŒãæ¡çšãããŠããŸãã 転眮ã€ã³ããã¯ã¹ ã¯ä»¥äžã®ãããªåœ¢ã§äœæãããŸãã 転眮ã€ã³ããã¯ã¹ äœããã®æ¹æ³ã§ããã¥ã¡ã³ãã®ããã¹ããåå²ããããã«å¯Ÿå¿ããããã¥ã¡ã³ãIDããªã¹ãã«ãªããŸã ã¯ãšãªãæããããéãåæ§ãã¯ãšãªã®ããã¹ããåå²ããã€ã³ããã¯ã¹ã®ããŒã«ãããããæç« ãè¿ããšããä»çµã¿ã§ãã Analyzerãšã¯ å
ã»ã©ã¯ æ€çŽ¢ãšã³ãžã³ ã®ä»çµã¿ã«ã€ããŠèª¬æããŸããã äœããã®æ¹æ³ã§åå²ãã ãšãããŸãããããã®éšåãAnalyzerã®ä»äºã«ãªããŸãã æ£ç¢ºã«ã¯ãAnalyzerã¯ç®çã®ã€ã³ããã¯ã¹ãäœæããããã«ãããã¹ãã®åå²ãæ£èŠåãªã©ãè¡ããŸãã Analyzerã¯ä»¥äžã®3ã€ã®æ©èœã§æ§æãããŠããŸãã Char filter Tokenizer Token filter Analyzerã®åŠçãããŒã§ãã Char filter â Tokenizer â Token filterã®é ã«åŠçãããŸãã Tokenizerã¯å¿
é ã»ãã€äžã€ ããèšå®ã§ããŸããã Analyzerã®åŠçæé åæºå ä»åã¯Elasticsearchã§åäœç¢ºèªãè¡ããŸãã Analyzerãã©ã®ãããªåããããŠãããã©ããã ãç¥ãããæ¹ã¯é£ã°ããŠãã ããã Java 8以äžã®ç°å¢ãå¿
é ã§ãã Elasticsearchãã€ã³ã¹ããŒã« # brew brew install elasticsearch # yum yum install elasticsearch æ¥æ¬èªé¢é£ã®æ©èœãšãããã¹ãã®æ£èŠåã匷åãã2ã€ã® ãã©ã°ã€ã³ ãã€ã³ã¹ããŒã«ããŸã analysis-kuromoj analysis- icu cd { ES_HOME } bin/elasticsearch-plugin install analysis-kuromoji bin/elasticsearch-plugin install analysis-icu Elasticsearchãèµ·åããŠèµ·å確èªãè¡ããŸã # èµ·å bin/elasticsearch # èµ·åç¢ºèª curl http://localhost:9200/ " name " : " 1GpZYN9 " , " cluster_name " : " elasticsearch " , " cluster_uuid " : " hoge " , " version " : { " number " : " 6.8.3 " , " build_flavor " : " oss " , " build_type " : " tar " , " build_hash " : " 0c48c0e " , " build_date " : " 2019-08-29T19:05:24.312154Z " , " build_snapshot " : false , " lucene_version " : " 7.7.0 " , " minimum_wire_compatibility_version " : " 5.6.0 " , " minimum_index_compatibility_version " : " 5.0.0 " } , " tagline " : " You Know, for Search " } ããã§æºåå®äºã§ããããããå®éã«AnalyzerãäœæããªãããChar filter, Tokenizer, Token filterã«ã€ããŠãããã解説ããŸãã Char filter Char filterã®ãã€ã³ãã¯ä»¥äžã®éãã§ãã ããã¹ãã«å¯ŸããŠ æ©æ¢°ç ã«ååŠçãè¡ã å¿
é ã§ã¯ãªã ããã€ã§ãèšå®ã§ãã æ©æ¢°ç ãªååŠçãšããã®ã¯ãäŸãã°æåã®å
šè§âåè§åŠçã ã£ããã æ£èŠè¡šçŸ ã«ããæœåºãªã©ãæããããŸãã ããããããããAnalyzerã®èšå®ãè¡ã£ãŠãããŸãã Elasticsearchã§ã¯æ§ã
ãªæ©èœã REST API ã§æäœã§ããŸãã ä»åã¯Char filterã«ä»¥äžãèšå®ããŸãã icu _normalizer èšå·ã»æ°åã» ç¹æ®æå ãªã©ã®æ£èŠåãè¡ã kuromoji_iteration_mark èžãåãæ£èŠå èžãåãšã¯ã
ããœããã®ãããªå眮ã®åèªã«ãã£ãŠèªã¿æ¹ãå€åããåèªã®ããšã§ã Analyzerãèšå®ããã«ã¯ã€ã³ããã¯ã¹ãä¿ç®¡ããŠããã¹ããŒã¹ãäœæããªããã°ãªããŸããã ä»åã¯ã€ã³ããã¯ã¹åã analyzer_handson ãšããŠãAnalyzerãèšå®ããŠãããŸãã 以äžã® json ãmy_kuromoji_analyzer. json ãšä¿åããPOSTããŸãã { " settings ": { " analysis ": { " analyzer ": { " my_kuromoji_analyzer ": { " type ": " custom ", " char_filter " : [ " icu_normalizer ", " kuromoji_iteration_mark " ] , " tokenizer ": " keyword " } } } } } json ã localhost :9200/analyzer_handson/ ã«PUTããŸãã curl -XPUT localhost:9200/analyzer_handson/ -H " Content-type: application/json " -d @my_kuromoji_analyzer.json ããã§ my_kuromoji_analyzer ãšããAnalyzerãèšå®ãããŸããã æ©éããã¹ããã¢ãã©ã€ãºããŠãããŸãã ~/_analyze ããšã³ããã€ã³ãã«ãªã£ãŠããããã¡ãã§ç¹å®ã®Analyzerã®æåã確èªããããšãã§ããŸãã ããã«ä»¥äžã® json ãPOSTããŸãã { " analyzer ": " my_kuromoji_analyzer ", " text ": " ã³ãŠãã€ãã³ã®ã³ã¯äœæ Œã®ãããã®ã¯ïŒïŒïŒã¢ãããšãããåã¯åºŠã
ãã³ã³ãã¥ãŒã¿ãŒã§ãããèŠãŠåãã§ãã " } curl -XPOST localhost:9200/analyzer_handson/_analyze -H " Content-Type: application/json " -d @query.json 以äžã®ãããªçµæãè¿ã£ãŠããŸãã { " tokens ": [ { " token ":" ã³ãŠãã€ãã³ã®ã³ã¯äœæ Œã®ãããã®ã¯130ã»ã³ããããšãããåã¯åºŠåºŠãã³ã³ãã¥ãŒã¿ãŒã§ãããèŠãŠåãã§ãã ", " start_offset ": 0 , " end_offset ": 50 , " type ":" word ", " position ": 0 } ] } ç¡äºæ£èŠåãããŠããŸãã ïŒïŒïŒã¯å
šè§æ°åâåè§æ°å ã¢âã»ã³ã 床ã
â床床 ã«å€æãããŠããŸãã ãã®ãããªæ£èŠåã®åŠç㯠èªç¶èšèªåŠç ã®ååŠçãšããŠéåžžã«éèŠã§ãã å
éšçã«ã¯åè§æ°åãšå
šè§æ°åãªã©ã¯éãæåãšããŠæ±ããããããæ€çŽ¢ã®ãã€ãºã«ãªãããšããéã«æ¬²ããããã¥ã¡ã³ãããããããªããªã©ã®åé¡ãçºçããŸãã èžãåãªã©ã¯äž»ã«å€å
žãªã©ã§ãã€ãºã«ãªãããšãå€ãã§ãã ä»ã«ãChar filterã¯ããããããã®ã§ãè²ã
詊ããŠãããããšããã§ãã Tokenizer Tokenizerã¯ããã¹ãã åãã¡æžã ããŸãã åãã¡æžã ãšã¯ãç¹å®ã®èŠåã«ä¹ã£åã£ãŠããã¹ããåå²ããããšã§ãã ã§ã¯ã©ã®ããã« åãã¡æžã ããã®ãã§ãããæ¥æ¬èªã®å Žå㯠圢æ
çŽ è§£æ ããã㯠N-gram ã䜿çšããããšãå€ãã§ãã 圢æ
çŽ è§£æ ã N-gram ã®Tokenizerã«ã¯ä»¥äžã®ãããªãã®ããããŸãã kuromoji_tokenizer æ¥æ¬èªçšã® 圢æ
çŽ è§£æ åšã§ããKuromojiã䜿çšã㊠圢æ
çŽ è§£æ ãè¡ã èŸæžã¯2007幎ããã¡ã³ããã³ã¹ãããŠããªããããæ°èªïŒèžèœäººã®ååãæµè¡èªïŒãªã©ã«åŒ±ã *1 N-gram Tokenizer æåæ°ã§ æ©æ¢°ç ã«åºåãã åãã¡æžã ãè¡ã 1-gramãuni-gram, 2-gramãbi-gram, 3-gramãtri-gramãšåŒã¶ 圢æ
çŽ è§£æ ã§ã¯ãããã¹ããåè©åäœã§åè§£ããåå²ãããåèªã®ããšã ããŒã¯ ã³ãšåŒã³ãŸãã åè§£ãã ããŒã¯ ã³ã«ã¯åè©ã®æ
å ±ã¯ãã¡ãããæŽ»çšåœ¢ãªã©ã®æ
å ±ãä»äžãããŸãã Elasticsearchã§ã¯ Kuromoji ãšããæ¥æ¬èª 圢æ
çŽ è§£æ åšãçšããããŠããŸãã ããäžã€ã® åãã¡æžã ã®ææ®µã® N-gram ã«ã€ããŠã§ãã äŸãã°tri-graã ãšãã®ããã«åè§£ãããŸã N-gram ä»åã¯tri-gramãªã®ã§ããã¹ãã3ã€ã«åºåããªããäžã€ãã€æšªã«ããããŠããæãã§ãã ããã2ã€ã®ææ³ã¯ããããå©ç¹ã»æ¬ ç¹ããããŸã 圢æ
çŽ è§£æ èŸæžããŒã¹ã§åºåããããèŸæžã«èŒã£ãŠããåèªã«ã€ããŠã¯æ¯èŒçé«ãç²ŸåºŠã®æ€çŽ¢çµæãåŸããã éã«èŸæžã«èŒã£ãŠããªãåèªãªã©ã¯1æååºåãã§ åãã¡æžã ããããããæªç¥ã®åèªã«åŒ±ã N-gram æ©æ¢°ç ã«åºåããããããæªç¥ã®åèªãªã©ã§ãããã¥ã¡ã³ããããããããããšãã§ãã "京éœã§éã¶"ã®ãããªåèªã§æ€çŽ¢ããããšããäžèšã®ç»åã®ããã«"æ±äº¬éœ"ãå«ãããã¥ã¡ã³ããããããã æ€çŽ¢ãã€ãºãå¢ãããã ã©ã¡ããäžé·äžçãªã®ã§ãæ€çŽ¢èŠä»¶ã«ãã£ãŠé©åã«Tokenizerãèšå®ããå¿
èŠããããŸãã ä»å㯠圢æ
çŽ è§£æ ãçšããŠåèªåå²ããŠãããããšæããŸãã Analyzerã®èšå®ãæŽæ°ããããã«ããŸãã¯äžæŠã€ã³ããã¯ã¹ãcloseããŸãã curl -XPOST localhost:9200/analyzer_handson/_close 次ã«ãChar filterã®æã«äœ¿çšããmy_kuromoji_analyzer. json ã®tokenizerã kuromoji_tokenizer ã«ç·šéãPOSTããŸãã { " settings ": { " analysis ": { " analyzer ": { " my_kuromoji_analyzer ": { " type ": " custom ", " char_filter " : [ " icu_normalizer ", " kuromoji_iteration_mark " ] , " tokenizer ": " kuromoji_tokenizer " } } } } } # æŽæ°ããéã¯/_settingsã«POSTããŸã curl -XPUT localhost:9200/analyzer_handson/_settings -H " Content-type: application/json " -d @my_kuromoji_analyzer.json æåŸã«openããŠå®æã§ã curl -XPOST localhost:9200/analyzer_handson/_open æ©éããã¹ããæããŠã¿ããšä»¥äžã®ãã㫠圢æ
çŽ è§£æ ãããŠããããšãããããŸãã { " tokens ": [ { " token ":" ã³ãŠãã€ãã³ã®ã³ ", " start_offset ": 0 , " end_offset ": 8 , " type ":" word ", " position ": 0 } , { " token ":" 㯠", " start_offset ": 8 , " end_offset ": 9 , " type ":" word ", " position ": 1 } , { " token ":" äœæ Œ ", " start_offset ": 9 , " end_offset ": 11 , " type ":" word ", " position ": 2 } , ... ~ ... { " token ":" åã ", " start_offset ": 45 , " end_offset ": 47 , " type ":" word ", " position ": 21 } , { " token ":" ã§ ", " start_offset ": 47 , " end_offset ": 48 , " type ":" word ", " position ": 22 } , { " token ":" ãã ", " start_offset ": 48 , " end_offset ": 50 , " type ":" word ", " position ": 23 } ] } ç¡äºKuromojiã䜿çšã㊠åãã¡æžã ãããããšãã§ããŸããã æåŸã¯Token filterã§ãã Token filter Token filterã§ã¯Tokenizerã§ åãã¡æžã ããã ããŒã¯ ã³ã«å¯ŸããŠæ§ã
ãªå€æåŠçãè¡ããŸãã 以äžã¯Token filterã®äžäŸã§ãã Lower case Token filter ããŒã¯ ã³ãå
šãŠå°æåã«å€æãã Stop Token filter ã¹ãããã¯ãŒã ã®é€å»ãè¡ã ã¹ãããã¯ãŒã ãšã¯ã èªç¶èšèªåŠç ã«ãããŠãäžè¬çã§ãããªã©ã®äžèŠãªåèªã®ããš Stemer Token filter èªå¹¹ããšã«å®çŸ©ãããã¹ããã³ã°åŠçãè¡ã ã¹ããã³ã°ãšã¯èªåœ¢ã®å€åããªããã衚çŸãçµ±äžããããš Synonym Token filter é¡çŸ©èªã®å±éãè¡ã è¡šèšæºãïŒåŒè¶ããåŒã£è¶ããåŒè¶ïŒãé¡çŸ©èªïŒããœã³ã³ãPCãã³ã³ãã¥ãŒã¿ïŒãªã© Kuromoji-Analysisã«ä»å±ããŠããToken FIlter kuromoji_baseform åè©ã»åœ¢å®¹è©ãååã«æ»ããŸããæŽ»çšåœ¢ã¯è¡šèšæºãã®åå ã«ãªããŸãã kuromoji_part_of_speech ç¹å®ã®åè©ãåé€ããŸããæ€çŽ¢ã«ãããŠãå©è©ãå©åè©ãªã©ã¯å¿
èŠã§ãªãã±ãŒã¹ããããŸãã ããã©ã«ãã§ã¯ {å©è©-æ Œå©è©-äžè¬, å©è©-çµå©è©}ãåé€ããŸãã 圢æ
çŽ è§£æ ãè¡ãããšã«ãã£ãŠãåèªã«åè©æ
å ±ãä»äžãããŸã kuromoji_stemmer æ¥æ¬èªã«ç¹åããã¹ããã³ã°åŠççšã®Token filterãã«ã¿ã«ãã®äŒžã°ãæ£ãåé€ããŸãã ä»åã¯äžèšã® Kuromojiã®Token filter 3çš®ãšã Synonym Token filter , Stop Token filter ã䜿çšããŠãããŸã " ã³ãŠãã€ãã³ã®ã³ "ã§äžåèªã§ããã"ãã³ã®ã³"ã§æ€çŽ¢ããæã«ãããããªã äºã€ã®åèªãäžã€ã®ã·ããã ã°ã«ãŒã *2 ãšããŠæ±ãmy_synonym_penguin_filterãæ°ããäœæããfilterã«è¿œå åè©ã圢容è©ã§ããããããããã®ãããããããªã©æ§ã
ãªæç« ã§é »åºããããæç« ã®ç¹åŸŽã衚ããªãã®ã§ãªããŠãæ§ããªãããªåèªããã *3 é »åºããããªåèªãstopwordsã«è¿œå { " settings ": { " analysis ": { " analyzer ": { " my_kuromoji_analyzer ": { " type ": " custom ", " char_filter " : [ " icu_normalizer ", " kuromoji_iteration_mark " ] , " tokenizer ": " kuromoji_tokenizer ", " filter ": [ " kuromoji_baseform ", " kuromoji_part_of_speech ", " kuromoji_stemmer ", " my_synonym_penguin_filter ", " my_stop_filter " ] } } , " filter ": { " my_synonym_penguin_filter ": { " type ": " synonym ", " synonyms ": [ " ã³ãŠãã€ãã³ã®ã³,ãã³ã®ã³ " ] } , " my_stop_filter ": { " type ": " stop ", " stopwords ": [ " ãã ", " ãã® ", " ãã ", " ãã ", " ãã ", " ãã " ] } } } } } èšå®ãé©çšãããåãã¯ãšãªãæããŠãããŸãã ãããšä»¥äžã®ãããªçµæã«ãªããŸããã { " tokens ": [ { " token ":" ã³ãŠãã€ãã³ã®ã³ ", " start_offset ": 0 , " end_offset ": 8 , " type ":" word ", " position ": 0 } , { " token ":" ãã³ã®ã³ ", " start_offset ": 0 , " end_offset ": 8 , " type ":" SYNONYM ", " position ": 0 } , { " token ":" äœæ Œ ", " start_offset ": 9 , " end_offset ": 11 , " type ":" word ", " position ": 2 } , { " token ":" 130 ", " start_offset ": 17 , " end_offset ": 20 , " type ":" word ", " position ": 7 } , { " token ":" ã»ã³ã ", " start_offset ": 20 , " end_offset ": 21 , " type ":" word ", " position ": 8 } , { " token ":" å ", " start_offset ": 27 , " end_offset ": 28 , " type ":" word ", " position ": 12 } , { " token ":" 床床 ", " start_offset ": 29 , " end_offset ": 31 , " type ":" word ", " position ": 14 } , { " token ":" ã³ã³ãã¥ãŒã¿ ", " start_offset ": 32 , " end_offset ": 39 , " type ":" word ", " position ": 15 } , { " token ":" èŠã ", " start_offset ": 43 , " end_offset ": 44 , " type ":" word ", " position ": 19 } , { " token ":" åã ", " start_offset ": 45 , " end_offset ": 47 , " type ":" word ", " position ": 21 } ] } ãã£ããèšå®ããToken filterã®å¹æã衚ããŠããŸã kuromoji_baseformïŒååãžå€æïŒ èŠãŠâèŠã åãâåã kuromoji_part_of_speech(å©è©-æ Œå©è©-äžè¬, å©è©-çµå©è©ã®åé€) ã³ãŠãã€ãã³ã®ã³ "ã¯" ãã³ã³ãã¥ãŒã¿ãŒ"ã§"ãªã©ãåé€ãããŠããŸã kuromoji_stemmerïŒèªå¹¹ã®çµ±äžïŒ ã³ã³ãã¥ãŒã¿ãŒâã³ã³ãã¥ãŒã¿ã®ããã«äŒžã°ãæ£ãé€å»ãããŠããŸã Synonym Token filter ã³ãŠãã€ãã³ã®ã³ ã ã³ãŠãã€ãã³ã®ã³ ãšãã³ã®ã³ã®äºèªã«å±éãããŠããŸã Stop Token filter èšå®ãã"ãã", "ãã®", "ãã", "ãã", "ãã", "ãã"ãããããé€å»ãããŠããŸãã ãããã« å®éã«Analyzerãèšå®ããŠã¿ãŸããã Analyzerã ãã§ã¯ãªããä»ã«ãæ§ã
ãªæ©èœãElasticsearchã«ã¯ãããŸãã æ¬¡ã¯æ€çŽ¢ãã¿ã話ããããšæããŸãã ãšã³ãžã㢠äžéæ¡çš ãµã€ã ã©ã¯ ã¹ã§ã¯ããšã³ãžãã¢ã»ãã¶ã€ããŒã® äžéæ¡çš ãç©æ¥µçã«è¡ã£ãŠãããŸãïŒ ãèå³ãããŸãããæ¯éã確èªããé¡ãããŸãã https://career-recruit.rakus.co.jp/career_engineer/ ã«ãžã¥ã¢ã«é¢è«ãç³èŸŒã¿ãã©ãŒã ã©ã®è·çš®ã«å¿åããã°è¯ããããããªããšããæ¹ã¯ãã«ãžã¥ã¢ã«é¢è«ãéæè¡ã£ãŠãããŸãã 以äžãã©ãŒã ãããç³èŸŒã¿ãã ããã rakus.hubspotpagebuilder.com ã©ã¯ ã¹Developersç»é²ãã©ãŒã https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ ã€ãã³ãæ
å ± äŒç€Ÿã®é°å²æ°ãç¥ãããæ¹ã¯ãæ¯é±éå¬ããŠããã€ãã³ãã«ãåå ãã ããïŒ âTECH PLAY techplay.jp âconnpass rakus.connpass.com *1 : MeCab çšã®æ°èªå¯Ÿå¿èŸæž mecab -ipadic-neologdãé©çšãããKuromojiã® ãã©ã°ã€ã³ ããããŸã *2 : åæ¹åå±é *3 : å®éã«äœ¿çšããéã¯å
¬éãããŠãã ã¹ãããã¯ãŒã ãªã¹ããã¡ã³ããã³ã¹ããã®ãè¯ã