简体   繁体   English

当将JSONB /哈希与数组一起使用时,ActiveRecord构造不正确的SQL

[英]ActiveRecord constructs incorrect SQL when using JSONB/Hash with Array

I am using jsonb datatype for a column named data . 我正在将jsonb数据类型用于名为data的列。 When I query using a simple hash, it works correctly: 当我使用简单的哈希查询时,它可以正常工作:

[1] pry(PredictionService)> Prediction.where(data: {"innings_no" => 1})

  Prediction Load (1.2ms)  SELECT "predictions".* FROM "predictions" WHERE "data"."innings_no" = 1
=> #<Prediction::ActiveRecord_Relation:0x3fcb34634e78>

It fails with an incorrect SQL when I use an array like this: 当我使用如下数组时,它将失败并显示错误的SQL:

[2] pry(PredictionService)> Prediction.where(data: {"innings_no" => [1,2]})

ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  relation "data" does not exist
LINE 5:                WHERE a.attrelid = '"data"'::regclass
                                          ^
:               SELECT a.attname, format_type(a.atttypid, a.atttypmod),
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
               WHERE a.attrelid = '"data"'::regclass
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum
from /Users/lenin.rajasekaran/.rvm/gems/ruby-2.3.1@duggout-app/gems/activerecord-4.2.5/lib/active_record/connection_adapters/postgresql_adapter.rb:592:in `async_exec'

Is this a known issue with jsonb/ActiveRecord or this can be fixed? 这是jsonb / ActiveRecord的已知问题还是可以解决的?

I cannot use Postgres's array functions to access a particular key, as the keys are dynamic and I am using this query to find existing records before creating a new one. 我不能使用Postgres的数组函数来访问特定的键,因为键是动态的,并且在创建新记录之前,我使用此查询来查找现有记录。

So you have a jsonb column called data that contains things like 因此,您有一个名为datajsonb列,其中包含类似

{ "innings_no": 6, ... }

To work with that column in a query, you have to use the PostgreSQL JSON functions and operators . 要在查询中使用该列,您必须使用PostgreSQL JSON函数和运算符 In this case, you probably want ->> , which extracts a field as a text value, and a type cast. 在这种情况下,您可能需要->> ,它将字段提取为text值并进行类型转换。 So something like: 所以像这样:

Prediction.where("(data ->> 'innings_no')::int = ?", 1)

The data ->> 'innings_no' is more or less equivalent to data['innings_no'] in Ruby (or JavaScript for that matter), the ::int casts the string that ->> yields to an integer. data ->> 'innings_no'或多或少与Ruby(或JavaScript)中的data['innings_no']等效, ::int->>产生的字符串转换为整数。

Of course, using = inside a little SQL snippet means that you're responsible for modifying the query to account for an array: 当然,在少量SQL代码段中使用=意味着您负责修改查询以说明数组:

Prediction.where("(data ->> 'innings_no')::int = any(array[?])", [1,2])

or: 要么:

Prediction.where("(data ->> 'innings_no')::int in (?)", [1,2])

Luckily the = any and in versions will work in either case so you can do this and no worry about 幸运的是, = any情况下的= anyin版本都可以使用,因此您可以这样做而不必担心

innings = 1
Prediction.where("(data ->> 'innings_no')::int = any(array[?])", innings)

innings = [1,2]
Prediction.where("(data ->> 'innings_no')::int = any(array[?])", innings)

If, on the other hand, you have a Ruby Hash has you want to find models whose data overlaps that Hash then you can use the @> operator: 另一方面,如果您有一个Ruby Hash,想要查找其data与Hash重叠的模型,则可以使用@>运算符:

@> jsonb
Does the left JSON value contain the right JSON path/value entries at the top level? 左侧的JSON值是否在顶层包含正确的JSON路径/值条目?
'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb

and a to_json call to build the right hand side. to_json调用以构建右侧。 For example: 例如:

hash = { :innings_no => 1, :pancakes => 11 }
Prediction.where('data @> ?', hash.to_json)

PostgreSQL will automatically cast the ? PostgreSQL是否会自动强制转换? string to JSONB for you but you could be explicit and say: 为您提供JSONB字符串,但您可以明确地说:

Prediction.where('data @> ?::jsonb', hash.to_json)
# -------------------------^^^^^^^

Your first query: 您的第一个查询:

Prediction.where(data: {"innings_no" => 1})

doesn't work because ActiveRecord reserves that argument structure for referencing JOINed tables as you can see in the SQL: 不起作用,因为ActiveRecord保留了该参数结构用于引用JOINed表,如您在SQL中所看到的:

WHERE "data"."innings_no" = 1
table -^^^^
column -------^^^^^^^^^^

so when that ends up in the database, PostgreSQL will be looking for a table called data but won't find one. 因此,当它最终出现在数据库中时,PostgreSQL将寻找一个名为data的表,但找不到该表。 Of course, this won't fail until it hits that database so it looked fine in pry . 当然,这只有在命中该数据库后才会失败,因此在pry看起来还不错。

Your second query: 您的第二个查询:

Prediction.where(data: {"innings_no" => [1,2]})

fails for the same reason but it fails earlier because, for some reason, the [1,2] array makes ActiveRecord want to know the structure of the data table while it is trying to build the query; 出于相同的原因而失败,但是由于某些原因,它由于更早的原因而失败,因为[1,2]数组使ActiveRecord在尝试构建查询时希望知道data表的结构; the odd looking query on the pg_attribute and pg_attrdef system tables is what ActiveRecord uses to figure out the structure of a table so any time you see a query like that it is ActiveRecord trying to figure out the columns structure of a table. ActiveRecord用于确定pg_attributepg_attrdef系统表的查询看起来很奇怪,因此,每当您看到这样的查询时,ActiveRecord都会试图找出该表的列结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM