Cấu hình và sử dụng Dynamic Mapping trong Elasticsearch

Mục lục- 1 Dynamic Mapping

  • 1.1 Dynamic Mapping là gì
  • 1.2 Thử nghiệm Dynamic Mapping
  • 1.3 Phân tích nguyên nhân kết quả tìm kiếm không nhất quán
  • 2 Kích hoạt chiến lược Dynamic Mapping
  • 2.1 Ràng buộc chiến lược
  • 2.2 Ví dụ về chiến lược
  • 3 Tùy chỉnh chiến lược Dynamic Mapping
  • 3.1 date_detection - Chiến lược nhận dạng ngày
  • 3.2 Tùy chỉnh mẫu Dynamic Mapping trong type
  • 3.3 [Đã lỗi thời] Tùy chỉnh mẫu mặc định trong index
  • Tài liệu tham khảo

Lưu ý: Các ví dụ trong bài viết này sử dụng phiên bản Elasticsearch 6.6.0, các phiên bản khác có thể có sự khác biệt.

1 Dynamic Mapping

1.1 Dynamic Mapping là gì

Dynamic Mapping là một tính năng quan trọng của Elasticsearch: không cần phải tạo index trước, định nghĩa thông tin mapping và type, bạn có thể chèn trực tiếp dữ liệu tài liệu vào ES, và ES sẽ tự động cấu hình type và các thông tin mapping cho mỗi field mới dựa trên kiểu dữ liệu có thể có của nó, quá trình này được gọi là Dynamic Mapping.

Ví dụ về Dynamic Mapping trong Elasticsearch:

Nội dung trường (field) Kiểu trường được ánh xạ (type)
true | false boolean
1234 long
123.4 float
2018-10-10 date
"hello world" text

Lưu ý: Mặc dù Dynamic Mapping tiện lợi, nhưng không trực quan. Để tùy chỉnh các cài đặt liên quan, bạn có thể tạo index và type trước khi thêm tài liệu, và cấu hình mapping tương ứng cho type, để thay thế cho Dynamic Mapping. Cấu hình mapping có thể tham khảo bài viết: ES 11 - Cách cấu hình Mapping trong Elasticsearch.

1.2 Thử nghiệm Dynamic Mapping

(1) Chèn dữ liệu sau:

Nếu đã có index website, hãy xóa DELETE blog trước, sau đó thực hiện các thao tác chèn dưới đây:

PUT blog/_doc/1
{
    "blog_id": 10001,
    "author_id": 5520,
    "post_date": "2018-01-01",
	  "title": "bài blog đầu tiên của tôi",
	  "content": "bài blog đầu tiên trên website"
}

PUT blog/_doc/2
{
    "blog_id": 10002,
    "author_id": 5520,
    "post_date": "2018-01-02",
    "title": "bài blog thứ hai của tôi",
    "content": "bài blog thứ hai trên website"
}

PUT blog/_doc/3
{
    "blog_id": 10003,
    "author_id": 5520,
    "post_date": "2018-01-03",
    "title": "bài blog thứ ba của tôi",
    "content": "bài blog thứ ba trên website"
}

(2) Thực hiện các bài kiểm tra tìm kiếm:

Lưu ý: Trường hợp có 3 kết quả.

GET blog/_search?q=2018                 // 1 kết quả, các phiên bản trước 5.6 là 3 kết quả
GET blog/_search?q=2018-01-01           // 1 kết quả, các phiên bản trước 5.6 là 3 kết quả
GET blog/_search?q=post_date:2018       // 1 kết quả
GET blog/_search?q=post_date:2018-01-01	// 1 kết quả

(3) Xem mapping được tự động tạo bởi ES:

GET blog/_mapping

// Kết quả như sau:
{
  "blog" : {                  // index là blog
    "mappings" : {
      "_doc" : {              // type là _doc - để dễ dàng nâng cấp phiên bản sau này
        "properties" : {
          "author_id" : {
            "type" : "long"
          },
          "blog_id" : {
            "type" : "long"
          },
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "post_date" : {
            "type" : "date"  // Ngày "2018-01-01" được tự động nhận dạng là kiểu date
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

1.3 Phân tích nguyên nhân kết quả tìm kiếm không nhất quán

Nếu bạn đang sử dụng phiên bản Elasticsearch 5.6.x trở về trước, kết quả tìm kiếm trong mục [1.2] sẽ không nhất quán, lý do là:

Khi Elasticsearch tự động tạo mapping, nó ánh xạ các field khác nhau với các kiểu dữ liệu khác nhau, và các kiểu dữ liệu khác nhau có hành vi phân tích, tìm kiếm khác nhau.

Khi chúng ta tìm kiếm bằng cách q=xxx, tầng dưới sẽ tìm kiếm từ trường _all của mỗi tài liệu —— Elasticsearch mặc định trích xuất tất cả các giá trị của các field trong tài liệu vào một trường meta có tên _all.

Tài liệu chính thức chỉ ra rằng, từ phiên bản 6.0+, trường _all đã bị cấm sử dụng, và chúng ta nên sử dụng copy_to để thực hiện chức năng tương tự.—— Điều này có nghĩa là, nếu trường _all bị tắt, sẽ không có hiện tượng kết quả tìm kiếm không nhất quán.

(1) GET website/blog/_search?q=2018

Giá trị của _all trong tài liệu id=1 là:

2018-01-01 my first blog my first blog in the website 5520

Giải thích: Trường _all chỉ mục tất cả các giá trị dưới dạng chuỗi, vì vậy ngày tháng được chỉ mục thành ba giá trị: năm, tháng, ngày. Kết quả chỉ mục ngược của trường _all như sau:

| | doc1 | doc2 | Doc3 | |---|---|---|---| | 2018 | \* | \* | \* | | 01 | \* | \* | \* | | 02 | | \* | | | 03 | | | \* |

Trong tìm kiếm này, Elasticsearch tìm kiếm từ trường _all, và cả 3 tài liệu đều có 2018, vì vậy có 3 kết quả.

(2) GET website/blog/?q=2018-01-01

Tương tự như (1), Elasticsearch cũng tìm kiếm từ trường _all, và số kết quả cũng là 3.

(3) GET website/blog/_search?q=post_date:2018-01-01

Tìm kiếm này chỉ định điều kiện tìm kiếm, Elasticsearch sẽ tìm kiếm từ trường post_date, và post_date được ánh xạ thành kiểu date, vì vậy sẽ thực hiện so khớp chính xác.

Trường kiểu date có định dạng chỉ mục mặc định cố định. Kết quả chỉ mục ngược của trường post_date như sau:

| | doc1 | doc2 | doc3 | |---|---|---|---| | 2018-01-01 00:00:00 UTC | \* | | | | 2018-01-02 00:00:00 UTC | | \* | | | 2018-01-03 00:00:00 UTC | | | \* |

Có thể thấy, chỉ có 1 kết quả thỏa mãn, đó là doc1.

(4) GET /_search?q=post_date:2018

Đây là một tối ưu hóa trong phiên bản ES 5.x, tìm kiếm post_date:01 sẽ không có kết quả, tìm kiếm 2018 sẽ cho ra kết quả đầu tiên.

2 Kích hoạt chiến lược Dynamic Mapping

2.1 Ràng buộc chiến lược

Chiến lược Chức năng
true Kích hoạt —— Khi gặp trường không quen thuộc, thực hiện Dynamic Mapping
false Tắt —— Bỏ qua các trường không quen thuộc
strict Khi gặp trường không quen thuộc, báo lỗi

2.2 Ví dụ về chiến lược

(1) Sử dụng các chiến lược ràng buộc khác nhau:

PUT blog_user
{
"mappings": {
"_doc": {
"dynamic": "strict",			// Chiến lược kiểm soát nghiêm ngặt
"properties": {
"name": { "type": "text" },
"address": {
"type": "object",
"dynamic": "true"		// Kích hoạt chiến lược Dynamic Mapping
}
}
}
}
}

(2) Chèn dữ liệu để minh họa:

// Khi chèn dữ liệu, thêm một trường bổ sung
PUT blog_user/1
{
"name": "shou feng",
"content": "đây là blog của tôi",  // Trường bổ sung
"address": {
"province": "guangdong",
"city": "guangzhou"
}
}

Sẽ ném ra lỗi sau:

{
"error": {
"root_cause": [
{
"type": "strict_dynamic_mapping_exception",
// Lỗi: Không cho phép thêm trường động
"reason": "mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed"
}
],
"type": "strict_dynamic_mapping_exception",
"reason": "mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed"
},
"status": 400
}

Thêm dữ liệu phù hợp với ràng buộc, thao tác sẽ thành công:

PUT blog_user/_doc/1
{
"name": "shou feng",
"address": {
"province": "guangdong",
"city": "guangzhou"
}
}

(3) Xem thông tin mapping:

GET user/_mapping

// Thông tin mapping như sau:
{
"blog_user" : {
"mappings" : {
"_doc" : {
"dynamic" : "strict",      // Ràng buộc nghiêm ngặt
"properties" : {
"address" : {
"dynamic" : "true",    // Kích hoạt chiến lược Dynamic Mapping
"properties" : {
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"province" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"name" : {
"type" : "text"
}
}
}
}
}
}

3 Tùy chỉnh chiến lược Dynamic Mapping

3.1 date_detection - Chiến lược nhận dạng ngày

Đối với dữ liệu kiểu date, Elasticsearch có chiến lược nhận dạng mặc định, ví dụ "yyyy-MM-dd". Có trường hợp:

① Khi thêm tài liệu lần đầu, một field có giá trị như "2018-01-01", kiểu của nó sẽ được ánh xạ động thành date;
② Khi thêm tài liệu sau này, field đó có giá trị như "hello world", ES sẽ báo lỗi do không khớp kiểu.

Để giải quyết vấn đề này, bạn có thể tắt thủ công date_detection cho một type; nếu không cần tắt, nên chỉ định thủ công field này là kiểu date. Ví dụ:

PUT blog_user/_mapping/_doc
{
"date_detection": false
}

3.2 Tùy chỉnh mẫu Dynamic Mapping trong type

(1) Định nghĩa mẫu Dynamic Mapping trong type —— Ánh xạ tất cả các kiểu String thành kiểu text và keyword:

Xóa index blog_user đã có: DELETE blog_user, sau đó thực hiện lệnh dưới đây:

PUT blog_user
{
"mappings": {
"_doc": {
"dynamic_templates": [
{
"en": {       // Tên mẫu động
"match": "*_en",           // Khớp với field có tên "*_en"
"match_mapping_type": "string",
"mapping": {
"type": "text",        // Ánh xạ tất cả các kiểu string thành kiểu text
"analyzer": "english", // Sử dụng bộ phân tích từ english
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}
}

(2) Thêm dữ liệu:

PUT blog_user/_doc/1
{
"name": "người dùng đăng ký đầu tiên"
}

PUT blog_user/_doc/2
{
"name_en": "người dùng đăng ký thứ hai"
}

(3) Tìm kiếm dữ liệu:

// Có kết quả: "name" không khớp với bất kỳ mẫu động nào, sử dụng bộ phân tích standard mặc định
GET blog_user/_search
{
"query": {
"match": {"name": "người"}
}
}

// Không có kết quả: "name_en" khớp với mẫu động, sử dụng bộ phân tích english, "the" là từ dừng và bị lọc bỏ
GET blog_user/_search
{
"query": {
"match": {"name_en": "người"}
}
}

Giải thích:

match_mapping_type hỗ trợ các kiểu [object, string, long, double, boolean, date, binary], nếu sử dụng text sẽ ném ra lỗi sau:

{
  "error": {
    "root_cause": [
      {
        "type": "mapper_parsing_exception",
        "reason": "Failed to parse mapping [_doc]: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]"
      }
    ],
    "type": "mapper_parsing_exception",
    "reason": "Failed to parse mapping [_doc]: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]",
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]"
    }
  },
  "status": 400
}

Trong các phiên bản trước 6.0, sẽ có cảnh báo lỗi thời sau:

Deprecation: match_mapping_type [text] is invalid and will be ignored: 
No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]

3.3 [Đã lỗi thời] Tùy chỉnh mẫu mặc định trong index

_default mapping - Mẫu ánh xạ mặc định giống như một biến toàn cục, có tác dụng cho index được cấu hình.

Mẫu ánh xạ mặc định không còn được hỗ trợ trong Elasticsearch 6.x, vì từ phiên bản 6.0, mỗi index chỉ có thể có một type, vì vậy mẫu mặc định không còn ý nghĩa.

Quá trình minh họa dưới đây được thực hiện trên các phiên bản trước 6.0.

(1) Định nghĩa mẫu ánh xạ mặc định trong index:

Xóa index blog_user đã có: DELETE blog_user, sau đó thực hiện lệnh dưới đây:

PUT blog_user
{
"mappings": {
"_default_": {
"_all": { "enabled":  false }
},
"_doc": {
"_all": { "enabled":  true  }
}
}
}

(2) Dynamic Mapping có thể kết hợp với Index Templates.

Cả việc tạo Index tự động và tạo Index một cách rõ ràng, Index Templates đều có thể được sử dụng để cấu hình mappings (ánh xạ), settings (cài đặt) và aliases (bí danh) mặc định cho index mới. Cách sử dụng cụ thể xin tham khảo blog: ES 10 - Cách sử dụng Index Templates trong Elasticsearch

Tài liệu tham khảo

(phiên bản 6.6) Tài liệu chính thức - Dynamic Mapping

Thẻ: Elasticsearch dynamic mapping mapping search indexing

Đăng vào ngày 21 tháng 7 lúc 14:29