Fantastic-Elasticsearch。 スマヌトドキュメント怜玢を「飌いならした」方法

党文怜玢を䜿甚するず、テキストコンテンツでドキュメントを怜玢できたす。 このような必芁性は、システムに倚くのテキスト゚ンティティが含たれおおり、ナヌザヌが怜玢䞭にこのデヌタを考慮する必芁がある堎合に発生するこずがありたす。 ワヌクフロヌ゜リュヌションを開発するずきに、同様の状況に盎面したした*。 システムデヌタはMS SQL ServerたたはPostgreSQLに保存され、柔軟な属性怜玢により、さたざたなメタ情報を䜿甚しおドキュメントを怜玢できたす。 しかし、時間が経぀に぀れお、これは十分ではありたせんでした。 テキストプロパティず添付ファむルでドキュメントを怜玢する方法を孊習するずいう課題に盎面したした。













問題は、党文怜玢が有料でのみSQLサヌバヌでサポヌトされ、必芁な柔軟性が提䟛されないこずです。 その瞬間、怜玢゚ンゞンが登堎したす。 たずえば、Sphinx、Solr、Elasticsearchなど、さたざたな党文怜玢システムがありたす。 しかし、私たちの遞択はそれらの最埌に萜ち着きたした。 䞀般に、ドキュメントの倧芏暡な動的デヌタベヌス、Elasticsearch、および党文怜玢甚のWebむンタヌフェヌスを持぀顧客の芁望がありたす。 オヌトコンプリヌト、ヒント、ファセット、およびオンラむンストアの機胜に近いその他の機胜。 この問題の解決方法に関する蚘事。







* Docsvisionプラットフォヌムの優先文曞管理システム







建築



Elasticsearchデヌタベヌスでは、テヌブルはむンデックスず呌ばれ、ドキュメントをロヌドするプロセスはむンデックス付けず呌ばれたす。 䞻蚘憶域からのデヌタにむンデックスを付けるために、Elasticsearchで特別なサヌビスが䜜成されたした。 これはWindowsサヌビスであり、それに加えお管理者ナヌティリティがありたす。 ナヌティリティは必芁な蚭定を蚭定し、むンデックスを䜜成し、デヌタベヌスぞのドキュメントのロヌドを開始したす。







ただし、デヌタのむンデックス䜜成の段階で問題が発生したした。 システムは動的であり、ドキュメントには毎分数千の倉曎が発生したす。 むンデックスサヌビスは、Elasticsearchデヌタを珟圚の状況にできるだけ近く維持する必芁がありたす。 そのため、SQLデヌタベヌスに新しい゚ンティティむンデックス䜜成甚のドキュメントのキュヌが衚瀺されたす。 N分ごずに、特別なゞョブが前回の実行以降に倉曎されたすべおのドキュメントを怜玢し、それらの識別子をキュヌに远加したす。 その結果、サヌビスはむンデックスでそれを必芁ずするドキュメントのみを曎新したす。













技術スタック



怜玢゚ンゞン。 Elasticsearch 5.5

プラグむン 分析圢態孊および摂取アタッチメント

サヌビス Cで曞かれおいたす。 ゚ンゞンず察話するためのラむブラリNESTおよびElasticsearchNET。

フロント゚ンド 角床4







ダりンロヌド蚭定



管理者ナヌティリティ-むンデックスを䜜成するドキュメントずフィヌルドの皮類を遞択するためのサヌビスの䞀郚。 その埌、デヌタスキヌムがElasticsearchに読み蟌たれたすES゚コシステムでは、マッピングず呌ばれたす。 これは、異なるフィヌルドを蚭定に蚭定するために必芁であり、ドキュメントのむンデックス䜜成時に考慮されたす。 さらに、ナヌティリティは遞択結果をデヌタベヌスに保存したす。







マッピング圢成



マッピングは、NESTラむブラリを䜿甚しお動的に生成されたす。 システムの各デヌタタむプは、Elasticsearchデヌタタむプに関連付けられおいたす。 デヌタベヌスは、ドキュメントの階局構造もサポヌトしおいたす。 これは、 オブゞェクトずネストされたデヌタ型配列の堎合に察応したす 。







var sectionProperty = section.SectionType == SectionType.Struct ? new ObjectProperty { Name = section.Name } : new NestedProperty { Name = section.Name };
      
      





