在一个PostgreSQL表"PRIVATE_INNOTION"中,我有一个JSONB列"RECORD_MAP",它可能包含也可能不包含嵌套对象,例如.

{
  "blocks": {
    "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
      "name": "block1",
      "value": 1,
      "child": {
        "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
          "name": "block2",
          "value": 2,
          "child": {
            "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
              "name": "block3",
              "value": 3
            }
          }
        },
        "7a9abf0d-a066-4466-a565-4e6d7a960a38": {
          "name": "block4",
          "value": 4,
          "child": {
            "7a9abf0d-a066-4466-a565-4e6d7a960a39": {
              "name": "block5",
              "value": 5,
              "child": {
                "7a9abf0d-a066-4466-a565-4e6d7a960a40": {
                  "name": "block6",
                  "value": 6
                }
              }
            }
          }
        },
      }
    }
  }
}

要检索数据,我们不知道哪个块有我们想要的数据,我们只有密钥.让我们假设我们正在寻找这个键为"7a 9abf 0 d-a066 -4466-a565- 4 e6 d 7a 960 a40"的对象,但我们不知道它位于父块4和块5的子块6中.另一个请求可能会寻找父块4等等,我必须通过它的键找到块.

整个代码如下所示;

async def get_private_notion_page(
        site_uuid: str, page_id: str, db_session: AsyncSession
    ) -> PrivateNotionPage:
       
        page_id_path = f"{page_id}" # page_id looks like this 7a9abf0d-a066-4466-a565-4e6d7a960a37
        path = f"$.** ? (@.{page_id_path})"

        stmt = text(
            f"""
            SELECT jsonb_path_query(record_map, {path})
            FROM private_notion
            WHERE site_id = {site_uuid}
            """
        )

        result = await db_session.execute(stmt)
        result = result.scalars().first()

        if result:
            return result
        else:
            raise PrivateNotionSiteWasNotFound

因此,我想出了下面的查询语句,这些语句使用了SQL"Text"方法来接受原始的SQL查询,但是jsonb_path_query_arrayjsonb_path_query抛出了类似的错误;syntax error at or near "$".

page_id_path = f"{page_id}"
path = f"$.** ? (@.{page_id_path})"
stmt = text(
            f"""
            SELECT jsonb_path_query(record_map, {path})
            FROM private_notion
            WHERE site_id = {site_uuid}
            """
        )

Error:

sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.ProgrammingError) <class 'asyncpg.exceptions.PostgresSyntaxError'>: syntax error at or near "$"
[SQL: 
             SELECT jsonb_path_query(record_map, $.** ? (@.7a9abf0d-a066-4466-a565-4e6d7a960a37))
             FROM private_notion
             WHERE site_id = 26f52d8e-a380-46ab-9131-e6f7f62c528f
]

我后来了解到"$**运算符在SQL查询中无效.相反,您可以使用jsonb_路径_查询_数组函数递归地搜索所有级别的JSONB对象."

显然,在重构代码后,我也遇到了同样的错误.

page_id_path = f"{page_id}"
path = f"$[*] ? (@ like_regex {page_id_path})"
stmt = text(
            f"""
            SELECT jsonb_path_query_array(record_map -> 'block', {path})
            FROM private_notion
            WHERE site_id = {site_uuid}
            """
        )

Error:

sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.ProgrammingError) <class 'asyncpg.exceptions.PostgresSyntaxError'>: syntax error at or near "$"
[SQL: 
     SELECT jsonb_path_query_array(record_map -> 'block', $[*] ? (@ like_regex 7a9abf0d-a066-4466-a565-4e6d7a960a37))
     FROM private_notion
     WHERE site_id = 26f52d8e-a380-46ab-9131-e6f7f62c528f
]

我的问题是双管齐下的,到底是什么错误?还有在JSONB列中通过键检索嵌套对象的更好方法吗?谢谢您抽时间见我.

