1

My User model have jsonb column which names is raw. It looks like this:

{"client"=>{"tokens"=>["asdasd"]}}

Now I want to find a user by a token which is in raw["client"]["tokens"]. How can I do that?

Mateusz Urbański
  • 7,352
  • 15
  • 68
  • 133
  • 1
    Try `User.where("raw->'client'->'tokens' = ?", 'your token here')` – Pavan Jul 11 '17 at 07:25
  • 1
    This might be relevant to what you were looking for https://stackoverflow.com/questions/22667401/postgres-json-data-type-rails-query – Ace Dimasuhid Jul 11 '17 at 07:31
  • 1
    If you need search by one token from `tokens` array, you have to use `@>`: `User.find_by("raw->'client'->'tokens' @> ?", ['token'].to_json)` – Pavel Mikhailyuk Jul 11 '17 at 08:17

1 Answers1

2

I usually first craft such queries in a SQL console and then convert it to ActiveRecord.

You can navigate hash keys in Postgres. The query

SELECT
  raw #> '{client,tokens}'
FROM users

will return just the tokens array from that path. Now we will need to check if it contains the value we are looking for. The query

SELECT
  raw #> '{client,tokens}' ? 'asdasd'
FROM users

will select t for those row that have a matching token. Now you can move this to the WHERE section:

SELECT
*
FROM users
WHERE  raw #> '{client,tokens}' ? 'asdasd'

And if this selects what you expect, then you can convert it to AR:

User.where("config #> '{client, tokens}' ? :token", token: 'asdasd')

Note that I can not use ? for parameter substitution and use :token instead. Also note that this only works in JSONB (Postgres 9.4+ https://www.postgresql.org/docs/9.4/static/functions-json.html#FUNCTIONS-JSONB-OP-TABLE)

Update:

You should(tm) (I have not tested this) get along with:

CREATE INDEX index_tokens ON users USING GIN ((raw #> '{client, tokens}'));

See https://www.postgresql.org/docs/9.6/static/datatype-json.html#JSON-INDEXING for more details

Update2:

Another way to query would be:

raw @> '{"client": {"tokens": ["asdasd"]}}'   

Which should be able to use a simple GIN index on the raw column (which uses more space than the expression index described above).

CREATE INDEX index_user_raw ON users USING GIN (raw)

Again: details see the JSON INDEXING link above. And use a Query Visualizer to see the differences. I like http://tatiyants.com/pev

Pascal
  • 8,464
  • 1
  • 20
  • 31