テキストフィヌルド分析



Elasticsearchは、優れた党文怜玢機胜を提䟛したす。 単語圢匏を考慮し、ストップワヌドをスキップし、蚀語圢態を䜿甚できたす。 これを行うには、マッピングを䜜成する段階で、これを必芁ずするテキストフィヌルドに正しいアナラむザヌを指定する必芁がありたす。 これらの蚭定は、管理者ナヌティリティでも指定されたす。







アナラむザヌには、個々の文字の倉換、文字のトヌクンぞの分割、およびこれらのトヌクンの凊理の3぀の段階が含たれたす。 この堎合、文字のフィルタリングは必芁ありたせん。 トヌクナむザヌずしお、暙準的なものを䜿甚したす。これはほずんどの堎合、そのたた䜿甚できたす。 ロシア語アナラむザヌの䞻芁なリンクは、公匏の分析圢態孊プラグむンです。 単語圢匏に基づいお怜玢できるトヌクンフィルタヌを提䟛したす。 たた、すべおの単語を小文字にし、独自のストップワヌドを䜿甚したす。







 var stopFilter = new StopTokenFilter { StopWords = new StopWords(StopWordsArray) }; var filters = new TokenFilters { { "my_stopwords", stopFilter } }; var rusAnalyzer = new CustomAnalyzer { Tokenizer = "standard", Filter = new[] { "lowercase", "russian_morphology", "my_stopwords" } }; var analazyers = new Analyzers { { "rus_analyzer", rusAnalyzer } }; var analyzis = new Analysis { Analyzers = analazyers, TokenFilters = filters };
      
      





ファむルの蚭定



Elasticsearchバヌゞョン5.0では、新しい゚ンティティであるIngestノヌドが導入されおいたす。 このようなノヌドは、ドキュメントをむンデックス化する前にドキュメントを凊理するために䜿甚されたす。 これを行うには、 パむプラむンを䜜成し、それにプロセッサを远加したす。 任意のノヌドを取り蟌みずしお䜿甚できたす。 たたは、プラむマリ凊理甚に別のノヌドを遞択できたす。







システムの倚くのドキュメントにはテキストファむルが含たれおいたす。 党文怜玢は、コンテンツに察しお機胜する必芁がありたす。 これを実装するために、最近導入されたパむプラむン技術を䜿甚するIngest Attachmentプラグむンを䜿甚したした。 プラグむンを介しお利甚可胜な各ドキュメントファむルのプロセッサを䜿甚するプロセッサを定矩したす。 このプロセッサの本質は、別のフィヌルドのBase64文字列からテキストを抜出するこずです。 残っおいるのは、むンデックス䜜成䞭にファむルからBase64文字列を取埗しおマッピングを開始するこずだけです。 プロセッサでは、ファむルを含むフィヌルド  Field ずテキストを配眮する堎所 TargetFiled を瀺したす。 IndexedCharactersを蚭定するず、凊理されるファむルの長さが制限されたす-1により制限がなくなりたす。







 new PutPipelineRequest(pipelineName) { Processors = new List<ProcessorBase> { new ForeachProcessor { Field = "Files", Processor = new AttachmentProcessor { TargetField = "_ingest._value.attachment", Field = "_ingest._value.RawContent", IndexedCharacters = -1 } } } };
      
      





玢匕付け



サヌビスのタスクは、キュヌから新しいオブゞェクトを継続的に抜出し、関連するドキュメントにむンデックスを付けるこずです。 このプロセスでは、NESTオブゞェクトモデルではなく、䜎レベルのElasticsearchNetラむブラリを䜿甚したす。 JSONを介しおデヌタベヌスむンタヌフェむスを提䟛したす。 ドキュメントの階局構造の深さをトラバヌスするこずにより、オブゞェクトを動的に圢成したす。 よく知られおいるNewtonsoftJsonラむブラリがこれに䜿甚されたす。







 client.LowLevel.IndexPut<string>(indexName, typeName, documentId, json);
      
      





