ElasticSearch
الموقع الرسمي: Elasticsearch:官方分布式搜索和分析引擎 | Elastic
Elasticsearch هو محرك بحث وتحليل بيانات موزع بأسلوب RESTful، قادر على حل مختلف الحالات الاستخدام التي تظهر باستمرار. كنواة لـ Elastic Stack، يقوم Elasticsearch بتخزين بياناتك مركزيًا، مما يتيح لك إكمال البحث بسرعة فائقة، وضبط الصلة بدقة، وإجراء تحليلات قوية، وتوسيع النطاق بسهولة. ستشرح هذه المقالة كيفية استخدام Go لإجراء بعض العمليات الأساسية على Elasticsearch، مثل الإنشاء والحذف والتعديل والاستعلام وما إلى ذلك. إذا لم تكن على دراية بـ Elasticsearch، يرجى تعلمه بنفسك أولاً.
التبعيات
تحميل مكتبة التبعيات الرسمية
$ github.com/elastic/go-elasticsearch/v7إذا كنت تستخدم ES8، فقم بتغيير النسخة
$ github.com/elastic/go-elasticsearch/v8TIP
تستخدم هذه المقالة ES8 للعرض التقديمي
الاتصال
استخدم الدالة elasticsearch.NewClient لإنشاء اتصال جديد
func NewClient(cfg Config) (*Client, error)يستخدم ES8+ اتصال HTTPS افتراضيًا الآن. عند إنشاء اتصال HTTPS، إما أن تستخدم شهادة CA أو بصمة CA، وكلاهما يتم إنشاؤهما على جانب خادم Elasticsearch. مثال على ذلك:
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 يمكن اختبار ما إذا كانت الخدمة متاحة:
pingResp, err := client.Ping(client.Ping.WithPretty(), client.Ping.WithHuman())
if err != nil {
panic(err)
}
fmt.Println(pingResp)المخرجات
[200 OK]ومثال آخر، من خلال Info API لعرض حالة الخدمة:
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
// Indices contains the Indices APIs
type Indices struct {
AddBlock IndicesAddBlock
Analyze IndicesAnalyze
ClearCache IndicesClearCache
Clone IndicesClone
Close IndicesClose
...
...
ValidateQuery IndicesValidateQuery
}الإنشاء
إنشاء فهرس كما يلي:
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"salary": {
"type": "double"
}
}
}
}العملية الفعلية تشبه إرسال طلب HTTP إلى حد كبير
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)
}المخرجات
[200 OK] {"acknowledged":true,"shards_acknowledged":true,"index":"user"}الحصول على
الحصول على معلومات عدة فهارس
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)
}المخرجات
[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"
}
}
}
}
}التحليل
تحليل سلسلة نصية لفهرس محدد وإرجاع النتائج، النص كالتالي:
{
"analyzer": "standard",
"text": ["this is a test", "the second text"]
}الكود
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)
}المخرجات
[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
}
]
}الحذف
حذف عدة فهارس محددة
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)
}المخرجات
[200 OK] {"acknowledged":true}في واجهات API أعلاه، يمكن ملاحظة أن جسم الطلب يحتاج إلى تسلسله يدويًا، فالجهة الرسمية لم تقم بتحويله إلى هياكل Go، كما أن جسم الاستجابة يحتاج أيضًا إلى معالجته يدويًا. هذه هي واجهات API الأكثر استخدامًا، واستخدام الأخرى ليس مختلفًا كثيرًا.
المستندات
الإنشاء
إنشاء مستند كالتالي:
{
"name": "jack",
"age": 12,
"salary": 5701.1
}الكود
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)
}المخرجات
[201 Created] {
"_index" : "user",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 3,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}الحصول على
الحصول على مستند بمعرف محدد
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)
}المخرجات
[200 OK] {
"_index" : "user",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "jack",
"age" : 12,
"salary" : 5701.1
}
}التحديث
تحديث محتوى المستند
{
"doc": {
"name": "jack",
"age": 35,
"salary": 5701.1
}
}الكود
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)
}المخرجات
[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 لمعرفة المزيد.
الحذف
حذف مستند محدد من خلال المعرف
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)
}المخرجات
[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، فيما يلي عرض بسيط للاستخدام، أولاً نجهز البيانات.
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.
الاستعلام عن جميع المستندات
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)
}مطابقة حقل معين
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، يرجى استكشافها بنفسك.
