对象数组的JSON字段的Where子句

时间:2017-09-26 23:31:26

标签: mysql json

使用MySQL 5.7.19,我发誓一小时前这有效,但现在我的查询没有返回任何内容

CREATE TABLE FlattenedData.blog_posts
(
  post_id CHAR(13) NOT NULL PRIMARY KEY UNIQUE,
  post_data JSON,
  date_published DATETIME NOT NULL, # for primary indexing
  date_added DATETIME DEFAULT CURRENT_TIMESTAMP,
  date_updated DATETIME DEFAULT CURRENT_TIMESTAMP on UPDATE CURRENT_TIMESTAMP,
  post_categories VARCHAR(255) GENERATED ALWAYS AS (post_data->>"$.categories[*].slug"),
  post_tags VARCHAR(512) GENERATED ALWAYS AS (post_data->>"$.tags[*].slug"),
  KEY idx_date_published (date_published),
  INDEX idx_categories (post_categories),
  INDEX idx_tags (post_tags),
  INDEX idx_categories_tags (post_categories, post_tags)
);

作为注释:帖子可以有多个类别,就像标签一样

这是我的查询

select
  *
from blog_posts
WHERE
  post_data->>"$.categories[*].slug" = "site-news"

就像我说的那样,我发誓这是在早些时候工作,但现在我什么都没有回来。

以下是解释:

enter image description here

即使我删除了索引和生成的列,只使用普通的json字段,我仍然突然得不到任何东西。我可以使用的唯一结果是JSON_SEARCH,但是有成千上万的记录,这些是相当大的json blob,并且搜索到的文本可能会出现在正文中

顺便说一句,类别字段看起来像这样

[{slug: "site-news", title: "Site News"}, {slug: "personal", title: "Personal"}]

标签遵循完全相同的结构

修改

我刚试过post_data->>"$.categories[0].slug" = "site-news"并带来了记录。但是我需要where子句来考虑数组的所有元素,因为我不能保证这个类别的数组元素插槽。

正如MySQL的文档所述:[*] represents the values of all cells in the array. https://dev.mysql.com/doc/refman/5.7/en/json-path-syntax.html

使用

的核心问题
select
  *
from blog_posts
WHERE
  JSON_CONTAINS(post_data->"$.categories[*].slug", json_quote("site-news"))

是完全避免使用我的索引,这将是关键

enter image description here

1 个答案:

答案 0 :(得分:1)

带有通配符的路径表达式返回一个值数组,作为JSON表示法中的数组。

SELECT post_data->>'$[*].slug' FROM blog_posts;
+---------------------------+
| post_data->>'$[*].slug'   |
+---------------------------+
| ["site-news", "personal"] |
+---------------------------+

这显然不等于标量字符串' site-news'。

因此,您可以在JSON数组上使用JSON_SEARCH()来查找特定字符串:

SELECT * FROM blog_posts 
WHERE JSON_SEARCH(post_data->>'$[*].slug', 'one', 'site-news') IS NOT NULL;

我用MySQL 8.0.3-rc测试了它。我加载了这些数据:

INSERT INTO blog_posts (post_id, date_published, post_data)
VALUES('blah blah', now(), '[{"slug": "site-news", "title": "Site News"}, {"slug": "personal", "title": "Personal"}]');

我知道这不是post_data的格式,但它仍然表明在JSON上使用通配符路径会返回一个数组。