Truy vấn Elasticsearch – Hướng dẫn chi tiết

Truy vấn Elasticsearch – Hướng dẫn chi tiết

Để viết truy vấn Elasticsearch theo đúng cú pháp của nó có thể khó khăn và gây nhầm lẫn, mặc dù tìm kiếm là chức năng chính của Elasticsearch. Bạn muốn làm chủ các cách truy vấn của Elasticsearch, bài viết này sẽ giúp bạn.

Cú pháp truy vấn Lucene

Elasticsearch là một phần của ELK Stack và được xây dựng trên Lucene, thư viện tìm kiếm từ Apache, và sử dụng cú pháp truy vấn của Lucene. Nó là một phần không thể thiếu của Elasticsearch mà khi bạn trỏ curl hoặc trình duyệt của bạn đến địa chỉ http://localhost:9200/, nó sẽ cho bạn biết phiên bản Lucene:

{
  "name": "node-1",
  "cluster_name": "my-cluster",
  "cluster_uuid": "8AqSmmKdQgmRVPsVxyxKrw",
  "version": {
    "number": "6.1.2",
    "build_hash": "5b1fea5",
    "build_date": "2018-01-10T02:35:59.208Z",
    "build_snapshot": false,
    "lucene_version": "7.1.0",
    "minimum_wire_compatibility_version": "5.6.0",
    "minimum_index_compatibility_version": "5.0.0"
  },
  "tagline": "You Know, for Search"
}

Biết được cú pháp và các toán tử Lucene sẽ giúp bạn xây dựng các truy vấn. Nó được sử dụng trong cả truy vấn đơn giản và tiêu chuẩn. Dưới đây là một số điều cơ bản:

Toán tử Boolean

Như với hầu hết các ngôn ngữ máy tính, Elasticsearch hỗ trợ toán tử VÀ, HOẶC và KHÔNG:

  • jack AND jill – Sẽ trả về các dữ liệu chứa cả jack và jill
  • ahab NOT moby – Sẽ trả về các dữ liệu có chứa ahab nhưng không chứa moby
  • tom OR jerry – Sẽ trả về các dữ liệu có tom hoặc jerry, hoặc cả hai

Trường

Bạn có thể đang tìm kiếm các dữ liệu trong đó một trường cụ thể chứa các thuật ngữ nhất định. Bạn có thể chỉ định tên trường, nhập dấu hai chấm, sau đó là chuỗi trong dấu ngoặc kép hoặc giá trị không có dấu ngoặc kép. Dưới đây là một số ví dụ về trường trong Lucene:

  • name: “Ned Stark”
  • status: 404

Hãy cẩn thận với các giá trị có khoảng trắng như “Ned Stark”. Bạn sẽ cần đặt nó trong dấu ngoặc kép để đảm bảo rằng toàn bộ giá trị được sử dụng.

Dải giá trị

Bạn có thể tìm kiếm các trường trong một phạm vi cụ thể, sử dụng dấu ngoặc vuông cho các tìm kiếm trong phạm vi bao gồm giá trị đầu và cuối và dấu ngoặc nhọn cho các tìm kiếm trong phạm vi không bao gồm giá trị đầu và cuối:

  • age:[3 TO 10] – Sẽ trả về các sự kiện có độ tuổi từ 3 đến 10
  • price:{100 TO 400} – Sẽ trả lại các sự kiện có giá từ 101 đến 399
  • name:[Adam TO Ziggy] – Sẽ trả về các tên giữa và bao gồm cả Adam và Ziggy

Như bạn có thể thấy trong các ví dụ trên, bạn cũng có thể sử dụng phạm vi trong các trường không phải số như chuỗi và ngày.

Ký tự đại diện

Tìm kiếm sẽ không còn là tìm kiếm nếu không có ký tự đại diện. Bạn có thể sử dụng ký tự * để đại diện cho không hoặc nhiều ký tự bất kỳ hoặc ký tự ? để đại diện cho một ký tự bất kỳ:

  • Ma?s – Sẽ khớp với Mars, Mass và Maps
  • Ma*s – Sẽ khớp với Mars, Mass, Maps, Matches và Massachusetts

Truy vấn Regex

