GOにおけるマイクロサービスの構築:弾性検索による探索


Disclaimer: This post includes Amazon affiliate links. If you click on one of them and you make a purchase I'll earn a commission. Please notice your final price is not affected at all by using those links.



何が弾性検索ですか?
Source (強調表示)

... is a distributed, free and open search and analytics engine for all types of data, including textual, numerical, geospatial, structured, and unstructured.


エラスティックサーチは、Elkとして一般的に知られている弾性スタックの中央コンポーネントです.
  • E : 弾性検索
  • L : ログStash
  • K : 貴花.
  • FlashSearchは、すでに3つの大きなクラウドプロバイダによってサポートされているよく知られているツールです.
  • アマゾンウェブサービスManaged Elasticsearch
  • クラウド・プロバイダーElastic Cloud
  • マイクロソフトアジュールElastic Cloud

  • 囲碁における弾性検索の利用
    囲碁において弾性検索と相互作用する2つの一般的なパッケージがあります.

  • github.com/elastic/go-elasticsearch : 公式GOパッケージは、弾性でサポートされています

  • github.com/olivere/elastic : 非公式のパッケージは、コミュニティでよく知られています.
  • どちらを選びましょうか.
    これらのパッケージは両方ともサポートされていて、準備ができていますelastic/go-elasticsearch の代わりにolivere/elastic これはコメントによるものですOliver Eilhard (作者)olivere/elastic ) on Github 彼はおそらくそれはv8 そのパッケージの利用可能ではない場合は、そのバージョンが利用可能であるときに弾性モードのバージョン8にアップグレードを計画している場合は問題になる可能性があります.
    公正な、今日まで、Oliver Eilhardによって行われた仕事は、それ以外の場合、明示的に公式のいずれかを使用して表示される必要がありますエラスティックサーチAPIとの相互作用するときに必要なものの多くを簡素化するolivere/elastic/v7 それは文字通り公式APIで利用可能なすべての可能なオプションをカバーして実装されており、それはfluent-like API その使用法とデフォルト値は最初から定義済みです.

    The code used for this post is available on Github.



    使用elastic/go-elasticsearchエラスティックサーチデータストアを実装するにはRepository Pattern 私たちの“マイクロサービスを行う”を更新します.我々は新しい定義elasticsearch パッケージは、新しいタイプでは、対応するロジックをインデックスに必要なだけでなく、レコードを削除するために必要が含まれます.この型は既に存在する部分の一部として呼び出されますservice.Task , 最後に我々は、タスクを検索するための新しいHTTP APIを介して、この機能をお客様に公開されます.
    実際には、索引付け用のコードは以下のようになります.
    func (t *Task) Index(ctx context.Context, task internal.Task) error {
        // XXX: Excluding OpenTelemetry and error checking for simplicity
    
        body := indexedTask{
            ID:          task.ID,
            Description: task.Description,
            Priority:    task.Priority,
            IsDone:      task.IsDone,
            DateStart:   task.Dates.Start.UnixNano(),
            DateDue:     task.Dates.Due.UnixNano(),
        }
    
        var buf bytes.Buffer
    
        _ = json.NewEncoder(&buf).Encode(body) // XXX: error omitted
    
        req := esv7api.IndexRequest{
            Index:      t.index,
            Body:       &buf,
            DocumentID: task.ID,
            Refresh:    "true",
        }
    
        _ = req.Do(ctx, t.client) // XXX: error omitted
        defer resp.Body.Close()
    
        io.Copy(ioutil.Discard, resp.Body)
    
        return nil
    }
    
    また、検索には次のようになります.
    func (t *Task) Search(ctx context.Context, description *string, priority *internal.Priority, isDone *bool) ([]internal.Task, error) {
        // XXX: Excluding OpenTelemetry and error checking for simplicity
    
        if description == nil && priority == nil && isDone == nil {
            return nil, nil
        }
    
        should := make([]interface{}, 0, 3)
    
        if description != nil {
            should = append(should, map[string]interface{}{
                "match": map[string]interface{}{
                    "description": *description,
                },
            })
        }
    
        if priority != nil {
            should = append(should, map[string]interface{}{
                "match": map[string]interface{}{
                    "priority": *priority,
                },
            })
        }
    
        if isDone != nil {
            should = append(should, map[string]interface{}{
                "match": map[string]interface{}{
                    "is_done": *isDone,
                },
            })
        }
    
        var query map[string]interface{}
    
        if len(should) > 1 {
            query = map[string]interface{}{
                "query": map[string]interface{}{
                    "bool": map[string]interface{}{
                        "should": should,
                    },
                },
            }
        } else {
            query = map[string]interface{}{
                "query": should[0],
            }
        }
    
        var buf bytes.Buffer
    
        _ = json.NewEncoder(&buf).Encode(query)
    
        req := esv7api.SearchRequest{
            Index: []string{t.index},
            Body:  &buf,
        }
    
        resp, _ = req.Do(ctx, t.client) // XXX: error omitted
        defer resp.Body.Close()
    
        var hits struct {
            Hits struct {
                Hits []struct {
                    Source indexedTask `json:"_source"`
                } `json:"hits"`
            } `json:"hits"`
        }
    
        _ = json.NewDecoder(resp.Body).Decode(&hits) // XXX: error omitted
    
        res := make([]internal.Task, len(hits.Hits.Hits))
    
        for i, hit := range hits.Hits.Hits {
            res[i].ID = hit.Source.ID
            res[i].Description = hit.Source.Description
            res[i].Priority = internal.Priority(hit.Source.Priority)
            res[i].Dates.Due = time.Unix(0, hit.Source.DateDue).UTC()
            res[i].Dates.Start = time.Unix(0, hit.Source.DateStart).UTC()
        }
    
        return res, nil
    }
    
    どちらの場合もoriginal implementation 具体的な詳細については、前に述べたように Delete method 以前にインデックスされたタスクを削除するときに呼び出されることを意味します.
    これらの呼び出しを接続するには service type 明示的に検索ストアを呼び出して、既に定義した新しいアクションを実行します将来の投稿では、サービスの実装に直接ストアを明示的に呼び出す代わりに、イベントを使用してこれを行う方法を説明します.
    例えば、service.Task タイプは以下のようにしますCreate :
    // Create stores a new record.
    func (t *Task) Create(ctx context.Context, description string, priority internal.Priority, dates internal.Dates) (internal.Task, error) {
        // XXX: Excluding OpenTelemetry and error checking for simplicity
    
        task, _ := t.repo.Create(ctx, description, priority, dates)
    
        _ = t.search.Index(ctx, task) // XXX: New Search call to index and store records
    
        return task, nil
    }
    
    同様の呼び出しはインデックスとレコードを検索する新しいメソッドに追加されます.
    func (t *Task) By(ctx context.Context, description *string, priority *internal.Priority, isDone *bool) ([]internal.Task, error) {
      // XXX: Excluding OpenTelemetry and error checking for simplicity
    
        res, _ := t.search.Search(ctx, description, priority, isDone) // XXX: error omitted
    
        return res, nil
    }
    
    これによって使用される他のデータストアと同様ですservice.Task また、依存性注入を使用して、elasticsearch.Task ストア.

    結論
    AnimticSearchは、複数の異なる方法でレコードを検索することができます強力なツールであり、それは水平方向にスケールし、インデックスと検索値に複数の方法をサポートしています.例えば、テキストベースのフィールドを使用するときに、それは値を表現するために実際のリテラル番号と人間化された方法を検索できるように単語に番号を変換することができます.
    同様にそれは彼らがあなたの人気によってそれらを最初に表示するための柔軟性を与えてどのように多くの試合に応じて並べ替えフィールドを可能にするだけでなく、検索の用語に関連するレコードを検索ファジー検索のような他の人気のある機能に応じて並べ替えることができます.
    AntiticSearchを使用するときに留意する1つのことは、時々主要なバージョンアップグレードは、APIがまだ同じであっても変化を壊すような動作をもたらすことです.
    AntiticSearchは、私はまた、分析のために使用することができます本当に強力なツールだ、上記の言及よりも含まれています、それは確かに使用するには圧倒的かもしれないが、それは無視されるべきではないツールです.

    推奨読書
    あなたがより柔軟な関連話題にあなたの歯を沈めるのを見ているならば、私は以下の本を推薦します:
  • Mastering Elasticsearch 5.x
  • Relevant Search: With applications for Solr and Elasticsearch
  • Elasticsearch in Action
  • Designing Data-Intensive Applications