The Welsh Dragon
The Welsh Dragon

Reputation: 529

How to filterby with nested jsonb with sqlalchemy and postgres

I can't work out how to use SQLAlchemy to query a table that contains JSONB data.

The JSON structure is similar to this:

example = {
  "product": "Widget",
  "version": 1,
  "release_date": "2019-07-01T00:00:00.000Z",
  "widget": {
    "code": "ABCDEF",
    "name": "my widget",
    "uses": {
        "rain": "yes",
        "snow": "no",
        "heat": "sometimes",
...

I'm trying to execute a query with this functionality:

SELECT
    json  AS acdata
FROM
    aircraft.aircraft
WHERE json -> 'widget' -> 'uses' ->> 'rain' = 'No';

in this form:

widget= session.query(Widget).filter_by(rain='No').first()

I know the rain='No' isn't correct but I can't work out what should go there! The objective is to get the first Widget whose key widget.uses.rain = 'No'

I have this definition for my Widget class

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.dialects.postgresql import JSONB, UUID

class Widget(Base):
    __tablename__ = 'widgets'
    __table_args__ = {'schema' : 'factory'}

    id = Column(UUID(as_uuid=True), primary_key=True)
    json = Column(JSONB, nullable=False)
...

Upvotes: 4

Views: 2680

Answers (1)

Ilja Everilä
Ilja Everilä

Reputation: 52929

To access a nested jsonb value use a tuple of keys (path index operation) and astext to convert the value as text:

widget = session.query(Widget).\
    filter(Widget.json["widget", "uses", "rain"].astext == 'No').\
    first()

The resulting query uses the #>> operator for the path index + astext operation. You cannot use filter_by() for this kind of predicate, as it is used for simple equality checks against attributes of a model class.

Upvotes: 2

Related Questions