Regexes cung cấp cho bạn nhiều sức mạnh hơn nữa. Chỉ cần đặt regex của bạn giữa các dấu xuyệt (/):

  • /p[ea]n/ – Sẽ khớp với cả pen và pan
  • /<.+>/ – Sẽ khớp văn bản giống với thẻ HTML

Truy vấn tìm kiếm mờ

Tìm kiếm mờ sử dụng khoảng cách Damerau-Levenshtein (Damerau-Levenshtein Distance) để khớp các cụm từ giống nhau về chính tả. Điều này thật tuyệt khi tập dữ liệu của bạn có các từ sai chính tả.

Sử dụng dấu ngã (~) để tìm các thuật ngữ tương tự:

  • blow~

Điều này sẽ trả về các kết quả như “blew”, “brow” và “glow”.

Sử dụng dấu ngã (~) cùng với một số để xác định khoảng cách giữa các từ có thể lớn như thế nào:

  • john~2

Điều này sẽ khớp với những thứ khác: “jean”, “johns”, “jhon” và “horn”.

Không phải là chữ

Nó có thể là âm thanh. Chỉ cần nhập thuật ngữ hoặc giá trị bạn muốn tìm. Đây có thể là một trường, một chuỗi trong một trường, v.v.

Truy vấn thuật ngữ Elasticsearch

Cũng được gọi là truy vấn thuật ngữ, điều này sẽ trả về một kết quả khớp chính xác cho một thuật ngữ nhất định. Lấy ví dụ này từ cơ sở dữ liệu thống kê bóng chày:

POST /mlb_index/_search
{
   "query": {
       "term" : {
           "pitcher_last": "rivera"
           "pitcher_first": "mariano"
           "boost": 1.0 
       }
   },
   "_game" : ["date","innings_pitched","pitch_count","cutters","fastballs"]
}

Đảm bảo rằng bạn đang sử dụng truy vấn thuật ngữ ở đây, KHÔNG phải truy vấn văn bản. Truy vấn thuật ngữ sẽ tìm kiếm kết quả phù hợp chính xác; truy vấn văn bản sẽ tự động lọc dấu chấm câu.

Truy vấn tập thuật ngữ Elasticsearch

Tương tự như truy vấn thuật ngữ, truy vấn tập thuật ngữ có thể tìm kiếm nhiều giá trị dựa trên các điều kiện nhất định được xác định trong yêu cầu PUT. Xem ví dụ về bóng chày để biết thêm:

