halostatue

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

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")
16
Post #5

Also Liked

NobbZ

NobbZ

Instead of "? \\? ?" one can also use ~S"? \? ?", which removes one layer of escaping.

halostatue

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.

  1. Use the underlying function of ? directly. fragment("jsonb_exists(?, ?)", u.data, "honorific"). The functions for ?& and ?| are jsonb_exists_all and jsonb_exists_any, so those can be used as well (but note that the types are text[], so you would need to do fragment("jsonb_exists_any(?, ?)", u.data, ["honorific"])).

  2. 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

halostatue

I should have tried that. This might be worth putting in the Ecto documentation.

Where Next?

Popular in Questions Top

New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 44778 311
New

We're in Beta

About us Mission Statement