Home > Blockchain >  SqlAlchemy Postgres - query with @> ANY(ARRAY['["...]']::jsonb[])
SqlAlchemy Postgres - query with @> ANY(ARRAY['["...]']::jsonb[])

Time:12-19

I have a postgres table represented in the sql alchemy like

from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class ListingDatabaseDocument(Base):
    __tablename__ = 'listing'
    uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True)
    doc = Column(JSONB, nullable=False)

My doc jsonb field looks like

{"name": "SOME_NAME", "features": ["BALCONY", "GARAGE", "ELEVATOR"]}

Now I'd like to get all rows where the doc->'features' array contains "ELEVATOR","GARAGE" - in pure sql I do it like

SELECT * FROM listing
WHERE doc -> 'features' @> ANY(ARRAY['["ELEVATOR","GARAGE"]']::jsonb[])

How to achieve this in SqlAlchemy ? I tried something like

from sqlalchemy.dialects.postgresql import JSONB, ARRAY
from sqlalchemy.sql.expression import cast
from sqlalchemy import any_

return session.query(ListingDatabaseDocument).filter(
    ListingDatabaseDocument.doc['features'].op('@>')(any_(cast(['ELEVATOR','GARAGE'], ARRAY(JSONB))))
).all()

but it's not working. Thanks for help !

CodePudding user response:

If you enable logging in SQLAlchemy you can see that the SQLAlchemy version o the query is casting differently than the SQL version. Specifically it's casting to JSONB[] twice, whereas in the SQL version the casts are JSONB (implicit - the string is already JSON) and JSONB.

SELECT listing.uuid AS listing_uuid, listing.doc AS listing_doc 
  FROM listing 
  WHERE (listing.doc -> %(doc_1)s) @> ANY (CAST(%(param_1)s::JSONB[] AS JSONB[]))

Rather than trying to fix this, I think the SQL version can be simplified to

SELECT uuid, doc 
  FROM listing 
  WHERE doc -> 'features' @> '["ELEVATOR", "GARAGE"]';

And the SQLAlchemy equivalent would be

s.query(ListingDatabaseDocument)
.filter(
    ListingDatabaseDocument.doc['features'].op('@>')(
        sa.cast(['ELEVATOR', 'GARAGE'], JSONB)
    )
)
  • Related