PostgreSQL jsonb 更新多个嵌套字段

3

我在PostgreSQL数据库中有一个表,该表具有id字段和jsonb字段。 jsonb具有以下类似结构:

{
    "id": "some-id",
    "lastUpdated": "2018-10-24T10:36:29.174Z",
    "counters": {
        "counter1": 100,
        "counter2": 200
    }
}

我需要做的是更新lastModified和其中一个计数器:
def update(id: String, counter: Option[String])

例如,如果我执行update("some-id", Some("counter2")),我需要lastUpdated为当前日期时间,并且将counter2增加到201
我正在使用ScalikeJDBC,目前已经做到了这一步:
def update(id: String, counter: Option[String]): Option[ApiKey] = DB localTx { implicit session =>

val update =
  if(counter.isDefined)
    sqls"""'{"lastUpdated": ${DateTime.now()}, "counters": {'${counter.get}: COALESCE('counters'->>${counter.get},'0')::int'}'"""
  else
    sqls"""'{"lastUpdated": ${DateTime.now()}}'"""

sql"UPDATE apiKey SET content = content || $update WHERE id = $key".update().apply()
}

但我收到以下错误:

org.postgresql.util.PSQLException: 列索引超出范围:4,列数为3

我尝试了其他方法,但无法使其工作。是否可能将此写为单个查询?

这是一个有问题的fiddle,可用于测试 https://www.db-fiddle.com/f/bsteTUMXDGDSHp32fw2Zop/1


我没有使用ScalikeJDBC的经验,但Postgres查询可能如下所示:https://www.db-fiddle.com/f/mSugD6cNPc5EiujCtRjZjd/1 - Ionuț G. Stan
或者这样,以消除双重的 jsonb_set 调用:https://www.db-fiddle.com/f/mSugD6cNPc5EiujCtRjZjd/2 使用 || 的问题在于它不执行递归合并。 - Ionuț G. Stan
有道理,我会试一下的,谢谢! - Maria Livia
还有一个版本,只有在存在更新时才更新嵌套计数器:https://www.db-fiddle.com/f/mSugD6cNPc5EiujCtRjZjd/3 我意识到我无法真正回答你的问题,因为我不知道ScalikeJDBC是如何工作的,但我希望它能有所帮助。 - Ionuț G. Stan
实际上这非常有帮助,因为我终于成功编写了一个符合我的要求的查询:https://www.db-fiddle.com/f/UiGJyQo1Zp45N6nYeVZdK/0。不幸的是,我仍然无法在Scalike中使其工作,但这是一个开始。 - Maria Livia
1个回答

2

我对PostgreSQL的jsonb类型不是很了解,但似乎不可能将所有内容作为JDBC PreparedStatement中的绑定参数传递。我不得不说,您可能需要使用SQLSyntax.createUnsafely来绕过PreparedStatement,如下所示:

def update(id: String, counter: Option[String]): Unit = DB localTx { implicit session =>
  val now = java.time.ZonedDateTime.now.toOffsetDateTime.toString
  val q: SQLSyntax = counter match { 
    case Some(c) => 
      val content: String =
        s"""
        jsonb_set(
            content || '{"lastUsed": "${now}"}',
            '{counters, $c}',
            (COALESCE(content->'counters'->>'$c','0')::int + 1)::text::jsonb
        )
        """
      SQLSyntax.createUnsafely(s"""
    UPDATE
        example
    SET
        content = ${content}
    WHERE
        id = '$id';
    """)
    case _ => 
      throw new RuntimeException
  }
  sql"$q".update.apply()
}
update("73c1fa11-bf2f-42c9-80fd-c70ac123fca9", Some("counter2"))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接