PUT /pitchers
{
  "mappings": {
    "properties": {
      "pitcher_last": {
        "type": "keyword"
      "pitcher_first": {
        "type": "keyword"
      },
      "pitch_type": {
        "type": "keyword"
      }
    }
  }
}

Tìm kiếm URI

Cách dễ nhất để tìm kiếm cụm Elasticsearch của bạn là thông qua tìm kiếm URI. Bạn có thể chuyển một truy vấn đơn giản đến Elasticsearch bằng cách sử dụng tham số truy vấn q. Truy vấn sau sẽ tìm kiếm toàn bộ cụm của bạn cho các tài liệu có trường name bằng “travis”:

curl "localhost:9200/_search?q=name:travis"

Với cú pháp Lucene, bạn có thể xây dựng các tìm kiếm khá ấn tượng. Thông thường, bạn sẽ phải mã hóa các ký tự URL, chẳng hạn như dấu cách (chúng tôi đã bỏ qua nó trong các ví dụ này để rõ ràng):

curl "localhost:9200/_search?q=name:john~1 AND (age:[30 TO 40} OR surname:K*) AND -city"

Một số tùy chọn có sẵn cho phép bạn tùy chỉnh tìm kiếm URI, cụ thể về việc sử dụng analyzer nào (analyzer), truy vấn chịu lỗi (lenient) và có nên cung cấp giải thích về điểm phù hợp hay không (explain).

Mặc dù tìm kiếm URI là một cách đơn giản và hiệu quả để truy vấn cụm của bạn, nhưng bạn sẽ nhanh chóng nhận thấy rằng nó không hỗ trợ tất cả các tính năng mà Elasticsearch cung cấp.

Toàn bộ sức mạnh của Elasticsearch được thể hiện rõ ràng qua Request Body Search. Sử dụng Request Body Search cho phép bạn xây dựng một yêu cầu tìm kiếm phức tạp bằng cách sử dụng các phần tử và mệnh đề truy vấn khác nhau sẽ khớp, lọc và sắp xếp thứ tự cũng như thao tác các tài liệu tùy thuộc vào nhiều tiêu chí.

Request Body Search sử dụng tài liệu JSON chứa các phần tử khác nhau để tạo tìm kiếm trên cụm Elasticsearch của bạn. Bạn không chỉ có thể chỉ định tiêu chí tìm kiếm, bạn còn có thể chỉ định phạm vi và số lượng tài liệu mà bạn mong đợi, các trường bạn muốn và nhiều tùy chọn khác.

Phần tử đầu tiên của một tìm kiếm là phần tử truy vấn sử dụng truy vấn DSL (Query DSL). Việc sử dụng truy vấn DSL đôi khi có thể gây nhầm lẫn vì DSL có thể được sử dụng để kết hợp và xây dựng các mệnh đề truy vấn thành một truy vấn có thể được lồng sâu vào nhau. Vì hầu hết các tài liệu của Elasticsearch chỉ đề cập đến các mệnh đề một cách riêng biệt, nên rất dễ mất đi nơi đặt các mệnh đề.

Để sử dụng truy vấn DSL, bạn cần có phần tử query trong nội dung tìm kiếm của mình và điền vào nó với một truy vấn được tạo bằng DSL:

{"query": { "match": { "_all": "meaning" } } }

Trong trường hợp này, phần tử query chứa mệnh đề truy vấn match tìm kiếm thuật ngữ “meaning” trong tất cả các trường trong tất cả các tài liệu trong cụm của bạn.

Phần tử query được sử dụng cùng với các phần tử khác trong nội dung tìm kiếm:

{
    "query": {
        "match": { "_all": "meaning" }
    },
    "fields": ["name", "surname", "age"],
    "from": 100, "size": 20
}

Ở đây, chúng tôi đang sử dụng phần tử fields để hạn chế trường nào sẽ được trả về và phần tử fromsize để cho Elasticsearch biết rằng chúng tôi đang tìm tài liệu từ 100 đến 119 (bắt đầu từ 100 và lấy 20 tài liệu).

Truy vấn DSL

Truy vấn DSL (Query DSL) có thể được gọi bằng cách sử dụng hầu hết các API tìm kiếm của Elasticsearch. Để đơn giản, chúng tôi sẽ chỉ xem xét API Tìm kiếm sử dụng endpoint _search.

Khi gọi API tìm kiếm, bạn có thể chỉ định index và / hoặc kiểu mà bạn muốn tìm kiếm. Bạn thậm chí có thể tìm kiếm trên nhiều index và kiểu bằng cách tách tên của chúng bằng dấu phẩy hoặc sử dụng ký tự đại diện để khớp với nhiều index và kiểu:

Tìm kiếm trên tất cả các index Logstash:

curl localhost:9200/logstash-*/_search

Tìm kiếm trong các index hiện tại và kế thừa, trong kiểu tài liệu:

curl localhost:9200/current,legacy/documents/_search

Tìm kiếm trong index khách hàng, trong các kiểu bigcorpsmallco:

curl localhost:9200/clients/bigcorp,smallco/_search

Chúng tôi sẽ sử dụng Request Body Search, vì vậy các tìm kiếm sẽ được gọi như sau:

curl localhost:9200/_search -d '{"query":{"match": {"_all":"meaning"}}}'

Truy vấn kết hợp

Mặc dù có nhiều loại mệnh đề truy vấn, nhưng loại mệnh đề bạn sẽ sử dụng nhiều nhất là Truy vấn kết hợp vì nó được sử dụng để kết hợp nhiều mệnh đề để tạo ra các truy vấn phức tạp.

Truy vấn boolean có lẽ được sử dụng nhiều nhất vì nó có thể kết hợp các tính năng của một số mệnh đề truy vấn phức hợp khác như mệnh đề And, Or, Filter và Not.

Nó được sử dụng nhiều đến mức bốn mệnh đề này đã không còn được dùng trong các phiên bản khác để ủng hộ việc sử dụng truy vấn Bool. Sử dụng nó được giải thích tốt nhất với một ví dụ:

curl localhost:9200/_search -d '{
    "query":{
        "bool": {
            "must": {
                "fuzzy" : {
                    "name": "john",
                    "fuzziness": 2
                }
            },
            "must_not": {
                "match": {
                    "_all": "city"
                }
            },
            "should": [
                {
                    "range": {
                        "age": { "from": 30, "to": 40 }
                    }
                },
                {
                    "wildcard" : { "surname" : "K*" }
                }
            ]
        }
    }
}'