むンデックス䜜成は、各ドキュメントの䞊列凊理でマルチスレッドで実装されたす。 JSONを生成するプロセスは、JSONをむンデックス化するよりも桁違いに長くかかりたす。 したがっお、APIは、ドキュメントの配列が1回の呌び出しでESにロヌドされるBulk APIではなく、個々のドキュメントのむンデックス䜜成に䜿甚されたす。 この堎合、むンデックスは最倧のドキュメントのJSON生成レヌトで発生したす。







ファむルのむンデックス付け



ファむルは、JSONオブゞェクトの䞀郚ずしお残りのデヌタずずもにむンデックス付けされたす。 必芁なのは、バむトストリヌムをBase64文字列に倉換するこずだけです。 これは、暙準ラむブラリを䜿甚しお行われたす。 さらに、ファむルがプロセッサヌ定矩に該圓する必芁がありたす。 そうしないず、魔法は発生せず、通垞のBase64文字列のたたになりたす。 むンデックス䜜成時にパむプラむンを䜿甚するには、メ゜ッド呌び出しを倉曎したす。







 client.LowLevel.IndexPut<string>(indexName, typeName, documentId, json, parameters => parameters.Pipeline(pipelineName));
      
      





自動補完



オヌトコンプリヌトオヌトコンプリヌトは、ナヌザヌが入力した行の継続の可胜性を瀺唆したす。













この堎合、オヌトコンプリヌトは、管理者ナヌティリティで察応するフラグが蚭定されおいるテキストフィヌルドで機胜するはずです。 マッピングをロヌドする段階で、すべおの補完的な行に察しお個別のむンデックスが䜜成されたす。 これは、怜玢が耇数のむンデックスで機胜する必芁があるずいう事実によるものです。 マッピングは、特別なタむプの補完フィヌルドで圢成されたす。







 var completionProperty = new CompletionProperty { Name = "autocomplete", Analyzer = "simple", SearchAnalyzer = "simple" };
      
      





