Trong các bài viết trước, chúng ta đã biết được Elasticsearch là gì và đã cài đặt thành công một cluster Elasticsearch và Kibana. Từ đây, chúng ta có thể bắt đầu tìm hiểu về cách tổ chức và thao tác với các dữ liệu trên search engine đầy mạnh mẽ này. Qua bài viết các bạn sẽ biết cách tạo các index để lưu dữ liệu và tận dụng các mapping của Elasticsearch một cách linh hoạt để tổ chức dữ liệu của mình đúng đắn và phù hợp cho các nhu cầu sử dụng sau này.

1. Cấu hình cho một index trong Elasticsearch

1.1. Một chút sơ lược về index trong Elasticsearch

Ở bài viết trước, chúng ta đã biết đơn vị lưu trữ tập chung các dữ liệu trong Elasticsearch được gọi là index. Nó được thiết kế để cho phép tìm kiếm full-text search. Cách thức của nó khá đơn giản, các văn bản được phân tách ra thành từng từ có nghĩa sau đó sẽ được map xem thuộc văn bản nào. Khi search tùy thuộc vào loại search sẽ đưa ra kết quả cụ thể.

Đơn giản là thế nhưng việc cấu hình một index sao cho chuẩn, sao cho đảm bảo khả năng chịu đựng tải cao, các sự thay đổi trong cấu trúc của index hay số lượng replica được tạo trong một cluster lại là một điều rất đáng lưu tâm.

1.2. Cú pháp tạo index trong Elasticsearch

Index trong Elasticsearch được cấu hình bởi nhiều thông số. Các thông số này được chia làm hai level đó là static và dynamic.

  • Các thông số cấu hình static: các cấu hình cho index chỉ được set tại thời điểm tạo index, sau đó thì không thể sửa đổi hay update:
POST /my-index-000001/_open
  • Các cấu hình dynamic: các cấu hình cho index có thể được thay đổi sau khi tạo index bằng cách sử dụng API update index-settings:
PUT /my-index-000001/_settings
{
  "index" : {
    "number_of_replicas" : 2
  }
}

1.3. Một số thông số cấu hình thông dụng cho index

Cấu hình static chỉ có thể cấu hình được tại thời điểm tạo index:

  • index.number_of_shards: số shard của một index, mặc định là 1. Mỗi một index có thể có tối đa 1024 shard.
  • index.codec: codec sử dụng để nén dữ liệu thực hiện lưu trữ, giá trị mặc định là LZ4.
  • index.soft_deletes.enable: có cho phép sử dụng soft delete hay không.
  • index.shard.check_on_startup: Elasticsearch sẽ tự động thực hiện các bước kiểm tra của nó sau khi nội dung của một shard được thay đổi tại nhiều điểm của một chu trình. Cấu hình này quyết định việc một cluster có thực hiện thêm các bước kiểm tra khác sau khi khởi động một shard hay không. Thông số này nhận vào các giá trị sau:

+ false: không thực hiện. Đây là giá trị mặc định và được khuyên sử dụng.

+ checksum: kiểm tra xem checksum các file trong shard có chung checksum với nội dung của nó.

+ true: thực hiện giống như bật checksum và kiểm tra thêm các thông tin logic thay đổi của các shard.

Cấu hình dynamic: có thể thay đổi cấu hình bằng API update index-settings:

  • index.number_of_replicas: số lượng các replica mà từng shard chính có.
  • index.auto_expand_replicas: tự động tăng số lượng replica dựa trên số node trong cluster. Giá trị này cần được cấu hình theo dạng một dải dữ liệu phân cách bởi dấu - (ví dụ: 0-5) hoặc sử dụng all cho điểm cực đại(0-all). 
  • index.search.idle.after: bao nhiêu lâu thì một shard không thể nhận một yêu cầu get hoặc search cho đến khi nó bị coi là đang chờ, mặc định là 30s.
  • index.refresh_interval: cấu hình tần suất thực hiện một quy trình refresh, điều này gây tác động gần như là ngay lập tức, giá trị mặc định là 1s. Chúng ta hoàn toàn có thể cấu hình giá trị này bằng -1 để tắt tính năng này.

>>> Xem thêm bài viết:

Giới thiệu về Elasticsearch và các điều bạn cần biết

Hướng dẫn cài đặt Elasticsearch và Kibana với Docker

2. Tạo mapping cho index sao cho phù hợp?

Mapping trong Elasticsearch là quá trình định nghĩa một document và các giá trị trong nó được lưu trữ và đánh chỉ mục cho tìm kiếm. 

Mỗi một document là một tập hợp của nhiều trường, các trường sẽ có kiểu dữ liệu của riêng mình. Khi tạo mapping cho dữ liệu, chúng ta sẽ tạo một định nghĩa các mapping có chứa các trường sẽ có trong các document. 

Có hai kiểu định nghĩa mapping cho index trong Elasticsearch: dynamic mapping và explicit mapping. Mỗi cách sẽ có những ưu điểm riêng của chúng dựa trên các mà bạn muốn sử dụng dữ liệu của mình. 

Dưới đây là những phân tích của Stringee về các kiểu mapping này.

2.1. Dynamic mapping

Dynamic mapping cho phép bạn có thể tạo và làm rất nhiều điều với dữ liệu của chúng ta ngay khi vừa mới bắt đầu. Elasticsearch sẽ tự động thêm các field một cách tự động mỗi khi nó kiểm tra thấy field của một document mới được index nhưng chưa có trong index. Bạn hoàn toàn có thể thêm trường cho mapping bằng cách thêm trường trong document hay cũng có thể thêm field vào các inner object hay các nested field.

Để có thể kiểm soát các custom mapping trong khi sử dụng kiểu mapping này. Bạn có thể sử dụng dynamic template để định nghĩa chúng và áp dụng cho các kiểu dữ liệu mà bạn muốn Elastic áp dụng cho chúng.

2.2. Explicit mapping

Với kiểu mapping này, Elasticsearch sẽ yêu cầu chúng ta chọn cách định nghĩa trước cấu trúc dữ liệu mà chúng ta sẽ thực hiện, cụ thể như là:

  • Các trường chuỗi nào có thể được coi như là một trường toàn các giá trị text
  • Trường nào có chứa các số, ngày, giá trị địa lý
  • Kiểu format các giá trị date
  • Các rule được tùy chỉnh để điều khiển mapping từ các trường được thêm bởi dynamic mapping

Cú pháp để tạo mapping sẽ có dạng như sau:

PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}

Với ví dụ này, chúng ta đã tạo thành công một index với field đã được định nghĩa: age có dạng là integer, email có dạng là keyword và name có dạng là text.

Cấu hình explicit mapping như thế nào? Đọc đến đây, các bạn có thể thấy được rằng, việc sử dụng Elasticsearch trong các môi trường thực tế thì việc sử dụng explicit mapping có vẻ là nên được sử dụng hơn đúng không. Quả đúng là như vậy, thế nhưng việc cấu hình sao cho đúng lại là một vấn đề mà chúng ta rất cần quan tâm đến.

Elasticsearch có rất nhiều kiểu mapping cho phép chúng ta có thể định nghĩa dữ liệu của mình:

  • binary: các giá trị nhị phân dưới dạng chuỗi Base64
  • boolean: có thể chứa giá trị true hay false
  • Các kiểu keywords: bao gồm các kiểu mapping keyword, constant_keyword, wildcard
  • Các kiểu số: long, double, integer
  • Các kiểu date: date, date_nanos
  • alias: định nghĩa một alias cho các field đã tồn tại
  • object: một Json Object
  • flattened: một Json Object dưới dạng một chuỗi duy nhất
  • nested: một Json Object giữ lại toàn bộ các mối quan hệ liên quan trong dữ liệu của nó
  • join: định nghĩa quan hệ cha/con của các dữ liệu trong cùng một index
  • Các kiểu text: text, match_only_text được sử dụng cho các text chưa được cấu trúc
  • annotated-text: text chứa các annotation được đánh dấu
  • completion: sử dụng trong trường hợp hỗ trợ auto-completion

