+8

[Elasticsearch] - Các khái niệm cơ bản - Phần 2

Welcome back to series Simple & Basic Elasticsearch . Trong phần này mình sẽ giới thiệu với các bạn các khái niệm về MAPPING, ANALYSISTOKENIZER.

1. Mapping

MAPPING là quá trình xử lý cách mà các DOCUMENT (và các PROPERTIES bên trong) sẽ được index và lưu trữ như thế nào. MAPPING giúp chúng ta cùng lúc khởi tạo 1 field & định nghĩa cách field đó được index: (thông qua Analyzer - sẽ được nói ở mục sau)

  • những string FIELD nào sẽ được xử lí dưới dạng Full text field
  • những field nào sẽ có kiểu number, date hay geolocations
  • format của các field với "type": "date"
  • ...
PUT my_index 
{
  "mappings": {
    "doc": { 
      "properties": { 
        "title":    { "type": "text"  }, 
        "name":     { "type": "text"  }, 
        "age":      { "type": "integer" },  
        "created":  {
          "type":   "date", 
          "format": "strict_date_optional_time||epoch_millis"
        }
      }
    }
  }
}

Giải thích:

  • Các Properties ("title", "name""age") được định nghĩa với type ( kiểu dữ liệu và cách Mapping cho từng Field ) ngay khi Index được khởi tạo.
  • Các Properties"type": "date" có thể khai báo thêm format.

Note

  • 1 field có thể được index theo nhiều cách khác nhau để phục vụ các mục đích khác nhau. Chẳng hạn, 1 field có type string có thể được index dưới dạng text nhằm phục vụ cho việc full-text search, nhưng đồng thời sẽ được index dưới dạng keyword nhằm mục đích sortingaggregations. Tương tự, bạn cũng có thể cùng lúc index 1 field với các analyzer(sẽ được giải thích ở mục sau): standard analyzer, english analyzerfrench analyzer với các mục đích khác nhau. Và để thực hiện được điều này chúng ta sẽ cần sử dụng tới multi-field (sẽ được giải thích ở bài sau). Các bạn có thể tham khảo thêm về multi-field tại Elasticsearch Docs
  • Mapping Type và các Properties bên trong không bắt buộc phải khai báo trước khi chúng được sử dụng. Bạn có thể khai báo TypeProperties ngay khi tạo index (cách này còn được gọi là Explicit Mapping) hoặc sử dụng Dynamic Mapping.

2. Analysis

Trong ví dụ trên mình có nói đến việc: trong quá trình MAPPING, chúng ta có thể khai báo cách mà 1 DOCUMENT được phân tách. Và nhìn đoạn ví dụ đó các bạn sẽ thắc mắc: Bằng cách nào?

Well, Analysis chính là câu trả lời cho các bạn.

Theo doc của Elasticsearch:

Analysis is the process of converting text, like the body of any email, into tokens or terms which are added to the inverted index for searching. Analysis is performed by an analyzer which can be either a built-in analyzer or a custom analyzer defined per index.

Analysis là quá trình chuyển đổi các Document Properties từ dạng text sang dạng Tokens of Terms. Các Token of Terms này sau đó sẽ được thêm vào Inverted Index để searching. Quá trình Analysis sẽ được thực hiện thông qua các Analyzer, các Analyzer này có thể là các Built-in Analyzer sẵn có của Elasticsearch hoặc là các Custom-Analyzer do chúng ta định nghĩa. Ta có thể chỉ định Analyzer ngay khi khởi tạo Index và trong khi thực hiện Searching.

  • Index-time Analysis
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "title": {
          "type":     "text",
          "analyzer": "english"
        }
      }
    }
  }
}

Như đoạn ví dụ trên, ngay khi khởi tạo INDEX và định nghĩa các MAPPING, chúng ta cũng đồng thời chỉ định luôn analyzer cho properties tittle. Như vậy tittle sẽ có kiểu là text và sẽ được analyze bới english analyzer.

Default:

At index time, if no analyzer has been specified, it looks for an analyzer in the index settings called default. Failing that, it defaults to using the standard analyzer.

Sau khi khai báo mapping cho field title, ta sẽ put thử 1 document vào index:

POST my_index/doc
{
    "title" : "The quick brown fox jumps over the lazy dog"
}

Document này sau đó sẽ được analyze bởi english analyzer và được tách thành các term : [quick, brown, fox, jump, over, lazi, dog]. Các term này sau đó sẽ được đưa vào Inverted Index để phục vụ cho quá trình tìm kiếm sau này.

  • Search-time Analysis

Sau khi put 1 document vào index, hãy thử search với 1 vài keyword:

GET my_index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "black foxes"
      }
    }
  }
}

