一起学Elasticsearch系列-Nested & Join

本文已收录至Github,推荐阅读 👉 Java随想录

微信公众号:Java随想录

ES的 Nested 类型用于处理在一个文档中嵌套复杂的结构数据,而 Join 类型用于建立父子文档之间的关联关系。

嵌套类型:Nested

Elasticsearch没有内部对象的概念,因此,ES在存储复杂类型的时候会把对象的复杂层次结果扁平化为一个键值对列表。

比如

json 复制代码
PUT my-index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

上面的文档被创建之后,user数组中的每个json对象会以下面的形式存储

json 复制代码
{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

user.firstuser.last字段被扁平化为多值字段,firstlast之间的关联丢失。

解决方法可以使用Nested类型,Nested属于object类型的一种,是Elasticsearch中用于复杂类型对象数组的索引操作,嵌套类型(Nested)允许在一个文档内部嵌套另一个文档,这使得可以在同一个文档中表示复杂的层次结构数据。

下面是关于如何定义和使用嵌套类型的示例:

定义映射(Mapping):

json 复制代码
PUT /my_index
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "comments": {
        "type": "nested",
        "properties": {
          "user": { "type": "keyword" },
          "message": { "type": "text" }
        }
      }
    }
  }
}

在上述示例中,我们创建了一个名为 "my_index" 的索引,并定义了一个 "comments" 字段作为嵌套类型。嵌套类型包含两个属性: "user" 和 "message"。

输入数据(Indexing):

json 复制代码
POST /my_index/_doc
{
  "name": "Product A",
  "comments": [
    {
      "user": "User 1",
      "message": "Great product!"
    },
    {
      "user": "User 2",
      "message": "Needs improvement."
    }
  ]
}

在上述示例中,我们向索引 "my_index" 中插入了一个文档,其中 "comments" 字段包含了两个嵌套文档。

查询数据(Querying):

json 复制代码
GET /my_index/_search
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "bool": {
          "must": [
            { "match": { "comments.user": "User 1" } },
            { "match": { "comments.message": "Great product!" } }
          ]
        }
      }
    }
  }
}

在上述示例中,我们使用嵌套查询(nested query)来搜索包含特定评论的文档。我们指定了路径为 "comments",并在 must 子句中添加了匹配条件。

输出结果:

json 复制代码
{
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "hits": [
      {
        "_source": {
          "name": "Product A",
          "comments": [
            {
              "user": "User 1",
              "message": "Great product!"
            }
          ]
        }
      }
    ]
  }
}

在上述示例中,我们得到了一个匹配的文档,其中 "comments" 字段只包含了符合查询条件的嵌套文档。

参数

  • path(必需):指定嵌套字段的路径。它告诉 Elasticsearch 在哪个字段上应用嵌套查询。

  • score_mode(可选):指定如何计算嵌套文档的评分。

    avg (默认):使用所有匹配的子对象的平均相关性得分。

    max:使用所有匹配的子对象中的最高相关性得分。

    min:使用所有匹配的子对象中最低的相关性得分。

    none:不要使用匹配的子对象的相关性分数。该查询为父文档分配得分为0。

    sum:将所有匹配的子对象的相关性得分相加。

  • inner_hits(可选):允许获取与嵌套文档匹配的内部结果。使用此参数可以检索与查询匹配的特定嵌套文档,并返回有关它们的信息。

  • ignore_unmapped(可选):如果设置为 true,则忽略没有嵌套字段映射的文档,并将其视为无匹配。默认情况下,设为 false

  • nested(可选):表示查询是否应该应用于嵌套字段的上下文。默认情况下,设为 true。如果设置为 false,则将查询视为普通的非嵌套查询。

  • score_mode(可选):指定如何计算嵌套文档的评分。可选的值包括 "none""avg""max""sum""min"。默认情况下,使用 "avg"

父子级关系:Join

连接数据类型是一个特殊字段,它在同一索引的文档中创建父/子关系。关系部分在文档中定义了一组可能的关系,每个关系是一个父名和一个子名。

父/子关系可以定义如下:

json 复制代码
PUT <index_name>
{
  "mappings": {
    "properties": {
      "<join_field_name>": { 
        "type": "join",
        "relations": {
          "<parent_name>": "<child_name>" 
        }
      }
    }
  }
}

常见的一个示例是创建一个索引来存储博客的数据。每个博客可以有多个评论,我们可以使用Join类型来建立博客和评论之间的父子关系。

首先,我们定义一个包含两个类型的索引:blogscommentsblogs类型表示博客,而comments类型表示评论。我们将为blogs类型定义一个Join字段,用于与comments类型建立关联。

以下是一个简化的示例:

创建索引并定义映射:

