Phần 4: ElasticSearch: Search in Depth

1. Finding Exact Values

Khi muốn tìm kiếm chính xác theo keywork thì bạn nên dùng các filter trong ES, và hãy dùng các filter nhiều nhất có thể bởi các filter sẽ không phải tính relevance , tính score giữa input đầu vào và kết quả trả về Các kết quả filter trả về được cache dễ dàng trên server do đó performance tìm kiếm sẽ tốt hơn nếu dùng full-text search.

  • Term Filter: là filter được sử dụng thường xuyên, nó có khả năng xử lý numbers, Booleans, datestext.

Ví dụ: giả sử có một loạt các products được index trong ES:

POST /my_store/products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3"}
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5"}
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }
  • Tìm kiếm chính xác các products có giá price = 20 sử dụng SQL:
SELECT document
FROM products
WHERE price = 20
  • Dùng filter trong ES:
GET /my_store/products/_search
{
    "query" : {
        "filtered" : {
            "query" : {
                "match_all" : {}
            },
            "filter" : {
                "term" : {
                    "price" : 20
                }
            }
        }
    }
}
  • Tìm kiếm sản phẩm có productID = XHDK-A-1293-#fJ3:
SELECT product
FROM products
WHERE productID = "XHDK-A-1293-#fJ3"
  • Truy vấn dùng DSL:
GET /my_store/products/_search
{
"query" : {
        "filtered" : {
            "filter" : {
                "term" : {
                    "productID" : "XHDK-A-1293-#fJ3"
                }
            }
        }
    }
}
    

Kết quả không như mong muốn vì productID đã bị analyze bởi standard analyzer, cần phải config không sử dụng analyzer cho field này

  • Thay đổi mapping type cho productID để field này không được đánh index, sau đó bạn có thể dùng filter
DELETE /my_store
PUT /my_store
{
    "mappings" : {
        "products" : {
            "properties" : {
                "productID" : {
                    "type" : "string",
                    "index" : "not_analyzed"
                }
            }
        }
    }
}
  • Combining Filters

Để tìm kiếm nhiều field trên một query, có thể kết hợp nhiều filter với nhau.

SELECT product FROM products
WHERE
(price = 20 OR productID = "XHDK-A-1293-#fJ3")
AND (price != 30)

Để viết câu query trên bằng DSL, cần dùng Boolean filter

  • Bool Filter:
GET /my_store/products/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "bool" : {
                    "should" : [
                        { "term" : {"price" : 20}},
                        { "term" : {"productID" : "XHDK-A-1293-#fJ3"}}
                        ],
                    "must_not" : {
                         "term" : {"price" : 30}
                    }
                }
            }
        }
    }
}
  • Nested Bool:

SQL query:

SELECT document
FROM products
WHERE productID= "KDKE-B-9947-#kL5"
OR (productID = "JODL-X-1937-#pV7" AND price = 30 )

Nested bool query:

GET /my_store/products/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "bool" : {
                    "should" : [
                        { "term" : {"productID" : "KDKE-B-9947-#kL5"}},
                        { "bool" : {
                            "must" : [
                            { "term" : {"productID" : "JODL-X-1937-#pV7"}},
                            { "term" : {"price" : 30}}
                            ]
                        }}
                    ]
                }
            }
        }
    }
}
  • Có rất nhiều các filter khác nhau giúp chúng ta có thể truy vấn các câu query như sử dụng SQL vây: range, exists, missing ...

2. Full-Text Search

Match Query: Là query để tìm kiếm full-text search. Trước tiên hãy đánh index cho một vài Document:

DELETE /my_index
PUT /my_index
{ "settings": { "number_of_shards": 1 }}
POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }
  • Tìm kiếm bởi một keyword:
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}

Để thực thi câu query trên Elasticsearch sẽ làm những step sau:

  1. Check the field type Check liệu xem field tìm kiếm có phải là field được config như một full-text ( analyzed ) string field hay không. Nếu có thì câu query cũng đường analyze

  2. Analyze the query string

Query stringQUICK! sẽ được xử lý bởi bộ standard analyzer. Vì câu này chỉ có một single term nên QUICK! => ``QUICK`

  1. Find matching docs

ES sẽ tìm trong inverted index xem có Document nào có chứa term QUICK hay không. Trong trường hợp này sẽ là các documents 1, 2 và 3

  1. Score each doc

Tùy thuộc vào mức độ relevance của keyword và kết quả match với document mà ES sẽ score cho mỗi kết quả trả về. Score sẽ được đánh giả bởi thuật toán term frequency/inverse document frequency(TF/IDF) Thuật toán này sẽ đánh giá dựa vào một số yếu tố sau:

  • Term frequency

Tần suất term xuất hiện trong field, term xuất hiện trong field càng nhiều thì điểm càng cao

  • Inverse document frequency

Tần suất term xuất hiện trong mỗi document, term nào xuất hiện trong càng nhiều documents sẽ có điểm thấp hơn các term mà có trong chỉ vài Document

  • Field-length norm

Term mà xuất hiện trong field ngắn hơn sẽ có điểm cao hơn là cũng term đó xuất hiện trong field có độ dài dài hơn

Trong full-text search vẫn còn rất nhiều topic cần tìm hiểu như:

  • Improving precision
  • Controlling precision
  • Combining queries
  • Controlling Analysis

Các bạn có thể tìm đọc cuốn Elasticsearch: The Definitive Guide để tìm hiểu sâu hơn về các vấn đề này và về Elasticsearch nói chung