You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust Diesel ORM中适配PostgreSQL jsonpath类型

在Diesel ORM中使用PostgreSQL的jsonb_path_exists函数

问题背景

最初尝试定义jsonb_path_exists函数时,将第二个参数指定为Text类型:

diesel::sql_function! {
    /// https://www.postgresql.org/docs/current/functions-json.html
    fn jsonb_path_exists(
        jsonb: diesel::sql_types::Nullable<diesel::sql_types::Jsonb>,
        path: diesel::sql_types::Text,
    ) -> diesel::sql_types::Bool;
}

调用时触发运行时错误:

mytable::table.filter(
    jsonb_path_exists(
        activities::activity_object,
        format!(r#"$.**.id ? (@ == "some-uuid")"#),
    ),
)

错误信息:diesel error: function jsonb_path_exists(jsonb, text) does not exist

核心原因是PostgreSQL原生的jsonb_path_exists签名中,第二个参数类型为jsonpath而非text:

jsonb_path_exists ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

解决方案

1. 定义对应PostgreSQL的jsonpath SQL类型

先创建Diesel可识别的JsonPathType,对应PostgreSQL的jsonpath类型(OID可通过SELECT typname, oid, typarray FROM pg_type WHERE typname = 'jsonpath';查询):

#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)]
#[diesel(postgres_type(oid = 4072, array_oid = 4073))]
pub struct JsonPathType;

2. 实现JsonPath包装结构体的表达式转换与序列化

创建JsonPath结构体包裹路径字符串,并实现Diesel所需的AsExpression和ToSql trait:

use diesel::{
    deserialize::{self, FromSql},
    expression::{AsExpression, Expression},
    pg::Pg,
    serialize::{self, Output, ToSql},
    sql_types, QueryId,
};

#[derive(Debug, Clone, PartialEq, Eq, QueryId)]
pub struct JsonPath(pub String);

// 实现AsExpression,让JsonPath可作为JsonPathType的表达式
impl AsExpression<JsonPathType> for JsonPath {
    type Expression = diesel::expression::bound::Bound<JsonPathType, Self>;

    fn as_expression(self) -> Self::Expression {
        diesel::expression::bound::Bound::new(self)
    }
}

impl<'a> AsExpression<JsonPathType> for &'a JsonPath {
    type Expression = diesel::expression::bound::Bound<JsonPathType, &'a JsonPath>;

    fn as_expression(self) -> Self::Expression {
        diesel::expression::bound::Bound::new(self)
    }
}

// 实现ToSql,让Diesel将JsonPath序列化为PostgreSQL的jsonpath类型
impl ToSql<JsonPathType, Pg> for JsonPath {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
        out.write_all(self.0.as_bytes())?;
        Ok(serialize::IsNull::No)
    }
}

// 可选:如需从数据库读取jsonpath类型,实现FromSql
impl FromSql<JsonPathType, Pg> for JsonPath {
    fn from_sql(bytes: diesel::backend::RawValue<'_, Pg>) -> deserialize::Result<Self> {
        let s = String::from_utf8(bytes.as_bytes().to_vec())?;
        Ok(JsonPath(s))
    }
}

3. 重新定义jsonb_path_exists函数

使用正确的JsonPathType作为第二个参数类型:

diesel::sql_function! {
    fn jsonb_path_exists(
        jsonb: diesel::sql_types::Nullable<diesel::sql_types::Jsonb>,
        path: JsonPathType,
    ) -> diesel::sql_types::Bool;
}

4. 调用示例

现在可正常使用jsonb_path_exists函数,传入JsonPath包装后的路径:

mytable::table.filter(
    jsonb_path_exists(
        activities::activity_object,
        JsonPath(format!(r#"$.**.id ? (@ == "some-uuid")"#)),
    ),
)

内容的提问来源于stack exchange,提问作者porkbrain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 20:10:27