halostatue
Ecto Fragments and the PostgreSQL JSON `?` operator
How do I use the PostgreSQL JSON ? operator in Ecto? Assume that I have a table users that has a JSONB field data, and I want to test (in PostgreSQL) whether it has a key honorific.
The SQL for this is:
SELECT *
FROM users
WHERE data ? 'honorific';
Neither of these work:
from u in :users, where: fragment("? \? ?", u.data, "honorific")
from u in :users, where: fragment("? ? ?", u.data, "?", "honorific")
The former fails with a compile error (not enough parameters to match the number of question marks) and the latter fails in the query because it produces SELECT u0.* FROM users WHERE u.data '?' 'honorific'.
I think that this is a bug, and I’d be interested to know how we would need to use such operators, because PostgreSQL has several:
┌──────┬─────────┬───────────┬─────────────┬────────────────────────────┐
│ Name │ Left arg│ Right arg │ Result type │ Description │
├──────┼─────────┼───────────┼─────────────┼────────────────────────────┤
│ <?> │ abstime │ tinterval │ boolean │ is contained by │
│ ? │ jsonb │ text │ boolean │ key exists │
│ ?# │ box │ box │ boolean │ deprecated, use && instead │
│ ?# │ line │ box │ boolean │ intersect │
│ ?# │ line │ line │ boolean │ intersect │
│ ?# │ lseg │ box │ boolean │ intersect │
│ ?# │ lseg │ line │ boolean │ intersect │
│ ?# │ lseg │ lseg │ boolean │ intersect │
│ ?# │ path │ path │ boolean │ intersect │
│ ?& │ jsonb │ text[] │ boolean │ all keys exist │
│ ?- │ point │ point │ boolean │ horizontally aligned │
│ ?- │ │ line │ boolean │ horizontal │
│ ?- │ │ lseg │ boolean │ horizontal │
│ ?-| │ line │ line │ boolean │ perpendicular │
│ ?-| │ lseg │ lseg │ boolean │ perpendicular │
│ ?| │ jsonb │ text[] │ boolean │ any key exists │
│ ?| │ point │ point │ boolean │ vertically aligned │
│ ?| │ │ line │ boolean │ vertical │
│ ?| │ │ lseg │ boolean │ vertical │
│ ?|| │ line │ line │ boolean │ parallel │
│ ?|| │ lseg │ lseg │ boolean │ parallel │
└──────┴─────────┴───────────┴─────────────┴────────────────────────────┘
Marked As Solved
michalmuskala
You can escape the question mark with \, but you need it doubled - once for string and once for fragment. This should work fine:
from u in :users, where: fragment("? \\? ?", u.data, "honorific")
Also Liked
NobbZ
halostatue
This does not answer the question I raised above, but I have found two workarounds for this using PostgreSQL. Both of these depend on an undocumented function (only the ? operator is documented) and a bit of digging to produce something similar as needed.
-
Use the underlying function of
?directly.fragment("jsonb_exists(?, ?)", u.data, "honorific"). The functions for?&and?|arejsonb_exists_allandjsonb_exists_any, so those can be used as well (but note that the types aretext[], so you would need to dofragment("jsonb_exists_any(?, ?)", u.data, ["honorific"])). -
Create a new operator analogous to
?,?&, and?|in your schema.
CREATE OPERATOR =~ (
PROCEDURE=pg_catalog.jsonb_exists, LEFTARG=jsonb, RIGHTARG=text
);
CREATE OPERATOR =~& (
PROCEDURE=pg_catalog.jsonb_exists_all, LEFTARG=jsonb, RIGHTARG=text[]
);
CREATE OPERATOR =~| (
PROCEDURE=pg_catalog.jsonb_exists_any,LEFTARG=jsonb, RIGHTARG=text[]
);
Then you can construct these…
fragment("? =~ ?", u.data, "honorific")
fragment("? =~& ?", u.data, ["honorific"])
fragment("? =~| ?", u.data, ["honorific"])
Unfortunately, no one in the PostgreSQL community will understand these operators against json/jsonb types because you’ve created them, and you’re no longer writing standard PostgreSQL JSON queries. Unless you know these operators have been created, asking questions about these would cause confusion.
halostatue
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









