Elasticsearch được biết đến là search engine đang đứng đầu trong ngành công nghệ tại thời điểm hiện nay, vì nhiều tính năng ưu việt và hiệu năng tìm kiếm tốt của nó mà hiện tại đang được nhiều công ty công nghệ lớn sử dụng. 

Đúng vậy, việc tìm kiếm dữ liệu trong Elasticsearch chính là use case quan trọng nhất mà công cụ này mang lại cho chúng ta. Trong bài viết này, Stringee và các bạn sẽ cùng nhau tìm hiểu các phương thức để tìm kiếm dữ liệu ở mức độ cơ bản với Elasticsearch.

>>> Xem thêm bài viết trong chuỗi bài hướng dẫn tìm hiểu ngôn ngữ lập trình Elasticsearch:

1. Cú pháp tìm kiếm cơ bản

GET /<target>/_search

GET /_search

POST /<target>/_search

POST /_search

Các truy vấn này cho phép chúng ta có thể thực hiện một truy vấn tìm kiếm và nhận về các kết quả đúng với truy vấn đó. Ở đây, ta cần cung cấp câu truy vấn để thực hiện tìm kiếm bằng cách sử dụng câu truy vấn chuỗi qua param q hoặc sử dụng request body.

Trong cú pháp trên, ta thấy có param <target>. Giá trị này là một list các data stream, index hay các alias để chúng ta có thể tìm kiếm và được phân cách với nhau bằng dấu “,” và cho phép chúng ta có thể tìm kiếm với dạng wildcard(*). Để thực hiện tìm kiếm trên toàn bộ các luồng dữ liệu và index, ta có thể bỏ đi giá trị này hoặc sử dụng * hoặc _all.

Để tìm kiếm dữ liệu, chúng ta có thể sử dụng một trong hai cách sau:

  • Sử dụng param q: nhận vào giá trị là một query được viết theo cú pháp tìm kiếm Lucene.
  • Sử dụng request body và điền query: nhận vào một object là DSL query. Đây là kiểu truy vấn được định nghĩa dùng cho Elasticsearch.

2. Truy vấn đơn giản đến Elasticsearch

2.1. Phân tích câu truy vấn tìm kiếm và kết quả tìm kiếm được từ Elasticsearch

Như chúng ta đã tìm hiểu ở phần trên, để tìm kiếm dữ liệu, chúng ta sẽ phải gọi đến API search của Elasticsearch và có thể dùng một trong hai cách đó là sử dụng param hoặc truyền vào body một truy vấn DSL.

Ta sẽ cùng nhau tìm hiểu hai câu lệnh tìm kiếm sau:

  • Tìm kiếm sử dụng param

GET index-01/_search?q=*

  • Tìm kiếm sử dụng body là DSL
GET index-01/_search
{
  "query": {
    "match_all": {}
  }
}

Hai câu lệnh này sẽ cùng cho chúng ta kết quả như 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"
        }
      }
    ]
  }
}

Trong phần dữ liệu trả về, bạn cần chú ý những thông tin sau:

  • took: thời gian hoàn thành truy vấn (ms)
  • timed_out: cho biết thời gian thực hiện truy vấn có vượt quá và bị dừng hay không
  • _shards: số shard mà Elasticsearch đã tìm kiếm 
  • hits: chứa kết quả tìm được
  • total: thông tin về số kết quả tìm được, hits: mảng dữ liệu tìm kiếm được (mặc định nếu không chỉ ra số lượng thì nó sẽ trả về tối đa là 10)

2.2. Làm nhiều điều hơn nữa với kết quả tìm kiếm được

  • Thêm số document mình muốn kết quả search trả về: sử dụng attribute size trong body hoặc param size trong truy vấn dùng parameter q.
GET index-01/_search
{
  "query": {
    "match_all": {}
  },
  "size": 1
}
  • Tùy chọn trường trả về: ta có thể tùy chọn các trường trả về trong truy vấn bằng cách điền thông tin các trường mong muốn vào attribute _source dưới dạng là một dãy.
GET index-01/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["_id", "name"]
}

Kết quả thu được sẽ là:

{
  "took": 303,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "index-01",
        "_id": "i5IEpYsB-XeRF7h4FTkP",
        "_score": 1,
        "_source": {
          "name": "John"
        }
      },
      {
        "_index": "index-01",
        "_id": "xQzGpYsBi-CQRnwWpDy3",
        "_score": 1,
        "_source": {
          "name": "Hi Doe"
        }
      }
    ]
  }
}
  • Sắp xếp kết quả trả về: sử dụng thuộc tính sort để thực hiện. Các bạn có thể xem ví dụ dưới đây:
