我正在使用 MySQL 8.0 和 SQLalchemy。
我有一个模型如下:
class Json(TypeDecorator):
impl = String(1000)
def process_bind_param(self, value, dialect):
return json.dumps(value)
def process_result_value(self, value, dialect):
if not value:
return None
return json.loads(value)
class BookCommerce(Base):
bcDetails = Column(Json)
这在数据库中呈现为 varchar(1000),装饰器意味着它对于访问我从该字段保存或检索的字典值来说一切都很好。
我的问题是我现在想要搜索字段寻找值(包括表示为
%my_string%
的值片段)。像这样的查询不会产生任何结果:
BookCommerce.query.filter(BookCommerce.bcDetails.like(fragname))).all()
因为 SQLA(我认为)在执行查询之前将 varchar 呈现为 JSON 文档。 奇怪的是,这个查询产生的 SQL 实际上可以在 MySQL 命令窗口中运行。
我已经尝试了this问题中接受的解决方案,但它引用了MySQL中的JSON类型,因此在语法上出现了问题(在Python和MySQL命令行中)。
对于如何正确处理这个问题有什么建议吗?一般来说,我不想放弃这个 Json 装饰的解决方案,我只是想要一个自由格式的搜索来查找匹配的记录。
从 create_engine 上的 echo=True 获得的 SQL 查询:
2021-03-25 19:47:11,638 INFO sqlalchemy.engine.Engine SELECT bookcommerce.`bcDetails` AS `bookcommerce_bcDetails`
FROM bookcommerce
WHERE bookcommerce.`bcDetails` LIKE %(bcDetails_1)s
2021-03-25 19:47:11,656 INFO sqlalchemy.engine.Engine [cached since 46.19s ago] {'bcDetails_1': '"%test%"'}
2021-03-25 19:47:11,663 DEBUG sqlalchemy.engine.Engine Col ('bookcommerce_bcDetails')
2021-03-25 19:47:11,667 INFO sqlalchemy.engine.Engine ROLLBACK
问题是查询项在查询编译过程中被“JSON化”:注意嵌入的双引号:
{'bcDetails_1': '"%test%"'}
也许可以在
JSON
的定义中修复此问题,但解决方法是将术语转换为 SQLAlchemy 的 String
类型:大多数 TypeDecorator
魔法将忽略已转换为 SQL 类型的值。
with Session() as s:
q = sa.select(BookCommerce).where(
BookCommerce.bcDetails.like(sa.cast('%test%', sa.String))
)
bcs = s.scalars(q)
for bc in bcs:
print(bc.bcDetails)
日志输出:
2024-04-07 08:48:06,419 INFO sqlalchemy.engine.Engine SELECT t66799742.id, t66799742.`bcDetails`
FROM t66799742
WHERE t66799742.`bcDetails` LIKE CAST(%(param_1)s AS CHAR)
2024-04-07 08:48:06,419 INFO sqlalchemy.engine.Engine [no key 0.00012s] {'param_1': '%test%'}