SQLAlchemy has_any()

SQLAlchemy has_any()

我无法使用 SQLAlchemy 的 has_any() 函数。我只是想在 jsonb 列中包含的列表中过滤我的结果:

db.session.query(models.Record.id).\
    filter(models.Record.record_metadata["teams"].has_any(team_ids))

models.Record.record_metadata["teams"] 是 jsonb 列 (record_metadata) 中的一个 id 列表,我想与 team_ids 进行比较,它也是一个 id 列表

生成的查询是:

SELECT record.id AS record_id
FROM record
WHERE ((record.record_metadata -> 'teams')) ?| '["id1", "id2"]';

这给出了错误

DataError: (psycopg2.DataError) malformed array literal: "["id1", "id2"]"
LINE 3: WHERE ((record.record_metadata -> 'teams')) ?| '["id1", "id2"]'
                                                       ^
DETAIL:  "[" must introduce explicitly-specified array dimensions.

但是关于 Postgres 官方文档中的 Table 9.44. Additional jsonb Operators|? 运算符后面应该跟一个 array[]

确实,当我手动编写查询时:

SELECT record.id AS record_id
FROM record
WHERE ((record.record_metadata -> 'teams')) ?| array['id1', 'id2'];

它工作得很好。

SQLAlchemy's documentation关于这个功能很差,我找不到任何例子。

难道我这个功能做错了什么?我应该为这种情况使用其他东西吗?还是has_any()坏了?

编辑:

SELECT record.id AS record_id
FROM record
WHERE ((record.record_metadata -> 'teams')) ?| '{"id1", "id2"}';

也很好用,我发现 has_any() 也接受字符串。

所以这是一个(非常)讨厌的修复,但是如果我用所需的 ID 格式化字符串:

db.session.query(models.Record.id).\
    filter(models.Record.record_metadata["teams"].has_any(
        str(team_ids).
        replace('[', '{').
        replace(']', '}').
        replace('\'', '\"')))

有效...

似乎默认情况下传递的值是 JSON 编码的,如果它是 listdict。您可以通过显式传递 array literal:

来解决这个问题
In [15]: from sqlalchemy.dialects.postgresql import array

In [16]: session.query(Foo).\
    ...:     filter(Foo.data['test'].has_any(array(['1', '2']))).all()
2018-06-18 09:07:20,780 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-06-18 09:07:20,780 INFO sqlalchemy.engine.base.Engine SELECT foo.id AS foo_id, foo.data AS foo_data 
FROM foo 
WHERE ((foo.data -> %(data_1)s)) ?| ARRAY[%(param_1)s, %(param_2)s]
2018-06-18 09:07:20,781 INFO sqlalchemy.engine.base.Engine {'data_1': 'test', 'param_1': '1', 'param_2': '2'}
Out[16]: []

这样您就不需要从 Python list 的字符串表示中手动格式化数组文字,这可能很容易出错。

错误本身是 Postgresql 试图将传递的文字(包含 JSON)解析为数组的结果。如错误所述,text representation of an array 可能以指定数组下标范围的维度修饰开头:

'[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[]