【问题标题】:How to write a migration to convert JSON field to Postgres Array for querying in rails?如何编写迁移以将 JSON 字段转换为 Postgres 数组以在 Rails 中查询?
【发布时间】:2021-01-25 15:02:28
【问题描述】:

有一个列类型为 JSON 的旧表,但此列中仅存储数组。 即使我正在存储数组,我也无法使用 ANY 关键字查询该字段(这将适用于 Postgres 中的数组类型列,如 post

例如:假设 ['Apple', 'Orange', 'Banana'] 在 fruits 列中存储为 Json,我想像 Market.where(":name = ANY(fruits)", name: "Orange") 一样查询并获取所有有橙子的市场。

谁能帮我写一个迁移来将现有的列(类型:Json)更改为数组类型?

【问题讨论】:

  • 为什么不使用JSON 运算符/函数?在任何情况下,您都需要展示一个存储内容的示例,以获得任何有用的转换帮助。
  • @AdrianKlaver 我在描述中给出了一个例子。看看有没有帮助
  • 这有帮助。还有一个问题,数据类型是json还是jsonb
  • 列类型为json

标签: ruby-on-rails postgresql rails-migrations


【解决方案1】:

假设json 字段的示例:

\d json_test 
               Table "public.json_test"
  Column   |  Type   | Collation | Nullable | Default 
-----------+---------+-----------+----------+---------
 id        | integer |           |          | 
 fld_json  | json    |           |          | 
 fld_jsonb | jsonb   |           |          | 
 fruits    | json    |           |          | 

insert into json_test (id, fruits) values (1, '["Apple", "Orange", "Banana"] ');
insert into json_test (id, fruits) values (2, '["Pear", "Orange", "Banana"] ');
insert into json_test (id, fruits) values (3, '["Pear", "Apple", "Banana"] ');

WITH fruits AS 
(SELECT 
    id, json_array_elements_text(fruits) fruit 
 FROM json_test) 
SELECT 
   id 
FROM 
   fruits
WHERE 
    fruit = 'Orange';
 id 
----
  1
  2

UPDATE 将 JSON 数组转换为 Postgres 数组的方法:

SELECT 
    array_agg(fruit) 
FROM 
    (SELECT 
        id, json_array_elements_text(fruits)AS fruit 
    FROM 
    json_test) AS elements 
GROUP BY 
    id;

 array_agg       
-----------------------
 {Pear,Apple,Banana}
 {Pear,Orange,Banana}
 {Apple,Orange,Banana}

这假定 JSON 数组具有同质元素,因为这是 Postgres 数组的要求。

json 字段中查找具有“橙色”的行的更简单方法:

SELECT 
    id, fruits 
FROM 
    json_test 
WHERE 
    fruits::jsonb ? 'Orange';

 id |             fruits             
----+--------------------------------
  1 | ["Apple", "Orange", "Banana"] 
  2 | ["Pear", "Orange", "Banana"] 

【讨论】:

  • 虽然这解释了如何查询 JSON 字段,但它没有回答 “谁能帮我编写迁移以更改现有列(类型:Json)到数组类型?” 同样考虑到这使用 CTE,它也不是很友好的导轨
  • 查看更新回答。
  • @AdrianKlaver 喜欢更新的答案。有没有办法检查值数组的存在? (例如:SELECT id, fruits FROM json_test WHERE fruits::jsonb ? ['Orange', 'apple'];)
  • 见这里JSON operators, ` SELECT id, fruits FROM json_test WHERE fruits::jsonb ?|数组['橙色','苹果'];`
【解决方案2】:
class AddArrayFruitsToMarkets < ActiveRecord::Migration[6.0]
  def up
    rename_column :markets, :fruits, :old_fruits
    add_column :markets, :fruits, :string, array: true
    Market.update_all('fruits = json_array_elements(old_fruits)')
  end
end
class RemoveJsonFruitsFromMarkets < ActiveRecord::Migration[6.0]
  def up
    remove_column :markets, :old_fruits
  end
end

但如果你真的要做某事,为什么不创建表格,因为你并没有真正改进任何东西?

class Fruit < ApplicationRecord
  validates :name, presence: true
  has_many :market_fruits
  has_many :markets, through: :market_fruits
end

class MarketFruit < ApplicationRecord
  belongs_to :market
  belongs_to :fruit
end

class Market < ApplicationRecord
  has_many :market_fruits
  has_many :fruits, through: :market_fruits

  def self.with_fruit(name)
    joins(:fruits)
      .where(fruits: { name: name })
  end

  def self.with_fruits(*names)
    left_joins(:fruits)
      .group(:id)
      .where(fruits: { name: names })
      .having('COUNT(fruits.*) >= ?', names.length) 
  end
end

【讨论】:

  • 我不能 100% 确定 'fruits = json_array_elements(old_fruits)' 会起作用,而且我现在无法测试它,因为我的 postgres 安装已损坏。但是你得到了一般的方法 - 希望为什么它是一个愚蠢的想法。
猜你喜欢
  • 2017-04-03
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-13
  • 2020-10-26
  • 2022-01-10
相关资源
最近更新 更多