72

I am using Postgres' json data type but want to do a query/ordering with data that is nested within the json.

I want to order or query with .where on the json data type. For example, I want to query for users that have a follower count > 500 or I want to order by follower or following count.

Thanks!

Example:

model User

data: {
     "photos"=>[
       {"type"=>"facebook", "type_id"=>"facebook", "type_name"=>"Facebook", "url"=>"facebook.com"}
      ], 
     "social_profiles"=>[
         {"type"=>"vimeo", "type_id"=>"vimeo", "type_name"=>"Vimeo", "url"=>"http://vimeo.com/", "username"=>"v", "id"=>"1"},
         {"bio"=>"I am not a person, but a series of plants", "followers"=>1500, "following"=>240, "type"=>"twitter", "type_id"=>"twitter", "type_name"=>"Twitter", "url"=>"http://www.twitter.com/", "username"=>"123", "id"=>"123"}
     ]
}
7
  • PostgrSQL 9.3 is what I am using Commented Mar 27, 2014 at 4:11
  • OK, and did you look at the json operators like -> ? Commented Mar 27, 2014 at 4:28
  • Record.where("data -> 'status' = 200 ") not working Commented Mar 27, 2014 at 4:32
  • Ah, you want to write it in Rails/ActiveRecord syntax. In that case ... runs rapidly away. Commented Mar 27, 2014 at 4:35
  • Ha :D How can this be done with pure Psql queries? Commented Mar 27, 2014 at 4:38

4 Answers 4

173

For any who stumbles upon this. I have come up with a list of queries using ActiveRecord and Postgres' JSON data type. Feel free to edit this to make it more clear.

Documentation to the JSON operators used below: https://www.postgresql.org/docs/current/functions-json.html.

# Sort based on the Hstore data:
Post.order("data->'hello' DESC")
=> #<ActiveRecord::Relation [
    #<Post id: 4, data: {"hi"=>"23", "hello"=>"22"}>, 
    #<Post id: 3, data: {"hi"=>"13", "hello"=>"21"}>, 
    #<Post id: 2, data: {"hi"=>"3", "hello"=>"2"}>, 
    #<Post id: 1, data: {"hi"=>"2", "hello"=>"1"}>]> 

# Where inside a JSON object:
Record.where("data ->> 'likelihood' = '0.89'")

# Example json object:
r.column_data
=> {"data1"=>[1, 2, 3], 
    "data2"=>"data2-3", 
    "array"=>[{"hello"=>1}, {"hi"=>2}], 
    "nest"=>{"nest1"=>"yes"}} 

# Nested search:
Record.where("column_data -> 'nest' ->> 'nest1' = 'yes' ")

# Search within array:
Record.where("column_data #>> '{data1,1}' = '2' ")

# Search within a value that's an array:
Record.where("column_data #> '{array,0}' ->> 'hello' = '1' ")
# this only find for one element of the array. 

# All elements:
Record.where("column_data ->> 'array' LIKE '%hello%' ") # bad
Record.where("column_data ->> 'array' LIKE ?", "%hello%") # good
Sign up to request clarification or add additional context in comments.

6 Comments

There's a helpful answer here that deals with multiple levels: stackoverflow.com/questions/23609331/… and the logic behind it here: postgresql.org/docs/9.3/static/functions-json.html
Thanks @AmeetWadhwani I am probably going to write a blog post on this topic. Stay tuned for other coming across this answer!
@MohamedElMahallawy Can you help with this question: stackoverflow.com/questions/27697489/…. I am not able to figure out where query for a row which has an array of json elements without a key.
When searching within an array, what does the latter 1 mean in {data1,1}?
Record.where("column_data ->> 'array' LIKE '%hello%' ") is OK. Record.where("column_data ->> 'array' LIKE '%#{user_input}%' ") is bad. That's where you need the SQL parameter: Record.where("column_data ->> 'array' LIKE '%?%' ", user_input)
|
9

According to this http://edgeguides.rubyonrails.org/active_record_postgresql.html#json there's a difference in using -> and ->>:

# db/migrate/20131220144913_create_events.rb
create_table :events do |t|
  t.json 'payload'
end

# app/models/event.rb
class Event < ActiveRecord::Base
end

# Usage
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})

event = Event.first
event.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]}

## Query based on JSON document
# The -> operator returns the original JSON type (which might be an object), whereas ->> returns text
Event.where("payload->>'kind' = ?", "user_renamed")

So you should try Record.where("data ->> 'status' = 200 ") or the operator that suits your query (http://www.postgresql.org/docs/current/static/functions-json.html).

Comments

4

JSON filtering in Rails

Event.create( payload: [{ "name": 'Jack', "age": 12 },
                                 { "name": 'John', "age": 13 },
                                 { "name": 'Dohn', "age": 24 }]

Event.where('payload @> ?', '[{"age": 12}]')
#You can also filter by name key
Event.where('payload @> ?', '[{"name": "John"}]')
#You can also filter by {"name":"Jack", "age":12}
Event.where('payload @> ?', {"name":"Jack", "age":12}.to_json)

You can find more about this here

Comments

3

Your question doesn't seem to correspond to the data you've shown, but if your table is named users and data is a field in that table with JSON like {count:123}, then the query

SELECT * WHERE data->'count' > 500 FROM users

will work. Take a look at your database schema to make sure you understand the layout and check that the query works before complicating it with Rails conventions.

1 Comment

This doesn't work for me in Postgres 9.3.3 unless I cast the JSON result to text and then to an integer: SELECT * FROM users WHERE (data->'count')::text::integer > 500;

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.