[英]Selecting data from PostgreSQL JSON field with SQLAlchemy / Flask SQLAlchemy
我在數據庫中的 JSON 是這樣的:
{
"ingredients": [
"3 tablespoons almond butter",
...
"Lime wedges"
],
"instructions": [
"In a bowl"
]
"tags": [
"gluten-free",
"grain bowls",
"Thai"
]
}
我的 app.py 文件:
class Recipe(db.Model):
__tablename__ = "recipe"
....
recipe = db.Column(db.JSON)
對於初學者,我正在嘗試 select 所有帶有“Thai”標簽的記錄。
Recipe.query.filter(Recipe.recipe['tags'].contains(["Thai"])).all()
告訴我我可能需要顯式類型轉換,我已經嘗試過 astext 和 cast.. 似乎沒有任何效果。 不知道我做錯了什么。
這是堆棧跟蹤的結尾:
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedFunction) operator does not exist: json ~~ text
LINE 3: WHERE ((recipe.recipe -> 'tags') LIKE '%' || CAST('["%Thai%"...
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
[SQL: SELECT recipe.id AS recipe_id, recipe.url_id AS recipe_url_id, recipe.author AS recipe_author, recipe.description AS recipe_description, recipe.recipe AS recipe_recipe
FROM recipe
WHERE ((recipe.recipe -> %(recipe_1)s) LIKE '%%' || CAST(%(param_1)s AS JSON) || '%%')]
[parameters: {'recipe_1': 'tags', 'param_1': '["%Thai%"]'}]
我嘗試了:
Recipe.query.filter(Recipe.recipe['tags'].contains(cast(["%Thai%"], JSON)))
有效的是
db.engine.execute(text("select * from recipe where recipe->> 'tags' like '%Thai%'"))
我可以忍受,但我頑固地希望它通過 ORM 發生。
我唯一的線索是在堆棧跟蹤中我看到它生成了一個 sql 語句 -> 不是 ->>... 但我不知道如何獲得 ->>
然后我發現了這個:
tags_subquery = db.session.query(func.json_array_elements(Recipe.recipe['tags']).label('tags')).subquery()
query = db.session.query(Recipe, tags_subquery.c.tags).filter(tags_subquery.c.tags.op('->>')('tags').like("%Thai%"))
並且沒有錯誤,但是結果是空的......我想我現在有點接近了@rmn
您可以通過將表達式的左側轉換為文本類型來避免錯誤:
# select cast('{"a": ["spam", "ham"]}'::json->'a' as text) like '%spam%' as "Exists?";
Exists?
═════════
t
但這也將匹配 substrings ,這可能是不可取的:
# select cast('{"a": ["spam", "ham"]}'::json->'a' as text) like '%am%' as "Exists?";
Exists?
═════════
t
更好的選擇是轉換為 jsonb:
# select cast('{"a": ["spam", "ham"]}'::json->'a' as jsonb) @> '["spam"]' as "Exists?";
Exists?
═════════
t
# select cast('{"a": ["spam", "ham"]}'::json->'a' as jsonb) @> '["am"]' as "Exists?";
Exists?
═════════
f
在 Python 方面,這看起來像
from sqlalchemy import cast
from sqlalchemy.dialects.postgresql import JSONB
result = Recipe.query.filter(
cast(Recipe.recipe['tags'], JSONB).contains(["Thai"])
).all()
將列類型更改為JSONB將消除強制轉換的需要(但需要遷移):
class Recipe(db.Model)
...
recipe = sb.Column(JSONB)
result = Recipe.filter(Recipe.recipe['tags'].contains(['Thai'])).all()
您可以在源代碼中查看 JSON(B) 比較方法 map 到 Postgresql 的功能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.