sqlalchemy 中的三元组距离操作
Trigram distance operations in sqlalchemy
我目前正在尝试使用 pg_trgm 操作 %
和 <->
。
列上的 GIN 索引已经可用,但我找不到与前面提到的运算符等效的 sqlalchemy。
除了编写纯文本查询之外,解决此问题的最佳方法是什么。
一个简单的示例查询是:
tag = test
tag_subq = session.query(sticker_tag.file_id, f'sticker_tag.name <-> {tag}'.label(distance)) \
.filter(f'sticker_tag.name % {tag}')) \
.filter('distance' < 0.3) \
.subquery("tag_subq")
上面的查询显然不起作用,select 和筛选器字符串只是占位符,用于可视化我打算做什么。
可以使用Operators.op()方法;这会生成您需要的任何运算符:
sticker_tag.name.op('<->')(tag)
sticker_tag.name.op('%%')(tag)
百分比运算符加倍以对其进行转义,因为 python dbapi 使用 %foo 或 %(foo) 语法将参数插入查询。
对于使用 Postgres 的人,可以使用 similarity
来代替。
注意:请记住先在您的 Postgres 中安装 pg_trgm
扩展:CREATE EXTENSION pg_trgm;
下面是一个使用 SQLAlchemy 的例子:
# ... other imports
from sqlalchemy import and_, func, or_
def search_store_product(search_string: str) -> Optional[list[Product]]:
try:
return session.query(Product).filter(
or_(
func.similarity(Product.name, search_string) > 0.6,
func.similarity(Product.brand, search_string) > 0.4,
),
and_(Product.updated_on >= datetime.utcnow() - timedelta(days=5)),
).order_by(Product.created_on).limit(20).all()
except ProgrammingError as exception:
logger.exception(exception)
raise
finally:
session.close()
如果有人感兴趣,我做了一些比较 %
方法和 similarity(...) > x
方法的测试,使用 %
有显着的加速。在某些情况下超过 10 倍。
SELECT * FROM X WHERE name % 'foo';
比
快多了
SELECT name FROM x WHERE similarity(name, 'foo') > 0.7;
所以我建议只在 SELECT
语句中使用 similarity(..)
函数,前提是它与您的查询相关。像这样:
SELECT name, similarity(name, 'foo') FROM X WHERE name % 'foo';
但是您需要在使用 %
之前设置 pg_trgm.similarity_threshold
,因为默认值为 0.3,在我看来这对于大多数应用程序来说太模糊且太慢了。所以r-m-n的答案更可取,记得每次会话设置similarity_threshold
!
在SQL炼金术中会是这样的:
db.session.execute('SET pg_trgm.similarity_threshold = 0.7;')
items = Model.query.filter(Model.name.op("%")(name)).all()
我目前正在尝试使用 pg_trgm 操作 %
和 <->
。
列上的 GIN 索引已经可用,但我找不到与前面提到的运算符等效的 sqlalchemy。
除了编写纯文本查询之外,解决此问题的最佳方法是什么。
一个简单的示例查询是:
tag = test
tag_subq = session.query(sticker_tag.file_id, f'sticker_tag.name <-> {tag}'.label(distance)) \
.filter(f'sticker_tag.name % {tag}')) \
.filter('distance' < 0.3) \
.subquery("tag_subq")
上面的查询显然不起作用,select 和筛选器字符串只是占位符,用于可视化我打算做什么。
可以使用Operators.op()方法;这会生成您需要的任何运算符:
sticker_tag.name.op('<->')(tag)
sticker_tag.name.op('%%')(tag)
百分比运算符加倍以对其进行转义,因为 python dbapi 使用 %foo 或 %(foo) 语法将参数插入查询。
对于使用 Postgres 的人,可以使用 similarity
来代替。
注意:请记住先在您的 Postgres 中安装 pg_trgm
扩展:CREATE EXTENSION pg_trgm;
下面是一个使用 SQLAlchemy 的例子:
# ... other imports
from sqlalchemy import and_, func, or_
def search_store_product(search_string: str) -> Optional[list[Product]]:
try:
return session.query(Product).filter(
or_(
func.similarity(Product.name, search_string) > 0.6,
func.similarity(Product.brand, search_string) > 0.4,
),
and_(Product.updated_on >= datetime.utcnow() - timedelta(days=5)),
).order_by(Product.created_on).limit(20).all()
except ProgrammingError as exception:
logger.exception(exception)
raise
finally:
session.close()
如果有人感兴趣,我做了一些比较 %
方法和 similarity(...) > x
方法的测试,使用 %
有显着的加速。在某些情况下超过 10 倍。
SELECT * FROM X WHERE name % 'foo';
比
快多了SELECT name FROM x WHERE similarity(name, 'foo') > 0.7;
所以我建议只在 SELECT
语句中使用 similarity(..)
函数,前提是它与您的查询相关。像这样:
SELECT name, similarity(name, 'foo') FROM X WHERE name % 'foo';
但是您需要在使用 %
之前设置 pg_trgm.similarity_threshold
,因为默认值为 0.3,在我看来这对于大多数应用程序来说太模糊且太慢了。所以r-m-n的答案更可取,记得每次会话设置similarity_threshold
!
在SQL炼金术中会是这样的:
db.session.execute('SET pg_trgm.similarity_threshold = 0.7;')
items = Model.query.filter(Model.name.op("%")(name)).all()