我的用户模型有 jsonb 列,其名称为
raw
。看起来像这样:
{"client"=>{"tokens"=>["asdasd"]}}
现在我想通过
raw["client"]["tokens"]
中的令牌查找用户。我怎样才能做到这一点?
我通常首先在 SQL 控制台中制作此类查询,然后将其转换为 ActiveRecord。
您可以在 Postgres 中导航哈希键。查询
SELECT
raw #> '{client,tokens}'
FROM users
将仅返回该路径中的
tokens
数组。现在我们需要检查它是否包含我们正在寻找的值。查询
SELECT
raw #> '{client,tokens}' ? 'asdasd'
FROM users
将为具有匹配标记的行选择
t
。现在您可以将其移至 WHERE
部分:
SELECT
*
FROM users
WHERE raw #> '{client,tokens}' ? 'asdasd'
如果这选择了您所期望的,那么您可以将其转换为 AR:
User.where("config #> '{client, tokens}' ? :token", token: 'asdasd')
请注意,我不能使用
?
进行参数替换,而应使用 :token
代替。另请注意,这仅适用于 JSONB(Postgres 9.4+ https://www.postgresql.org/docs/9.4/static/functions-json.html#FUNCTIONS-JSONB-OP-TABLE)
更新:
你应该(tm)(我还没有测试过这一点)相处:
CREATE INDEX index_tokens ON users USING GIN ((raw #> '{client, tokens}'));
请参阅https://www.postgresql.org/docs/9.6/static/datatype-json.html#JSON-INDEXING了解更多详细信息
更新2:
另一种查询方式是:
raw @> '{"client": {"tokens": ["asdasd"]}}'
它应该能够在原始列上使用简单的
GIN
索引(它比上面描述的表达式索引使用更多的空间)。
CREATE INDEX index_user_raw ON users USING GIN (raw)
再次:详细信息请参阅上面的 JSON INDEXING 链接。并使用查询可视化工具来查看差异。我喜欢http://tatiyants.com/pev
测试您的左侧值是否包含右侧值。 “包含”适用于对象和数组,因此以下查询将有效:
User.where("((raw #>> '{}')::json->>'client')::json->>'tokens' @> ?", ["asdasd"].to_json)