/ Tags: RAILS-CODE / Categories: SOLUTIONS

Find Records Where A Jsonb Column Contains A Specific Key In Rails

Settings bags, API payloads, and feature-flag hashes all end up in a jsonb column, and eventually you need to query them — every account with a particular preference set, every webhook whose payload has a given field.

Description

PostgreSQL gives you three operators worth knowing. ? tests for the presence of a top-level key. @> tests containment — does the document include this fragment. ->> extracts a value as text for comparison. ? collides with ActiveRecord’s bind placeholder, so it must be escaped as ?? inside a where string. This is the detail that sends people in circles. A GIN index supports ? and @> across the whole document. A B-tree index on a single extracted expression is smaller and faster when one key is hot.

Sample input:

  Account.where("settings ?? :key", key: "beta_features")


Sample Output:

  SELECT accounts.* FROM accounts WHERE settings ? 'beta_features'

Answer

  # Key exists at the top level — note the escaped ??
  Account.where("settings ?? :key", key: "beta_features")

  # Any of these keys exists
  Account.where("settings ??| array[:keys]", keys: %w[beta_features early_access])

  # All of these keys exist
  Account.where("settings ??& array[:keys]", keys: %w[theme timezone])

  # Containment — key present AND equal to a value (uses a GIN index)
  Account.where("settings @> ?", { theme: "dark" }.to_json)

  # Rails 7.1+ shorthand for containment
  Account.where(settings: { theme: "dark" })

  # Extract as text and compare
  Account.where("settings ->> 'theme' = ?", "dark")

  # Nested path
  Account.where("settings #>> '{notifications,email}' = ?", "true")

  # Key is absent or null
  Account.where("NOT (settings ?? 'theme')")

  # Indexes
  # add_index :accounts, :settings, using: :gin
  # add_index :accounts, "(settings ->> 'theme')", name: "idx_accounts_theme"

Learn More

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! 💻 🏡 Grad. Student, MCS. 🎓 Class of '23. GitKraken Ambassador 🇳🇵 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More