You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Elasticsearch:如何关联两种类型的数据?

嘿,这个问题我之前帮不少开发者捋清楚过——Elasticsearch和MySQL的思路完全不一样,它是为搜索和分析而生的,没法像关系型数据库那样直接做JOIN关联。不过针对你的需求(单个酒店+对应最便宜航班),有几个实用的方案,你可以根据自己的业务场景来选:

方案1:提前优化数据建模(首推,性能最优)

Elasticsearch的强项是读性能,所以如果能在写入数据的时候就把关联关系处理好,是最省心也最高效的方式。这里分两种建模方式:

嵌套文档(Nested)

如果酒店和对应的航班数据是同步更新的,或者航班数据量不大,直接把航班作为酒店文档的nested字段存储是个好选择。这样查询时能精准关联到当前酒店,还能快速聚合出最便宜的航班。

先定义映射(mapping):

PUT /hotels
{
  "mappings": {
    "properties": {
      "hotel_id": {"type": "keyword"},
      "hotel_name": {"type": "text"},
      "checkin_date": {"type": "date"},
      "stay_duration": {"type": "integer"},
      "flights": {
        "type": "nested",
        "properties": {
          "flight_id": {"type": "keyword"},
          "departure_date": {"type": "date"},
          "flight_duration": {"type": "integer"},
          "price": {"type": "float"}
        }
      }
    }
  }
}

然后用聚合查询,每个酒店只返回自身详情和对应的最便宜航班:

POST /hotels/_search
{
  "size": 0,
  "aggs": {
    "group_by_hotel": {
      "terms": {
        "field": "hotel_id",
        "size": 10
      },
      "aggs": {
        "cheapest_flight": {
          "nested": {
            "path": "flights"
          },
          "aggs": {
            "sort_by_price": {
              "top_hits": {
                "size": 1,
                "sort": [{"flights.price": {"order": "asc"}}],
                "_source": {
                  "includes": ["flights.flight_id", "flights.departure_date", "flights.price"]
                }
              }
            }
          }
        },
        "hotel_details": {
          "top_hits": {
            "size": 1,
            "_source": {
              "includes": ["hotel_id", "hotel_name", "checkin_date", "stay_duration"]
            }
          }
        }
      }
    }
  }
}

父子文档(Parent/Child)

如果酒店和航班是独立更新的(比如航班价格频繁变动,但酒店数据很少修改),用父子关系更合适——父子文档可以单独更新,不用重新索引整个酒店文档。映射时需要指定parent字段,查询时用has_child/has_parent结合聚合取最值,逻辑和嵌套文档类似,但更灵活。

方案2:用EQL做跨索引关联查询

如果数据已经分开存放在两个索引(比如hotelsflights),可以试试Elasticsearch的EQL(Event Query Language),它支持跨索引的事件关联,适合你这种基于日期、时长匹配的场景。

举个简单的查询例子:

POST /_eql/search
{
  "query": """
    FROM hotels
    | WHERE checkin_date = "2024-05-01" AND stay_duration = 3
    | JOIN flights
      ON flights.departure_date = hotels.checkin_date AND flights.flight_duration = hotels.stay_duration
    | STATS min(flights.price) as cheapest_price, latest(flights.*) as flight_details BY hotels.hotel_id, hotels.hotel_name
  """,
  "index": ["hotels", "flights"]
}

不过EQL更偏向时序数据的关联,性能上不如提前建模的方式,适合临时查询或者数据量不大的场景。

方案3:应用层关联(不推荐,仅小数据量可用)

如果以上方案都不适合,也可以先查询出所有符合条件的酒店,再批量查询对应的航班,最后在应用层筛选每个酒店的最便宜航班。但这种方式会有N+1查询的问题,数据量大时性能拉胯,只适合小范围数据的场景。

最后提个小建议:Elasticsearch的核心思路是写时优化优于读时优化,所以优先考虑方案1的建模方式,能最大化发挥ES的性能优势。

内容的提问来源于stack exchange,提问作者Ben Osborne

火山引擎 最新活动