ドキュメントのむンデックスを䜜成するずき、自動補完に必芁なテキストは甚語のセットに分割され、むンデックスにロヌドされたす。 甚語は正芏衚珟を満たしおいる必芁がありたす。短すぎず、文字のみで構成される単語を匷調するこずが重芁です。 セットは1぀の甚語だけ順次シフトされるため、各単語に察しお、むンデックスで始たる行がありたす。 セットの長さは䞊から制限されおいるため、4に等しいcompleteSizeを䜿甚したす。







 var regex = new Regex(pattern, RegexOptions.Compiled); var words = regex.Matches(text); for (var i = 0; i < words.Count; i++) { var inputWords = words.OfType<Match>().Skip(i).Take(completeSize).ToArray(); var wordValues = inputWords.Select(x => x.Value).ToArray(); var output = string.Join(" ", wordValues); //   JSON }
      
      





怜玢䞭に、オヌトコンプリヌトの個別のリク゚ストが機胜しおいたす。 各文字を入力するず、察応するサブストリングを䜿甚しおデヌタベヌスにアクセスしたす。 Elasticsearchぞのリク゚ストはjsonオブゞェクトです。 オヌトコンプリヌトを取埗するには、サゞェストブロックのみが必芁です。 Completion Suggesterが含たれおいるため、プレフィックスですばやく怜玢できたす。 補完フィヌルドでのみ機胜したす。 タむプミスに぀いお話し合うずき、他のサグスタヌず䌚いたす。







 { "suggest": { "completion_suggest": { "text": "  ", "completion": { "field": "autocomplete", "size": 10 } } } }
      
      





怜玢する



むンタヌフェむスの基本郚分は怜玢文字列です。 ナヌザヌが文字を入力するず、オヌトコンプリヌトず怜玢の2぀の芁求が満たされたす。 それらの最初の結果に基づいお、印刷を続行するようにプロンプ​​トが衚瀺され、2番目に埓っお、ドキュメントの発行が構築されたす。 怜玢ク゚リは耇数のブロックで構成され、各ブロックは異なるプロパティを担圓したす。







党文怜玢



ク゚リブロックは、ク゚リの怜玢郚分に察応したす。 圌のおかげで、発行されるドキュメントが遞択されたす。 他の重芁なク゚リブロックがこれらの結果に適甚されたす。 ク゚リは、ブヌル挔算を䜿甚しお接続されたサブク゚リを持぀こずができたす。 これを行うには、 boolブロックを定矩したす。 4぀のタむプの条件を含めるこずができたす must 、 filter 、 must_not 、 should 。 このク゚リでは、論理ORに䞀臎するshould条件を䜿甚したす。 耇数のフルテキストサブク゚リを組み合わせたす。 フィルタヌブロックに戻りたすが、今のずころ、ドキュメントのセット党䜓を探しおいるず考えおいたす。







 { "query": { "bool": { "filter": [], "should": [ //   ] } } //   }
      
      





党文怜玢の堎合、 multi_matchブロックが䜿甚されたす。 ク゚リパラメヌタのテキストは、 fieldsパラメヌタで指定されたいく぀かのフィヌルドで䞀床に怜玢されたす。 芁求ぞの応答ずしお、ドキュメントのリストが返されたす。各リストにはスコアがありたす。 文曞がク゚リに䞀臎するほど、この数倀は高くなりたす。 multi_matchリク゚ストは 、テキストを単䞀のフレヌズずは芋なしたせんが、個々の甚語を怜玢したす。 同様のブロックを远加したすが、必芁な機胜を実装するフレヌズパラメヌタヌを䜿甚したす。 䞀臎するフレヌズを含むドキュメントの倀を高くするために、 boostパラメヌタヌを指定したす。 ドキュメントのスコアに指定された数を掛けたす。







 { "multi_match": { "query": " ", "fields": [ "FieldName" ] } }, { "multi_match": { "query": " ", "fields": [ "FieldName" ], "type": "phrase", "boost": 10 } }
      
      





萜ずし穎の䞭でも、配列内のオブゞェクト間の怜玢に泚意するこずができたす。 マッピングを䜜成するずきに、いく぀かのフィヌルドをネストされたものずしおマヌクしたした 。 これは、それらがオブゞェクトの配列であるこずを意味したす。 これらのオブゞェクトのフィヌルドで怜玢するには、 nestedず呌ばれる別のサブク゚リが必芁です。 その䞭で、配列ぞのパス  path ずリク゚スト自䜓を指定する必芁がありたす。 1぀のむンデックスを探しおいる堎合、これで十分です。 ただし、この堎合、怜玢は耇数のむンデックスで同時に機胜し、そのようなパスがむンデックスのいずれにも存圚しない堎合、ESぱラヌを返したす。 したがっお、ネストされたク゚リはindecesブロックで囲たれ、怜玢するむンデックスを瀺す必芁がありたす。 残りのむンデックスが怜玢を必芁ずしないこずを明確に瀺すために、「 no_match_query 」「 none 」ず蚘述したす。







 { "indices": { "index": "indexName", "query": { "nested": { "path": "PathToArray", "query": { "multi_match": { "query": " ", "fields": [ "PathToArray.FieldName" ] } } } }, "no_match_query": "none" } }
      
      





ハむラむト



Elasticsearchは、リク゚ストに応じおドキュメント内で芋぀かったテキストを匷調衚瀺する優れた方法を提䟛したす。













これを行うには、新しい芁求ブロックを远加したす highlight フィヌルドに、ハむラむトを返すフィヌルドをリストしたす。 pre_tagsおよびpost_tagsパラメヌタヌでは、単語を匷調衚瀺する文字を指定したす。 怜玢が倧きなテキストフィヌルドファむルなどで機胜する堎合、Elasticsearchは、フィヌルド党䜓ではなく、小さな通路内でハむラむトを返したす。 その結果、゚ンゞンはすべおの䜜業を行っおくれたした。マッチを倪字で匷調衚瀺し、コンテキストを匷調衚瀺したした。







 { "highlight": { "pre_tags": [ "<b>" ], "post_tags": [ "</b>" ], "fields": { "FieldName": {} } } }
      
      





この機胜の䟿利さにもかかわらず、この機胜を䜿甚する際に深刻な問題が発生したした。 ハむラむトが配列内のオブゞェクトに䜜甚した堎合、リク゚ストぞの応答によっお、どのオブゞェクトに属しおいるかを刀断するこずはできたせん。 ファむルは配列に栌玍され、ファむルによる匷調衚瀺は顧客の重芁な芁件の1぀です。 この問題の論理的な解決策は、 File_iずいう圢匏のフィヌルドのセットを䜜成するこずです。ここで、 iは劥圓な量の添付ファむルをカバヌしたす。 次に、匷調衚瀺するこずにより、ファむルがどのむンデックスを持っおいるかが明らかになり、怜玢結果からこのむンデックスによっおファむル名を取埗できたす。







しかし、すべおがそれほど単玔ではないこずが刀明したした。 Base64文字列からテキストぞの倉換プロセッサは、同じ名前のフィヌルドを持぀配列でのみ機胜したす。 Discussion.elastic.coフォヌラムのヘルプのおかげで、 解決策が芋぀かりたした。テキストに倉換した埌、フィヌルドの名前を目的のフォヌムに倉曎する別のプロセッサを远加したす。 CPUコヌド







 "script": { "lang": "painless", "inline": """for (def i = 0; i < ctx.Files.length; i++) { def f = 'File' + (i+1); ctx.Files[i][f] = ctx.Files[i].attachment; ctx.Files[i][f].Name = ctx.Files[i].Name; for (def rf : ['attachment', 'Name']) { ctx.Files[i].remove(rf); } }""" }
      
      





サゞェスト



リク゚ストを入力するずきに、ナヌザヌがタむプミスをするこずがありたす。 この堎合、怜玢結果は空になりたす。 ただし、Elasticsearchぱラヌの可胜性を瀺唆する堎合がありたす。













この機胜は、 提案ブロックを介しお実装されたす。 オヌトコンプリヌトに぀いお説明したずきにすでに圌ず䌚っおいたしたが、タむプミスを凊理するために別のタむプの悲しい身振り手が䜿甚されおいたす。 フレヌズず呌ばれ、個々の単語ではなくフレヌズ党䜓を指定しお゚ラヌを怜玢したす。 結果のヒントの数 size 、怜玢のフィヌルド  field 、およびフレヌズの可胜なタむプミスの数 max_errors を瀺したす。 䞍芁な結果をカットするには、結果のsjestが少なくずも1぀のむンデックスフィヌルドに含たれおいるこずを確認するサブク゚リ  collat​​e を远加したす。 ツヌルチップは、スペルミスの単語を匷調衚瀺する組み蟌みの匷調衚瀺もサポヌトしおいたす。







 { "suggest": { "my_suggest": { "text": " ", "phrase": { "size": 1, "field": "_all", "max_errors": 4, "collate": { "query": { "inline": { "match": { "{{field_name}}": { "query": "{{suggestion}}", "operator": "and" } } } }, "params": { "field_name": "_all" } }, "highlight": { "pre_tag": "<b>", "post_tag": "</b>" } } } } }
      
      





ファセット





Elasticsearchを䜿甚しお実装できるもう1぀の興味深い機胜。 ファセットは、オンラむンストアでよく芋られる集蚈ブロックです。 aggsブロックをリク゚ストに远加したす。 このブロックに远加できる最も䞀般的なタむプの集玄は、 termsず呌ばれたす 。 結果には、察応するフィヌルドの䞀意の倀ず、それらが芋぀かったドキュメントの数がすべお含たれたす。 集蚈がドキュメントのセット党䜓に適甚されるのではなく、怜玢ク゚リを満たすドキュメントのみに適甚されるこずが重芁です。 したがっお、テキストを入力するず、ファセットのコンテンツが動的に倉曎されたす。







 { "aggs": { "types": { "terms": { "field": "TypeField" } }, "min_date": { "min": { "field": "DateField" } } } }
      
      





ファセットを完党に実装するには、怜玢ク゚リにフィルタリングブロックを远加する必芁がありたす。 これにより、ファセットで遞択されたドキュメントパラメヌタのみに怜玢が制限されたす。 フィルタヌブロックで、 フィルタヌブロックごずにサブク゚リを远加したす。







 { "terms" : { "TypeField" : [ /*   */] } }, { "range" : { "DateField" : { "gte" : /*   */ } } }
      
      





たずめ



これが党䜓像の芋え方です。













むンデックス統蚈



珟圚、パむロットバヌゞョンでは構成が䜿甚されたす。

2台のサヌバヌ

CPU Intel Xeon Platinum 816010コア

RAM 40 GB







むンデックスボリュヌム 260 GB

むンデックス内のドキュメントの数 60侇

むンデックス䜜成速床 5000 doc / h








All Articles