0

I have the following table Profile with a jsonb column

item_type object_changes
"Item" [{"customer_id": [1, 5], "other_id": 1}, {"customer_id": [4, 5], "other_id": 2}]
"Item" [{"customer_id": [3, 6], "other_id": 3}, {"customer_id": [3, 5], "other_id": 2}]

I want to be able to query using active record to find all rows that has customer_id 5.

I tried doing the following but it doesn't work

Profile.where("object_changes->'customer_id' @> '5'")

Profile.where("object_changes->'customer_id' @> ?::jsonb", [5].to_json)

Profile.where("? = ANY (object_changes->>'customer_id')", 5)

Does anyone know how i can be able to make this query in Ruby on Rails.

My Rails version is Rails 4.2 and Ruby version is 2.4.10 and I am using postgres as my DB

0

1 Answer 1

1

I think what you need is a combination of jsonb_to_recordset and lateral join.

For the following schema and data

CREATE TABLE profiles (
  id integer,
  item_type text,
  object_changes jsonb
);

INSERT INTO profiles(id, item_type, object_changes) VALUES
  (1, 'Item', '[{"customer_id": [1, 5], "other_id": 1}, {"customer_id": [4, 5], "other_id": 2}]'::jsonb),
  (2, 'Item', '[{"customer_id": [3, 6], "other_id": 3}, {"customer_id": [3, 5], "other_id": 2}]'::jsonb),
  (3, 'Item', '[{"customer_id": [4, 7], "other_id": 3}, {"customer_id": [8, 9], "other_id": 2}]'::jsonb);

Something like this would work:

SELECT distinct profiles.*
FROM 
  profiles, 
  jsonb_to_recordset(profiles.object_changes) AS changes(customer_id integer[], other_id integer)
WHERE 5 = ANY(changes.customer_id);


 id | item_type |                                  object_changes
----+-----------+----------------------------------------------------------------------------------
  2 | Item      | [{"other_id": 3, "customer_id": [3, 6]}, {"other_id": 2, "customer_id": [3, 5]}]
  1 | Item      | [{"other_id": 1, "customer_id": [1, 5]}, {"other_id": 2, "customer_id": [4, 5]}]
(2 rows)

So the final solution with AR query interface is something like (I hardcode the value to find but I believe you get the idea and parametrization is not a problem):

Profile.find_by_sql(<<~SQL)
  SELECT distinct profiles.*
  FROM 
    profiles, 
    jsonb_to_recordset(profiles.object_changes) AS changes(customer_id integer[], other_id integer)
  WHERE 5 = ANY(changes.customer_id)
SQL
Sign up to request clarification or add additional context in comments.

1 Comment

i end up getting an error PG::InvalidTextRepresentation: ERROR: malformed array literal: for the customer_id integer[]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.