Trên đây là một số mapping thông dụng, ngoài ra các bạn hoàn toàn có thể tìm hiểu thêm các kiểu mapping khác trên các docs của Elasticsearch nhé.

3. Thao tác với dữ liệu trên Elasticsearch

3.1. Thêm document vào một index

Cú pháp để sử dụng API thêm document vào index như sau:

PUT /<target>/_doc/<_id>

POST /<target>/_doc/

PUT /<target>/_create/<_id>

POST /<target>/_create/<_id>

Ví dụ, chúng ta thêm một document vào index index-01 bằng cách sử dụng lệnh sau trên DevTool của Kibana:

POST index-01/_doc
{
  "age": 18,
  "email": "john.doe@gmail.com",
  "name": "John Doe"
}

Kết quả nhận được là:

{
  "_index": "index-01",
  "_id": "i5IEpYsB-XeRF7h4FTkP",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

Theo kết quả nhận được, chúng ta biết được document đã được thêm thành công vào index index-01 và có id được tự sinh là i5IEpYsB-XeRF7h4FTkP

Thực hiện một truy vấn đơn giản để tìm lại document đã thêm, ta có thể sử dụng câu lệnh sau:

GET index-01/_search
{
  "query": {
    "match": {
      "_id": "i5IEpYsB-XeRF7h4FTkP"
    }
  }
}

Kết quả của truy vấn này ta thu được kết quả sau:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "index-01",
        "_id": "i5IEpYsB-XeRF7h4FTkP",
        "_score": 1,
        "_source": {
          "age": 18,
          "email": "john.doe@gmail.com",
          "name": "John Doe"
        }
      }
    ]
  }
}

>>> Xem thêm bài viết:

3.2. Update document trên index

Cú pháp để update một document:

POST /<index>/_update/<_id>

Có rất nhiều cách để có thể update một document trên Elasticsearch, nếu bạn muốn thay đổi một phần document thì bạn có thể sử dụng cú pháp dưới đây:

POST <index>/_update/<_id>
{
  "doc": {
    "<field_value>": "<value>"
  }
}

Với document ở trên, chúng ta có thể thử update nó với câu lệnh sau:

POST index-01/_update/i5IEpYsB-XeRF7h4FTkP
{
  "doc": {
    "name": "John"
  }
}

Sau khi thực hiện thành công chúng ta có thể kiểm tra lại document, kết quả thu được sẽ là:

{
  "took": 842,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "index-01",
        "_id": "i5IEpYsB-XeRF7h4FTkP",
        "_score": 1,
        "_source": {
          "age": 18,
          "email": "john.doe@gmail.com",
          "name": "John"
        }
      }
    ]
  }
}

3.3. Xóa document khỏi index

Xóa một document:

DELETE /<index>/_doc/<_id>

Kết bài

Qua bài viết này, Stringee đã giới thiệu tới các bạn cách làm sao để có thể tạo được một index phù hợp với nhu cầu sử dụng của mình, tổ chức làm sao để có thể tìm kiếm được theo ý muốn của ứng dụng mình phát triển. Tới đây, chúng ta sẽ cùng nhau tìm hiểu nhiều chủ đề hay hơn nữa về Elasticsearch trong loạt này, các bạn hãy cùng chú ý đón đọc nhé.


Stringee Communication APIs là giải pháp cung cấp các tính năng giao tiếp như gọi thoại, gọi video, tin nhắn chat, SMS hay tổng đài CSKH cho phép tích hợp trực tiếp vào ứng dụng/website của doanh nghiệp nhanh chóng. Nhờ đó giúp tiết kiệm đến 80% thời gian và chi phí cho doanh nghiệp bởi thông thường nếu tự phát triển các tính năng này có thể mất từ 1 - 3 năm.

Bộ API giao tiếp của Stringee hiện đang được tin dùng bởi các doanh nghiệp ở mọi quy mô, lĩnh vực ngành nghề như TPBank, VOVBacsi24, VNDirect, Shinhan Finance, Ahamove, Logivan, Homedy,  Adavigo, bTaskee…

Quý bạn đọc quan tâm xin mời đăng ký NHẬN TƯ VẤN TẠI ĐÂY: