简体   繁体   English

如何编写迁移以将 JSON 字段转换为 Postgres 数组以在 rails 中查询?

[英]How to write a migration to convert JSON field to Postgres Array for querying in rails?

There is an old table with column type as JSON but only arrays are stored in this column.有一个列类型为 JSON 的旧表,但只有 arrays 存储在此列中。 Even though I am storing array, I am not able to query this field using the ANY keyword (which will work on array type columns in Postgres like in this post )即使我正在存储数组,我也无法使用ANY关键字查询该字段(这将适用于 Postgres 中数组类型列,如本文中所示)

Eg : let's say ['Apple', 'Orange', 'Banana'] is stored as Json in the fruits column, I want to query like Market.where(":name = ANY(fruits)", name: "Orange") and get all the markets with Oranges available.例如:假设 ['Apple', 'Orange', 'Banana'] 在fruits列中存储为 Json,我想查询Market.where(":name = ANY(fruits)", name: "Orange")并获得所有可用橙子的市场。

Can anyone please help me to write a migration to change the existing column(type: Json) to array type?谁能帮我写一个迁移来将现有的列(类型:Json)更改为数组类型?

One example assuming a json field:一个假设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 Method to convert JSON array into Postgres array:将 JSON 数组转换为 Postgres 数组的UPDATE方法:

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}

This assumes the JSON array has homogeneous elements as that is a requirement for Postgres arrays.这假设 JSON 数组具有同质元素,因为这是 Postgres arrays 的要求。

A simpler method of finding rows that have 'Orange' in the json field:json字段中查找具有“橙色”的行的更简单方法:

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

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

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

But really if you're going to do something why not create tables instead as you're not really improving anything?但是,如果您真的要做某事,为什么不创建表格,因为您并没有真正改进任何东西?

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

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

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