Trong phần tử truy vấn, chúng tôi đã thêm mệnh đề bool chỉ ra rằng đây sẽ là một truy vấn boolean. Có khá nhiều thứ ở đó, vì vậy hãy trình bày theo từng mệnh đề, bắt đầu từ trên cùng:

must

Tất cả các truy vấn trong mệnh đề này phải khớp với một tài liệu để Elasticsearch trả về nó. Hãy coi đây là truy vấn AND của bạn.

Truy vấn mà chúng tôi sử dụng ở đây là truy vấn mờ và nó sẽ khớp với bất kỳ tài liệu nào có trường name khớp với “john”. Tham số “fuzziness” bổ sung cho Elasticsearch biết rằng nó nên sử dụng Khoảng cách Damerau-Levenshtein là 2 – 2 xác định độ mờ.

must_not

Bất kỳ tài liệu nào phù hợp với truy vấn trong mệnh đề này sẽ nằm ngoài tập kết quả. Đây là toán tử NOT hoặc trừ (-) của truy vấn DSL.

Trong trường hợp này, chúng tôi thực hiện một truy vấn khớp đơn giản, tìm kiếm các tài liệu có chứa thuật ngữ “city”. Sử dụng _all làm tên trường chỉ ra rằng thuật ngữ có thể xuất hiện trong bất kỳ trường nào của tài liệu.

Đây là mệnh đề must_not, vì vậy các tài liệu phù hợp sẽ bị loại trừ.

should

Cho đến bây giờ, chúng ta đã làm việc với các giá trị tuyệt đối như: mustmust_not. Should không phải là tuyệt đối và tương đương với toán tử OR.

Elasticsearch sẽ trả về bất kỳ tài liệu nào khớp với một hoặc nhiều truy vấn trong mệnh đề should.

  • Truy vấn đầu tiên mà chúng tôi cung cấp tìm kiếm các tài liệu có trường age từ 30 đến 40.
  • Truy vấn thứ hai thực hiện tìm kiếm theo ký tự đại diện trên trường surname, tìm kiếm các giá trị bắt đầu bằng “K*”.

Truy vấn chứa ba mệnh đề khác nhau, vì vậy Elasticsearch sẽ chỉ trả về tài liệu phù hợp với tiêu chí trong tất cả chúng. Các truy vấn này có thể được lồng vào nhau, vì vậy bạn có thể xây dựng các truy vấn rất phức tạp bằng cách chỉ định truy vấn bool là truy vấn must, must_not, should hoặc truy vấn filter.

filter

Một loại mệnh đề mà chúng ta chưa thảo luận đối với truy vấn kết hợp là mệnh đề filter. Đây là một ví dụ mà chúng tôi sử dụng mệnh đề filter:

curl localhost:9200/_search -d '{
    "query":{
        "bool": {
            "must": {
                { "match_all": {} }
            },
            "filter": {
                "term": {
                    "email": "[email protected]"
                }
            }
        }
    }
}'

Truy vấn match_all trong mệnh đề nói Elasticsearch rằng nó phải trả lại tất cả các tài liệu. Đây có vẻ không phải là một tìm kiếm quá hữu ích, nhưng nó rất hữu ích khi bạn sử dụng nó kết hợp với một bộ lọc như chúng tôi đã làm ở đây.

Bộ lọc mà chúng tôi đã chỉ định là một truy vấn thuật ngữ, yêu cầu tất cả các tài liệu có chứa trường email với giá trị “[email protected]”. Chúng tôi đã sử dụng bộ lọc để chỉ định tài liệu nào chúng tôi muốn, vì vậy tất cả chúng sẽ được trả về với điểm phù hợp là 1.

Bộ lọc không được sử dụng để tính điểm phù hợp, vì vậy truy vấn match_all cho tất cả tài liệu điểm phù hợp là 1.