và kết quả chúng ta nhận được:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "my_index",
        "_type": "doc",
        "_id": "Fi4VdmIB2EPp1n0baI-V",
        "_score": 0.2876821,
        "_source": {
          "title": "The quick brown fox jumps over the lazy dog"
        }
      },
      {
        "_index": "my_index",
        "_type": "doc",
        "_id": "-y4VdmIB2EPp1n0bDo6b",
        "_score": 0.2876821,
        "_source": {
          "title": "The quick brown fox jumps over the lazy dog"
        }
      }
    ]
  }
}

Mắc dù 2 keyword chúng ta dùng để search không xuất hiện trong document mà chúng ta put vào (brown vs black, fox vs foxes), nhưng do chúng ta cùng sử dụng english analyzer khi index document và khi search, nên term trong query string có thể match với term trong document bởi inverted index.

Tokenizer

  • Tokenizer là 1 options được khai báo trong quá trình ta định nghĩa 1 analyzer. Tokenizer nhận input là một stream các ký tự, phân tách chúng thành từng token riêng rẽ(thông thường sẽ là các từ riêng lẻ), và output ra 1 stream các token. Chẳng hạn, whitespace tokenizer sẽ break 1 đoạn text thành các token bất cứ khi nào 1 khoảng trắng xuất hiện: [quick brown fox] sẽ được break thành [quick, brown, fox]

  • Định nghĩa & khai báo 1 tokenizer:

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_custom_analyzer"
        }
      },
      "tokenizer": {
        "my_custom_analyzer": {
          "type":      "standard",
          "tokenizer": "english",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "lowercase",
            "asciifolding"
          ]
        }
      }
    }
  }
}

Giải thích: Trong ví dụ trên ta định nghĩa 1 custom-analyzer với tên là my_custom_analyzer . Analyzer này có type (required) standard, sử dụng english tokenizer, có thêm 1 số char_filter ... (mình sẽ không giải thích nhiều ở đây vì như vậy sẽ gây rối rắm khó hiểu).

Sau khi put mapping vào 1 index mới khởi tạo, thử GET ra setting của index đó để xem analyzer chúng ta vừa định nghĩa:

GET my_index

Result:

{
  "my_index": {
    "aliases": {},
    "mappings": {},
    "settings": {
      "index": {
        "number_of_shards": "5",
        "provided_name": "my_index",
        "creation_date": "1522409694119",
        "analysis": {
          "analyzer": {
            "my_analyzer": {
              "tokenizer": "my_custom_analyzer"
            }
          },
          "tokenizer": {
            "my_custom_analyzer": {
              "filter": [
                "lowercase",
                "asciifolding"
              ],
              "char_filter": [
                "html_strip"
              ],
              "type": "standard",
              "tokenizer": "english"
            }
          }
        },
        "number_of_replicas": "1",
        "uuid": "leaIAdodSe2Yu9CY8Xn2jA",
        "version": {
          "created": "6000199"
        }
      }
    }
  }
}

4. Ingest Node

Ingest Node được sử dụng khi ta cần pre-process các document trước khi chúng được index. Ingest node sẽ can thiệp vào giữa quá trình bulkindex, thực hiện các phép biến đổi, sau đó truyền kết quả trở lại index/bulk api.

Mặc định, các node đều enable ingest, vậy nên các node đêu có thể thực hiện được ingest. Nếu muốn disable ingest, đơn giản chỉ cần thêm config vào file elasticsearch.yml:

node.ingest: false

Để thực hiện pre-process, đầu tiên ta cần định nghĩa 1 pipeline chỉ định 1 chuỗi các processor. Mỗi processor sẽ biển đổi các document theo 1 cách nào đó. Cluster state sẽ thực hiện lưu trữ các configured pipeline.

Để sử dụng pipeline, những gì bạn cần làm là truyền thêm tham số pipeline vào request index/bulk:

PUT my-index/_doc/my-id?pipeline=my_pipeline_id
{
  "foo": "bar"
}

5. Query DSL

Elastic cung cấp cho chúng ta 1 bộ Query DSL đầy đủ dựa trên JSON để define các query. Theo như Doc của Elastic, Query DSL được ví như 1 AST(Abstract Syntax Tree), gổm 2 loại clause: Leaf query clauses & Compound query clauses.

Bạn có thể tham khải Doc của Elastic để hiểu rõ hơn về Query DSL.

6. Tổng Kết

2 phần của bài viết này đã cung cấp cho các bạn 1 vài khái niệm cơ bản của Elasticsearch. Trong những bài viết sau mình sẽ đi sâu hơn vào từng khái niệm để có thể giúp các bạn hiểu rõ hơn về Elastic.

Hy vọng các bạn cảm thấy bài viết này hữu ích.

Source: Elasticsearch Document

Thanks for reading!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí