Skip to content

ElasticSearch

الموقع الرسمي: Elasticsearch:官方分布式搜索和分析引擎 | Elastic

Elasticsearch هو محرك بحث وتحليل بيانات موزع بأسلوب RESTful، قادر على حل مختلف الحالات الاستخدام التي تظهر باستمرار. كنواة لـ Elastic Stack، يقوم Elasticsearch بتخزين بياناتك مركزيًا، مما يتيح لك إكمال البحث بسرعة فائقة، وضبط الصلة بدقة، وإجراء تحليلات قوية، وتوسيع النطاق بسهولة. ستشرح هذه المقالة كيفية استخدام Go لإجراء بعض العمليات الأساسية على Elasticsearch، مثل الإنشاء والحذف والتعديل والاستعلام وما إلى ذلك. إذا لم تكن على دراية بـ Elasticsearch، يرجى تعلمه بنفسك أولاً.

التبعيات

تحميل مكتبة التبعيات الرسمية

bash
$ github.com/elastic/go-elasticsearch/v7

إذا كنت تستخدم ES8، فقم بتغيير النسخة

bash
$ github.com/elastic/go-elasticsearch/v8

TIP

تستخدم هذه المقالة ES8 للعرض التقديمي

الاتصال

استخدم الدالة elasticsearch.NewClient لإنشاء اتصال جديد

go
func NewClient(cfg Config) (*Client, error)

يستخدم ES8+ اتصال HTTPS افتراضيًا الآن. عند إنشاء اتصال HTTPS، إما أن تستخدم شهادة CA أو بصمة CA، وكلاهما يتم إنشاؤهما على جانب خادم Elasticsearch. مثال على ذلك:

go
client, err := elasticsearch.NewClient(elasticsearch.Config{
    Addresses:              []string{"https://192.168.153.132:9200"},
    Username:               "elastic",
    Password:               "TETJ8IY+ifbt8SLc+RRQ",
    CertificateFingerprint: "C0E9867C7D446BFF72FE46E7E9FE3455E970A8ADB0D3DF0E1472D55DB2612CD5",
})

توفر elasticsearch واجهة برمجة تطبيقات Go التي تعتمد بشكل أساسي على الدوال الاختيارية. على سبيل المثال، من خلال ping API يمكن اختبار ما إذا كانت الخدمة متاحة:

go
pingResp, err := client.Ping(client.Ping.WithPretty(), client.Ping.WithHuman())
if err != nil {
    panic(err)
}
fmt.Println(pingResp)

المخرجات

[200 OK]

ومثال آخر، من خلال Info API لعرض حالة الخدمة:

go
infoResp, err := client.Info(client.Info.WithHuman())
if err != nil {
    panic(err)
}
fmt.Println(infoResp)

المخرجات

[200 OK] {
  "name" : "db-debian12",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "OMbDIsNwTFiuyjNF9Xnpbw",
  "version" : {
    "number" : "8.15.0",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "1a77947f34deddb41af25e6f0ddb8e830159c179",
    "build_date" : "2024-08-05T10:05:34.233336849Z",
    "build_snapshot" : false,
    "lucene_version" : "9.11.1",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

TIP

لأي أسئلة حول ES API، يرجى مراجعة الوثائق الرسمية ES Restful API.

الفهرس

من خلال Go API لتشغيل الفهارس، جميع واجهات API المتعلقة بعمليات الفهرس موجودة في الهيكل esapi.Indices

go
// Indices contains the Indices APIs
type Indices struct {
    AddBlock              IndicesAddBlock
    Analyze               IndicesAnalyze
    ClearCache            IndicesClearCache
    Clone                 IndicesClone
    Close                 IndicesClose
    ...
    ...
  ValidateQuery         IndicesValidateQuery
}

الإنشاء

إنشاء فهرس كما يلي:

json
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "long"
      },
      "salary": {
        "type": "double"
      }
    }
  }
}

العملية الفعلية تشبه إرسال طلب HTTP إلى حد كبير

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }
  dsl := bytes.NewBufferString(`{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
  },
  "mappings": {
    "properties": {
        "name": {
          "type": "text"
        },
        "age": {
          "type": "long"
        },
        "salary": {
          "type": "double"
        }
      }
  }
}`)

  createIndices := client.Indices.Create
  resp, err := createIndices("user", createIndices.WithBody(dsl))
  if err != nil {
    panic(err)
  }
  fmt.Println(resp)
}

المخرجات

json
[200 OK] {"acknowledged":true,"shards_acknowledged":true,"index":"user"}

الحصول على

الحصول على معلومات عدة فهارس

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  get := client.Indices.Get
  response, err := get([]string{"user"}, get.WithPretty(), get.WithHuman())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

المخرجات

json
[200 OK] {
  "user" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text"
        },
        "salary" : {
          "type" : "double"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date_string" : "2024-09-23T04:35:04.528Z",
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "3",
        "provided_name" : "user",
        "creation_date" : "1727066104528",
        "number_of_replicas" : "2",
        "uuid" : "AvhhuqV2ShGkRP9z7XbdDA",
        "version" : {
          "created_string" : "8.14.4-snapshot[8512000]",
          "created" : "8512000"
        }
      }
    }
  }
}

التحليل

تحليل سلسلة نصية لفهرس محدد وإرجاع النتائج، النص كالتالي:

json
{
  "analyzer": "standard",
  "text": ["this is a test", "the second text"]
}

الكود

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  analyze := client.Indices.Analyze
  dsl := bytes.NewBufferString(`{
  "analyzer" : "standard",
  "text" : ["this is a test", "the second text"]
}`)
  response, err := analyze(analyze.WithIndex("user"), analyze.WithBody(dsl), analyze.WithPretty(), analyze.WithHuman())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

المخرجات

json
[200 OK] {
  "tokens" : [
    {
      "token" : "this",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "is",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "a",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "test",
      "start_offset" : 10,
      "end_offset" : 14,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "the",
      "start_offset" : 15,
      "end_offset" : 18,
      "type" : "<ALPHANUM>",
      "position" : 104
    },
    {
      "token" : "second",
      "start_offset" : 19,
      "end_offset" : 25,
      "type" : "<ALPHANUM>",
      "position" : 105
    },
    {
      "token" : "text",
      "start_offset" : 26,
      "end_offset" : 30,
      "type" : "<ALPHANUM>",
      "position" : 106
    }
  ]
}

الحذف

حذف عدة فهارس محددة

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  indicesDelete := client.Indices.Delete
  response, err := indicesDelete([]string{"user"})
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

المخرجات

json
[200 OK] {"acknowledged":true}

في واجهات API أعلاه، يمكن ملاحظة أن جسم الطلب يحتاج إلى تسلسله يدويًا، فالجهة الرسمية لم تقم بتحويله إلى هياكل Go، كما أن جسم الاستجابة يحتاج أيضًا إلى معالجته يدويًا. هذه هي واجهات API الأكثر استخدامًا، واستخدام الأخرى ليس مختلفًا كثيرًا.

المستندات

الإنشاء

إنشاء مستند كالتالي:

json
{
  "name": "jack",
  "age": 12,
  "salary": 5701.1
}

الكود

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  doc := bytes.NewBufferString(`{
    "name": "jack",
    "age": 12,
    "salary": 5701.1
}`)
  create := client.Create
  response, err := create("user", "1", doc, create.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

المخرجات

json
[201 Created] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 3,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

الحصول على

الحصول على مستند بمعرف محدد

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  get := client.Get
  response, err := get("user", "1", get.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

المخرجات

json
[200 OK] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "jack",
    "age" : 12,
    "salary" : 5701.1
  }
}

التحديث

تحديث محتوى المستند

json
{
  "doc": {
    "name": "jack",
    "age": 35,
    "salary": 5701.1
  }
}

الكود

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  doc := bytes.NewBufferString(`{
   "doc":  { "name": "jack",
    "age": 35,
    "salary": 5701.1
}}`)
  update := client.Update
  response, err := update("user", "1", doc, update.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

المخرجات

json
[200 OK] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 3,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

يدعم Update API أيضًا استخدام script لتنفيذ عمليات مثل upsert وغيرها، انتقل إلى Update API لمعرفة المزيد.

الحذف

حذف مستند محدد من خلال المعرف

go
func main() {
    client, err := newClient()
    if err != nil {
       panic(err)
    }

    deleteDoc := client.Delete
    response, err := deleteDoc("user", "1", deleteDoc.WithPretty())
    if err != nil {
       panic(err)
    }
    fmt.Println(response)
}

المخرجات

json
[200 OK] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 3,
  "result" : "deleted",
  "_shards" : {
    "total" : 3,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

البحث

إن Search API هو الأكثر استخدامًا في ES API، فيما يلي عرض بسيط للاستخدام، أولاً نجهز البيانات.

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  for i := range 10 {
    doc := bytes.NewBufferString(fmt.Sprintf(`{
    "name": "%s",
    "age": %d,
    "salary": %f
}`, randomName(), rand.Intn(18)+18, rand.Float64()))
    create := client.Create
    response, err := create("user", string('0'+i), doc, create.WithPretty())
    if err != nil {
      panic(err)
    }
    fmt.Println(response)
  }
}

func randomName() string {
  var b []byte
  for range 10 {
    b = append(b, byte(rand.Intn(26)+'a'))
  }
  return string(b)
}

استخدام Search API مطابق تمامًا لاستخدام HTTP API.

الاستعلام عن جميع المستندات

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }
  dsl := bytes.NewBufferString(`{"query": {"match_all":{}}, "size": 1}`)
  search := client.Search
  response, err := search(search.WithBody(dsl), search.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

مطابقة حقل معين

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }
  dsl := bytes.NewBufferString(`{"query": {"term":{ "age": 22 } }, "size": 1}`)
  search := client.Search
  response, err := search(search.WithBody(dsl), search.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

خلاصة

العمليات الأساسية هي تقريبًا هكذا، استخدامها مطابق تمامًا لـ HTTP API، إذا أتقنت ES، فلن تواجه أي مشكلة في تشغيل Go API. أما بالنسبة للعمليات الأكثر تقدمًا مثل cluster و data stream وغيرها من API، يرجى استكشافها بنفسك.

Golang تم تحريره بواسطة www.golangdev.cn