Một điều cần lưu ý là truy vấn này sẽ không hoạt động như mong đợi nếu trường email được phân tích, đây là mặc định cho các trường trong Elasticsearch.

Lý do đằng sau đây là một chủ đề được thảo luận nhiều nhất trong một bài đăng blog khác, nhưng thực tế là Elasticsearch phân tích cả hai trường và truy vấn khi chúng xuất hiện.

Trong trường hợp này, trường email sẽ được chia thành ba phần: joe, blog và com. Điều này có nghĩa là nó sẽ khớp với các tìm kiếm và tài liệu cho bất kỳ ba cụm từ nào trong số đó.

filter so với query

Những người đã sử dụng Elasticsearch trước phiên bản 2 sẽ quen thuộc với filter và query. Bạn đã từng xây dựng nội dung truy vấn bằng cách sử dụng cả filter và query.

Sự khác biệt giữa chúng là filter thường nhanh hơn vì chúng chỉ kiểm tra xem một tài liệu có khớp hay không chứ không phải tài liệu có khớp tốt hay không.

Nói cách khác, filter đưa ra câu trả lời boolean trong khi query trả về điểm phù hợp được tính toán về mức độ phù hợp của tài liệu với một truy vấn. Các cải tiến hiệu suất khác nhau được liên kết với filter do tính chất đơn giản của chúng.

Kể từ phiên bản 2 của Elasticsearch, filter và query đã hợp nhất và bất kỳ mệnh đề truy vấn nào cũng có thể đóng vai trò là filter hoặc query (tùy thuộc vào ngữ cảnh). Như với phiên bản 1, các filter được lưu vào bộ nhớ đệm và sẽ được sử dụng nếu việc tính điểm phù hợp không thành vấn đề.

Điểm phù hợp

Chúng tôi đã đề cập đến thực tế là Elasticsearch trả về điểm cùng với tất cả các tài liệu phù hợp từ một truy vấn:

curl "localhost:9200/_search?q=application"

Kết quả:

{
    "_shards":{
        "total" : 5,
        "successful" : 5,
        "failed" : 0
    },
    "hits":{
        "total" : 1,
        "max_score": 2.3,
        "hits" : [
            {
                "_index" : "logstash-2016.04.04",
                "_type" : "logs",
                "_id" : "1",
                "_score": 2.3,
                "_source" : {
                    "message" : "Log message from my application"
                }
            }
        ]
    }
}

Điểm này được tính dựa trên các tài liệu trong Elasticsearch dựa trên các truy vấn được cung cấp. Các yếu tố như độ dài của một trường, tần suất xuất hiện của thuật ngữ được chỉ định trong trường và (trong trường hợp tìm kiếm theo ký tự đại diện và tìm kiếm mờ), thuật ngữ khớp với giá trị được chỉ định gần như thế nào đều ảnh hưởng đến điểm số.

Điểm được tính toán sau đó được sử dụng để sắp xếp thứ tự tài liệu, thường từ điểm cao nhất đến thấp nhất, và tài liệu đạt điểm cao nhất sau đó sẽ được trả lại cho client.

Có nhiều cách khác nhau để ảnh hưởng đến điểm số của các truy vấn, chẳng hạn như tham số tăng cường. Điều này đặc biệt hữu ích nếu bạn muốn mọt số truy vấn con trong một truy vấn phức tạp có trọng số lớn hơn các truy vấn khác và bạn đang tìm kiếm các tài liệu quan trọng nhất.

Khi sử dụng truy vấn trong ngữ cảnh bộ lọc (như đã giải thích trước đó), không có điểm phù hợp nào được tính. Điều này cung cấp hiệu suất cao thường được kết hợp với việc sử dụng bộ lọc nhưng không cung cấp các tính năng sắp xếp và ý nghĩa đi kèm với tính điểm.

Phần kết luận

Điều khó nhất về Elasticsearch là độ sâu và bề rộng của các tính năng có sẵn. Chúng tôi đã cố gắng đề cập đến các yếu tố cần thiết càng chi tiết càng tốt mà không làm bạn chìm đắm trong thông tin.

Bạn cũng có thể đọc hướng dẫn Elasticsearch trước đây của tôi để tìm hiểu thêm.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *