0

I have a table search with a column vector of type tsvector. I'd like to use the SQLAlchemy core API to insert rows. Computing vector requires 1) some processing in Python and 2) a call to to_tsvector (a PG function).

I'm messing around with conn.execute(insert(Search), [{'vector': func.to_tsvector('here is a string')}]) but am getting

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'Function'

I'm trying to replicate the SQL query insert into search(vector) values (to_tsvector('here is a string')).

1
  • I don't think there's a clean way to do it for multiple inserts, though what you are doing should work for a single insert. This is similar, with a (hacky) answer that you might be able to build on. Commented Jan 29, 2022 at 10:56

1 Answer 1

1

This seemed weird to me and I thought it was a bug but it is not a bug. zzzeek explains it in this discussion:

https://github.com/sqlalchemy/sqlalchemy/discussions/7640

You need to use insert().values() like session.execute(tbl.insert().values(values)) instead of directly using session.execute(tbl.insert(), values).

In case this gets lost you can perform the inserts like this example:

Code

from sqlalchemy import (
    create_engine,
    Integer,
    Text,
    DateTime,
)
from sqlalchemy.schema import (
    Column,
    MetaData,
)
from sqlalchemy.sql import func, not_
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.dialects.postgresql import TSVECTOR
from sqlalchemy.exc import ProgrammingError


metadata = MetaData()

Base = declarative_base(metadata=metadata)

engine = create_engine('postgresql+psycopg2://username:password@/database', echo=False)


class Doc(Base):
    __tablename__ = "docs"
    id = Column(Integer, primary_key=True, index=True)
    text = Column(Text, nullable=True)
    vector = Column(TSVECTOR, nullable=True)
    created_on = Column(DateTime, nullable=False)


metadata.create_all(engine)


doc_specs = [{
    "text": "Frog jumping.",
}, {
    "text": "Fish swimming.",
}, {
    "text": "Bird flying.",
}, {
    "text": "Cat jumping and flying.",
}]


with Session(engine) as session:
    values = [{
        "created_on": func.now(),
        "text": spec["text"],
        "vector": func.to_tsvector(spec["text"])
    } for spec in doc_specs]
    session.execute(Doc.__table__.insert().values(values))
    session.commit()

    print ("Matches")
    for doc in session.query(Doc).filter(Doc.vector.match("jumping & flying")):
        print (doc.id, doc.text, doc.vector)
    print ("Misses")
    for doc in session.query(Doc).filter(~Doc.vector.match("jumping & flying")):
        print (doc.id, doc.text, doc.vector)

Output

DROP DATABASE
CREATE DATABASE
Matches
4 Cat jumping and flying. 'cat':1 'fli':4 'jump':2
Misses
1 Frog jumping. 'frog':1 'jump':2
2 Fish swimming. 'fish':1 'swim':2
3 Bird flying. 'bird':1 'fli':2
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! Somewhat surprising, but the explanation makes sense!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.