GET index-01/_search
{
  "query": {
    "match_all": {}
  },
  "sort": {
    "age": {
      "order": "asc"
    }
  }
}

Truy vấn này sẽ trả về kết quả sau:

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

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

Tìm hiểu về ràng buộc (Constraint) trong SQL

Tìm hiểu về cơ sở dữ liệu phi quan hệ MongoDB

Sử dụng map reduce trong MongoDB

3. Một số kiểu truy vấn đơn giản

Tới đây thì chúng ta cũng đã đi gần hết các kiến thức mà Stringee muốn đem tới cho các bạn trong tuần này. Ở các phần trước, chúng ta đã cùng nhau tìm hiểu về cách sử dụng một số câu truy vấn và tùy chỉnh lại chúng làm sao cho phù hợp với cách sử dụng của mình.

Elasticsearch cung cấp rất nhiều các hình thức tìm kiếm khác nhau, trong khuôn khổ bài viết này, chúng ta sẽ tìm hiểu một vài cách tìm kiếm nổi bật dưới đây. Bật mí một chút cho các độc giả của chúng tôi, trong kỳ tới chúng ta sẽ cùng tìm hiểu về DSL, từ đó các bạn có thể phần nào nắm được cách tìm kiếm dữ liệu của mình phục thuộc vào từng nghiệp vụ của ứng dụng.

  • Tìm kiếm cụm từ có chứa một trong các từ thuộc từ khóa tìm kiếm

Để giải quyết bài toán này, chúng ta sẽ dùng câu truy vấn match. Ví dụ nếu chúng ta muốn tìm kiếm một document có trường name có chứa một trong hai từ “John” hoặc “Doe”, ta sẽ có truy vấn sau:

GET index-01/_search
{
  "query": {
    "match": {
      "name": "John Doe"
    }
  }
}

Kết quả tìm kiếm là:

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.8754687,
    "hits": [
      {
        "_index": "index-01",
        "_id": "i5IEpYsB-XeRF7h4FTkP",
        "_score": 0.8754687,
        "_source": {
          "age": 18,
          "email": "john.doe@gmail.com",
          "name": "John Doe"
        }
      },
      {
        "_index": "index-01",
        "_id": "xQzGpYsBi-CQRnwWpDy3",
        "_score": 0.18232156,
        "_source": {
          "age": 19,
          "email": "hi.doe@gmail.com",
          "name": "Hi Doe"
        }
      }
    ]
  }
}
  • Tìm kiếm cụm từ có cả cụm từ khóa tìm kiếm

Chúng ta sẽ sử dụng match_phrase để thực hiện quá trình tìm kiếm này. Giả sử chúng ta tìm một document có name chứa “John Doe”, câu truy vấn sẽ là:

GET index-01/_search
{
  "query": {
    "match_phrase": {
      "name": "John Doe"
    }
  }
}

Kết quả thu được là:

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.87546873,
    "hits": [
      {
        "_index": "index-01",
        "_id": "i5IEpYsB-XeRF7h4FTkP",
        "_score": 0.87546873,
        "_source": {
          "age": 18,
          "email": "john.doe@gmail.com",
          "name": "John Doe"
        }
      }
    ]
  }
}
  • Tìm kiếm với điều kiện logic

Tất nhiên là với chỉ các truy vấn về 1 trường dữ liệu là không đủ để phục vụ cho nhu cầu sử dụng của các ứng dụng. Với Elasticsearch, chúng ta có thể sử dụng truy vấn bool, cho phép chúng ta có thể định nghĩa các node tìm kiếm must (và) hay should (hoặc).

Nếu bạn muốn tìm kiếm nhiều điều kiện cùng xảy ra, chúng ta sẽ dùng must. Nếu chỉ cần một trong các điều kiện đúng, chúng ta sẽ sử dụng should.

Dưới đây là một ví dụ tìm kiếm document có “age” là 18 và name là “John Doe”

GET index-01/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase": {
            "name": "John Doe"
          }
        },
        {
          "match": {
            "age": "18"
          }
        }
      ]
    }
  }
}

Kết

Tìm kiếm dữ liệu trên Elasticsearch là một chủ đề được rất nhiều lập trình viên quan tâm. Cũng phải khi mà bản thân Elasticsearch vốn là một search engine nên việc tìm kiếm dữ liệu trên nó cũng là một điều mà bạn nhất thiết phải nắm vững được khi sử dụng công cụ này cho các hệ thống của mì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: