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

EdgeDB JavaScript客户端更新关联表报错,无需开启allow_user_specified_id的解决方法

EdgeDB关联更新social_media表的错误解决

问题背景

尝试通过basic_details关联更新social_media表,使用以下代码:

import * as edgedb from 'edgedb';
import e from '../../dbschema/edgeql-js';

const client = edgedb.createClient();

let query = await e.params({ items: e.json }, (params) => {
            return e.for(e.json_array_unpack(params.items), (item) => {
                return e.update(e.basic_details, () => ({
                    filter_single: { id: resumeId },
                    set: {
                        name: e.cast(e.str, item.name),
                        email: e.cast(e.str, item.email),
                        phone: e.cast(e.str, item.phone),
                        designation: e.cast(e.str, item.designation),
                        summary: e.cast(e.str, item.summary),
                        template_design_type: e.cast(e.str, item.template_design_type),
                        address: e.update(e.address, () => ({
                            filter_single: { id: e.cast(e.uuid, item.address.id) },
                            set: {
                                address: e.cast(e.str, item.address.address),
                                city: e.cast(e.str, item.address.city),
                                country: e.cast(e.str, item.address.country),
                                state: e.cast(e.str, item.address.state),
                                postal_code: e.cast(e.str, item.address.postal_code)
                            }
                        })),
                        social_media: e.for(
                            e.op(
                                "distinct", e.json_array_unpack(item.social_media)), (sm) => {
                                    return e.insert(e.social_media, {
                                        id: e.cast(e.uuid, sm.id),
                                        platform: e.cast(e.str, sm.platform),
                                        url: e.cast(e.str, sm.url)
                                    }).unlessConflict((socail) => ({
                                        on: socail.id,
                                        else: e.update(e.social_media, () => ({
                                            filter_single: {id: e.cast(e.uuid, socail.id)},
                                            set: {
                                                platform: e.cast(e.str, socail.platform),
                                                url: e.cast(e.str, socail.url)
                                            }
                                        }))
                                    }))
                                }
                        )
                    }
                }));
            });
        });
        const result = await query.run(client, { items: Array.isArray(data)? data : [data] });

执行后触发第一个错误:

web-1 | QueryError: cannot assign to property 'id'
web-1 | |
web-1 | 33 | id := std::uuid(__forVar__1["id"]),
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | Hint: consider enabling the "allow_user_specified_id" configuration parameter to allow setting custom object ids

移除social_media插入部分的id字段后,触发第二个错误:

web-1 | UnsupportedFeatureError: INSERT UNLESS CONFLICT ON does not support volatile properties
web-1 | |
web-1 | 32 | (INSERT default::social_media {
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 33 | platform := std::str(__forVar__1["platform"]),
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 34 | url := std::str(__forVar__1["url"])
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 35 | }
web-1 | | ^^^^^^^^^
web-1 | 36 | UNLESS CONFLICT ON default::social_media.id
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 37 | ELSE ((WITH
web-1 | | ^^^^^^^^^^^^^^^^^^^
web-1 | 38 | __scope_2_defaultsocialmedia := default::social_media
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 39 | UPDATE __scope_2_defaultsocialmedia
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 40 | FILTER (__scope_2_defaultsocialmedia.id = std::uuid(DETACHED default::social_media.id))
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 41 | SET {
web-1 | | ^^^^^^^^^^^^^
web-1 | 42 | platform := std::str(DETACHED default::social_media.platform),
web-1 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web-1 | 43 | url := std::str(DETACHED default::social_media.url)
web-1 | ^

错误原因分析

  1. 第一个错误:EdgeDB默认禁止手动赋值id字段(这是系统自动生成的UUID),除非开启allow_user_specified_id配置,而你不想开启该配置,所以不能直接设置id。
  2. 第二个错误:id属于volatile属性(每次查询会生成新值的属性),INSERT UNLESS CONFLICT ON不支持将volatile属性作为冲突判断条件,因此不能用id来做冲突检测。

解决方案

改用e.if_exists来实现social_media的 upsert 逻辑:先根据传入的sm.id查询是否存在对应的记录,存在则更新,不存在则插入(由EdgeDB自动生成id)。同时调整basic_details关联social_media的逻辑,确保关联正确。

修改后的代码:

import * as edgedb from 'edgedb';
import e from '../../dbschema/edgeql-js';

const client = edgedb.createClient();

let query = await e.params({ items: e.json }, (params) => {
  return e.for(e.json_array_unpack(params.items), (item) => {
    // 处理每个social_media的upsert逻辑
    const socialMediaUpserts = e.for(
      e.json_array_unpack(item.social_media),
      (sm) => {
        // 尝试根据传入的id查询已有记录
        const existingSM = e.select(e.social_media, () => ({
          filter_single: { id: e.cast(e.uuid, sm.id) }
        }));
        // 存在则更新,不存在则插入(不指定id,由系统生成)
        return e.if_exists(
          existingSM,
          (existing) => e.update(existing, () => ({
            set: {
              platform: e.cast(e.str, sm.platform),
              url: e.cast(e.str, sm.url)
            }
          })),
          () => e.insert(e.social_media, {
            platform: e.cast(e.str, sm.platform),
            url: e.cast(e.str, sm.url)
          })
        );
      }
    );

    return e.update(e.basic_details, () => ({
      filter_single: { id: resumeId },
      set: {
        name: e.cast(e.str, item.name),
        email: e.cast(e.str, item.email),
        phone: e.cast(e.str, item.phone),
        designation: e.cast(e.str, item.designation),
        summary: e.cast(e.str, item.summary),
        template_design_type: e.cast(e.str, item.template_design_type),
        address: e.update(e.address, () => ({
          filter_single: { id: e.cast(e.uuid, item.address.id) },
          set: {
            address: e.cast(e.str, item.address.address),
            city: e.cast(e.str, item.address.city),
            country: e.cast(e.str, item.address.country),
            state: e.cast(e.str, item.address.state),
            postal_code: e.cast(e.str, item.address.postal_code)
          }
        })),
        // 将处理后的social_media关联到basic_details
        social_media: socialMediaUpserts
      }
    }));
  });
});

const result = await query.run(client, { items: Array.isArray(data) ? data : [data] });

关键修改点

  • 移除手动设置id的代码,由EdgeDB自动生成新记录的id。
  • 用e.if_exists替代INSERT UNLESS CONFLICT,避免使用volatile属性id作为冲突条件。
  • 将social_media的upsert逻辑抽离,使代码结构更清晰,同时确保关联到basic_details的是处理后的有效记录。

如果你的业务场景中,social_media的唯一标识不是id(比如同一basic_details下platform唯一),可以调整查询条件为:

const existingSM = e.select(e.social_media, () => ({
  filter_single: {
    platform: e.cast(e.str, sm.platform),
    basic_details: e.select(e.basic_details, () => ({ filter_single: { id: resumeId } }))
  }
}));

这样可以避免依赖传入的id,直接通过业务唯一键做upsert。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:22:32