【问题标题】:Query nested array performance, Mongo vs ElasticSearch查询嵌套数组性能,Mongo vs ElasticSearch
【发布时间】:2021-05-06 02:42:52
【问题描述】:

我有一个music app,负责根据标签 ID 查找音乐推荐。

涉及两个实体:

  • Song - 歌曲记录包含其名称和该歌曲所属的音乐标签 ID(流派)列表
  • MusicTag - 音乐标签本身,包括 id、name 等。

数据当前存储在 MongoDB 中。

mongo 中的Songs 集合有数百万首歌曲,每首歌曲平均有7 个标签id。 MusicTags 有大约 30K 条记录。

Songs 集合如下所示:

[
  {
    name: "Metallica - one",
    tags: [
      "6018703624d8a5e8efa1b76e", // Rock
      "601861cc8cef62ba86765017", // Heavy metal
      "5fda07ac8db0615c1c503a46" // Hard Rock
    ]
  },
  {
    name: "Metallica - unforgiven",
    tags: [
      "6018703624d8a5e8efa1b76e", // Rock
      "5fda07ac8db0615c1c503a46", // Metal
    ]
  },
  {
    name: "Lady Gaga - Bad Romance",
    tags: [
      "5fc7b9f95e38e17282896b64", // Pop
      "5fc729be5e38e17282844eff", // Dance
    ]
  }
]

给定标签"6018703624d8a5e8efa1b76e" (Rock),我想查询Songs 集合并在tags 数组中找到所有带有Rock 标签的歌曲。

在 Mongo 中,这是我正在执行的查询:

db.songs.find({ tags: { $in: [ObjectId("6018703624d8a5e8efa1b76e")] }});

它的性能非常糟糕(在 10 到 40 秒之间,只要集合增长就会变得最差),我尝试以各种方式对 Mongo 进行索引(该表包含更多涉及搜索的数据,例如分数和持续时间,但现在不相关)但我的查询仍然需要太长时间,我无法解释(我阅读了很多官方和非官方的东西)但我觉得以这种嵌套形式保存数据使索引毫无价值,并且每次仍然以某种方式对表进行全面扫描 - 但我无法证明这一点(Mongo“解释”并没有真正向我解释一些事情:))

我正在考虑使用 ElasticSearch,同步所有歌曲数据,并查询它而不是 Mongo,它将作为数据 SSOT 和其他轻量级操作。

但是问题仍然悬而未决,我想确保:在 Elastic 中,我可以以这种形式保存数据(歌曲中的嵌套数组)或者我需要以不同的方式表示它(例如,将其扁平化,以便每条记录都是 song_tag索引等?

谢谢。

【问题讨论】:

  • 您好,您可以将解释输出添加到问题中吗?

标签: mongodb elasticsearch query-optimization database-performance


【解决方案1】:

Elasticsearch 不提供dedicated array type,因此您通常会根据各个数组项的类型定义映射——在您的情况下为keyword

PUT songs
{
  "mappings": {
    "properties": {
      "tags": {
        "type": "keyword"
      }
    }
  }
}

然后您将索引文档:

POST songs/_doc
{
  "name": "Metallica - one",
  "tags": [
    "6018703624d8a5e8efa1b76e",
    "601861cc8cef62ba86765017",
    "5fda07ac8db0615c1c503a46"
  ]
}

并查询tags:

POST songs/_search
{
  "query": {
    "bool": {
      "must": [
        { ... other queries },
        {
          "terms": {
            "tags": [
              "6018703624d8a5e8efa1b76e"     // one or more
            ]
          }
        }
      ]
    }
  }
}

标签是唯一的关键字,但不是人类可读的,因此您需要将它们与实际类型的地图保存在某个地方。由于流派可能设置一次并且很少更新,如果有的话,您也可以使用nested fields。但是你的标签会变成一个键值对数组:

POST songs/_doc
{
  "name": "Metallica - one",
  "tags": [
    {
      "tag": "6018703624d8a5e8efa1b76e",
      "genre": "Rock"
    }
    ...
  ]
}

映射会略有不同,查询也会有所不同,但现在您不需要翻译映射,而且您可以通过人类可读的值进行查询或聚合 -- tags.genre

【讨论】:

  • 知道了,谢谢,我试试第一种方法,看看效果如何
  • 很好,告诉我它是怎么做的。
  • 好!嘿,顺便说一句——我的 Elasticsearch 手册即将出版,因为您来自 MongoDB 背景,我认为它会给您带来价值。做let me know what you'd like to learn about,我会告诉你什么时候出来!
猜你喜欢
  • 1970-01-01
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多