json 复制代码
PUT my_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "join_field": {
        "type": "join",
        "relations": {
          "blogs": "comments" 
        }
      }
    }
  }
}

添加博客文档:

json 复制代码
PUT my_index/_doc/1
{
  "title": "Elasticsearch Join 示例",
  "join_field": "blogs"
}

添加评论文档,并关联到博客:

json 复制代码
PUT my_index/_doc/2?routing=1
{
  "title": "很棒的博客",
  "join_field": {
    "name": "comments",
    "parent": "1"
  }
}

查询博客及其关联的评论:

json 复制代码
GET my_index/_search
{
  "query": {
    "has_child": {
      "type": "comments",
      "query": {
        "match_all": {}
      }
    }
  }
}

以上示例展示了如何使用Join类型在Elasticsearch中建立父子关系,并进行查询操作。实际使用时,可能需要根据自己的数据结构和查询需求进行适当的调整。

使用场景

Join唯一合适应用场景是:当索引数据包含一对多的关系,并且其中一个实体的数量远远超过另一个的时候。比如:老师有 一万个学生

join类型不能像关系数据库中的表链接那样去用,不论是 has_child或者是 has_parent查询都会对索引的查询性能有严重的负面影响。并且会触发 global ordinals。

Global Ordinals是一种用于优化字段的查询性能的技术。在使用Join类型时,如果启用了Global Ordinals特性,它将为Join字段创建全局有序的编号,以支持快速的父子文档查询。

当你执行具有Join字段的查询时,ES会使用Global Ordinals来识别匹配的父文档,并快速定位到对应的子文档。这样可以避免对所有文档进行扫描和过滤的开销,提高查询的效率。

需要注意的是,启用Global Ordinals可能会增加索引的内存使用量和一些额外的计算开销。因此,在决定是否启用Global Ordinals时,需要权衡查询性能和资源消耗之间的平衡。

注意

  • 在索引父子级关系数据的时候必须传入routing参数,即指定把数据存入哪个分片,因为父文档和子文档必须在同一个分片上,因此,在获取、删除或更新子文档时需要提供相同的路由值。
  • 每个索引只允许有一个 join类型的字段映射。
  • 一个元素可以有多个子元素但只有一个父元素。
  • 可以向现有连接字段添加新关系。
  • 也可以向现有元素添加子元素,但前提是该元素已经是父元素。

参数

当使用Elasticsearch的Join类型进行查询时,以下是一些常用的参数和选项:

  • has_parenthas_child:这两个查询参数用于在父子文档之间执行查询。您可以指定要匹配的父文档或子文档的类型以及具体的查询条件。
  • parent_id:用于指定要查询的子文档的父文档ID。通过指定parent_id参数,您可以快速检索与特定父文档相关联的所有子文档。
  • inner_hits:内部命中参数允许您在查询结果中获取与父文档或子文档匹配的内部命中结果。您可以使用inner_hits来检索与查询条件匹配的子文档或匹配的父文档及其关联的子文档。
  • ignore_unmapped:当设置为true时,如果查询字段不存在映射或没有任何匹配的文档时,将忽略该查询并返回空结果。
  • max_children:可用于限制每个父文档返回的子文档数量。

这些只是一些常见的参数和选项,根据你的实际需求,还可以使用其他参数来进一步细化查询。请参考Elasticsearch官方文档以获取更详细的参数和用法信息。

相关推荐
A1330381453613 分钟前
电商店群模式如何利用云分账实现自动化资金管理
大数据
java_heartLake14 分钟前
微服务中间件之Nacos
后端·中间件·nacos·架构
GoFly开发者1 小时前
GoFly快速开发框架/Go语言封装的图像相似性比较插件使用说明
开发语言·后端·golang
苹果酱05671 小时前
通过springcloud gateway优雅的进行springcloud oauth2认证和权限控制
java·开发语言·spring boot·后端·中间件
CS数模2 小时前
2024 “华为杯” 中国研究生数学建模竞赛(D题)深度剖析|大数据驱动的地理综合问题|数学建模完整代码+建模过程全解全析
大数据·数学建模·华为
豌豆花下猫2 小时前
Python 潮流周刊#70:微软 Excel 中的 Python 正式发布!(摘要)
后端·python·ai
陈吉俊2 小时前
实时流处理框架(如Flink、Spark Streaming)
大数据
@听风吟2 小时前
力扣之182.查找重复的电子邮箱
大数据·javascript·数据库·sql·leetcode
Elastic 中国社区官方博客2 小时前
Elasticsearch:检索增强生成背后的重要思想
大数据·人工智能·elasticsearch·搜索引擎·全文检索
嵌入式冰箱2 小时前
2024年中国研究生数学建模竞赛D题“大数据驱动的地理综合问题”全析全解
大数据·数学建模