推荐答案

这将在任何级别提取包含基于目标UUID的键的整个对象:demo at db<>fiddle

SELECT jsonb_path_query(record_map, 
                        'strict $.**?(@.keyvalue().key==$target_id)',
                        jsonb_build_object('target_id',
                                           '7a9abf0d-a066-4466-a565-4e6d7a960a37'))
FROM private_notion
WHERE site_id = '45bf37be-ca0a-45eb-838b-015c7a89d47b';
jsonb_path_query
{
    "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
        "name": "block1",
        "child": {
            "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
                "name": "block2",
                "child": {
                    "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
                        "name": "block3",
                        "value": 3
                    }
                },
                "value": 2
            },
            "7a9abf0d-a066-4466-a565-4e6d7a960a38": {
                "name": "block4",
                "child": {
                    "7a9abf0d-a066-4466-a565-4e6d7a960a39": {
                        "name": "block5",
                        "child": {
                            "7a9abf0d-a066-4466-a565-4e6d7a960a40": {
                                "name": "block6",
                                "value": 6
                            }
                        },
                        "value": 5
                    }
                },
                "value": 4
            }
        },
        "value": 1
    }
}
{
    "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
        "name": "block2",
        "child": {
            "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
                "name": "block3",
                "value": 3
            }
        },
        "value": 2
    },
    "7a9abf0d-a066-4466-a565-4e6d7a960a38": {
        "name": "block4",
        "child": {
            "7a9abf0d-a066-4466-a565-4e6d7a960a39": {
                "name": "block5",
                "child": {
                    "7a9abf0d-a066-4466-a565-4e6d7a960a40": {
                        "name": "block6",
                        "value": 6
                    }
                },
                "value": 5
            }
        },
        "value": 4
    }
}
{
    "7a9abf0d-a066-4466-a565-4e6d7a960a37": {
        "name": "block3",
        "value": 3
    }
}

请注意取消嵌套后的对象重复:它们既单独出现,也出现在每个匹配的父 struct 中.

  1. JSONPath表达式需要用单引号引起来.这消除了语法错误:

    ERROR:  syntax error at or near "$"
    LINE 2:                         $.**.7a9abf0d-a066-4466-a565-4e6d7a9...
                                    ^
    
  2. JSONPath中基于UUID的键需要用双引号引起来.这将消除紧随其后的表达式中的一个问题:

    ERROR:  trailing junk after numeric literal at or near ".7a" of jsonpath input
    LINE 2:                         '$.**.7a9abf0d-a066-4466-a565-4e6d7a...
                                    ^
    
  3. 使用.**访问器时,默认为使用strict mode.

  4. 您可以使用SQLAlchemy JSONPath type来传递表达式.

Python相关问答推荐

如何使用Google Gemini API为单个提示生成多个响应?

Django管理面板显示字段最大长度而不是字段名称

连接两个具有不同标题的收件箱

Pandas 都是(),但有一个门槛

在Python中管理打开对话框

如何使用根据其他值相似的列从列表中获取的中间值填充空NaN数据

如何使用Python以编程方式判断和检索Angular网站的动态内容?

cv2.matchTemplate函数匹配失败

Scrapy和Great Expectations(great_expectations)—不合作

索引到 torch 张量,沿轴具有可变长度索引

为什么Django管理页面和我的页面的其他CSS文件和图片都找不到?'

可以bcrypts AES—256 GCM加密损坏ZIP文件吗?

matplotlib图中的复杂箭头形状

基于另一列的GROUP-BY聚合将列添加到Polars LazyFrame

当单元测试失败时,是否有一个惯例会抛出许多类似的错误消息?

如何在Gekko中处理跨矢量优化

Pandas:计数器的滚动和,复位

Polars定制函数返回多列

如何将一个文件的多列导入到Python中的同一数组中?

无法使用请求模块从网页上抓取一些产品的名称