banner
Feb 7, 2023
300 Views

Join giữa các index Elasticsearch với nhau

Written by
banner

Mở đầu và cài đặt

Khi làm việc với Elasticsearch, nhược điểm dễ nhận thấy nhất là Elasticsearch không hề có JOIN giữa các index khác hẳn nhau như những hệ thống RDBMS hoặc 1 số hệ thống NoSQL khác.

Tất nhiên theo sách vở thì mọi người khuyên chúng ta nên denormalize trước khi đưa vào elasticsearch, nhưng khi dữ liệu lớn và đặc thù thì việc denormalize sẽ tạo nên rất nhiều bất cập:

  • Khiến dữ liệu phình to ra do lưu các dữ liệu trùng lặp nhau. Ví dụ trong bảng product mà có chứa các objects supplier, brand.... có khi làm dữ liệu phình ra gấp 2-3 lần.
  • Khiến việc update không hiệu quả. Ví dụ bạn có 50 triệu sản phẩm trong 1 index, có 1 object con trong đó phải update khiến cả triệu sản phẩm có cùng object đó phải update thì quả thực là ác mộng.
  • ...

Trong bài này tôi sẽ giới thiệu 1 phương pháp là dùng plugin Siren Federate để thực hiện các tác vụ cần JOIN.

Bước 1: Cài elasticsearch và kibana như hướng dẫn sau.

Bước 2: Bạn phải cài đặt plugin, tôi đã sửa và để sẵn 1 bản plugin tương thích với Elasticsearch 7.17.9 (là bản cuối cùng của elasticsearch 7):

/usr/share/elasticsearch/bin/elasticsearch-plugin install https://blog.haonheo.com/files/siren7.19.9.zip
systemctl restart elasticsearch

Bước 3: Đưa dữ liệu mẫu vào elasticsearch thông qua kibana:

DELETE article
PUT article
PUT article/_mapping
{
"properties": {
"mentions": {
"type": "keyword"
}
}
}
POST article/_doc/
{
"title" : "The NoSQL database glut",
"mentions" : ["1", "2"]
}
POST article/_doc/
{
"title" : "Graph Databases Seen Connecting the Dots",
"mentions" : []
}
POST article/_doc/
{
"title" : "How to determine which NoSQL DBMS best fits your needs",
"mentions" : ["2", "4"]
}
POST article/_doc/
{
"title" : "MapR ships Apache Drill",
"mentions" : ["4"]
}
GET article/_search
{
"query": {
"match_all": {}
}
}
DELETE company
PUT company
PUT company/_mapping
{
"properties": {
"id": {
"type": "keyword"
}
}
}
POST company/_doc/
{ "id": "1", "name" : "Elastic" }
POST company/_doc/
{ "id": "2", "name" : "Orient Technologies" }
POST company/_doc/
{ "id": "3", "name" : "Cloudera" }
POST company/_doc/
{ "id": "4", "name" : "MapR" }
GET company/_search
{
"query": {
"match_all": {}
}
}

Chúng ta sẽ cùng tìm hiểu 2 loại JOIN mà siren federate hỗ trợ gồm:

Semi-join

Dùng để lọc 1 tập document A dựa trên 1 tập documents khác là B. Khi dùng semi-join siren sẽ trả về các documents A thỏa mãn điều kiện join. Việc này tương đương với hàm EXISTS() ở SQL

Ví dụ: chúng ta muốn tìm xem ở các bài báo có sẵn có các bài nào nhắc tới các công ty có chữ technologies ở tên công ty:

GET siren/article/_search
{
"query" : {
"join" : {
"indices" : ["company"],
"on" : ["mentions", "id"],
"request" : {
"query" : {
"match_phrase" : {
"name" : "technologies"
}
}
}
}
}
}
================RESULTS========================
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "article",
"_type" : "_doc",
"_id" : "zpR2IIYBAIFYOcgs_F7i",
"_score" : 1.0,
"_source" : {
"title" : "The NoSQL database glut",
"mentions" : [
"1",
"2"
]
}
},
{
"_index" : "article",
"_type" : "_doc",
"_id" : "0JR3IIYBAIFYOcgsCV7M",
"_score" : 1.0,
"_source" : {
"title" : "How to determine which NoSQL DBMS best fits your needs",
"mentions" : [
"2",
"4"
]
}
}
]
},
"planner" : {
"node" : "1Dg-0HtnQeS-mmzLgadZZw",
"took_in_millis" : 10,
"timestamp" : {
"start_in_millis" : 1675588097293,
"stop_in_millis" : 1675588097303,
"took_in_millis" : 10
},
"is_pruned" : false,
"is_truncated" : false
}
}

Inner-join

Dùng để nối các trường tùy ý của index B vào index A.

Ví dụ: bạn muốn tìm xem các công ty trong index company được nhắc tới trong bài báo ở các năm nào

GET siren/company/_search
{
"query" : {
"join" : {
"indices" : ["article"],
"on" : ["id","mentions"],
"request" : {
"project" : [
{
"field" : {
"name" : "year",
"alias" : "article_year"
}
}
],
"query" : {
"match_all" : {}
}
}
}
},
"script_fields" : {
"article_year" : {
"script" : "doc.article_year"
}
}
}
================RESULTS========================
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "company",
"_type" : "_doc",
"_id" : "8pTeIIYBAIFYOcgsl16K",
"_score" : 1.0,
"fields" : {
"article_year" : [
2022
]
}
},
{
"_index" : "company",
"_type" : "_doc",
"_id" : "85TeIIYBAIFYOcgsl17J",
"_score" : 1.0,
"fields" : {
"article_year" : [
2021,
2022
]
}
},
{
"_index" : "company",
"_type" : "_doc",
"_id" : "9ZTeIIYBAIFYOcgsmF4Z",
"_score" : 1.0,
"fields" : {
"article_year" : [
2021,
2023
]
}
}
]
},
"planner" : {
"node" : "1Dg-0HtnQeS-mmzLgadZZw",
"took_in_millis" : 18,
"timestamp" : {
"start_in_millis" : 1675589037390,
"stop_in_millis" : 1675589037408,
"took_in_millis" : 18
},
"is_pruned" : false,
"is_truncated" : false
}
}

Lưu ý:

  • Siren hiện thời chỉ hỗ trợ join 2 index với nhau.
  • Join số hiệu quả hơn join string. Bạn nên xem xét murmur hash các giá trị string trước khi index.
  • Bạn có thể tìm hiểu các API mà siren hỗ trợ tại đây: https://docs.siren.io/siren-federate-user-guide/29.2/siren-federate/search-apis.html
Article Categories:
dev
banner

Leave a Reply

Your email address will not be published. Required fields are marked *