【ITニュース解説】Enforcing XOR (Either/Or) Fields in SurrealDB

2025年09月05日に「Dev.to」が公開したITニュース「Enforcing XOR (Either/Or) Fields in SurrealDB」について初心者にもわかりやすいように丁寧に解説しています。

作成日: 更新日:

ITニュース概要

SurrealDBで、あるフィールドが2つのうちどちらか一方のみを持つXOR制約を実装する方法を紹介。リポジトリのオーナーをユーザーまたは組織のどちらか一方に限定する例を挙げ、DEFINE FIELD文とTHROW文を使って制約を定義する。SQLのCHECK制約と同等の機能を、より簡潔に実現する別の方法も提示。データベーススキーマ自体に制約を組み込むことで、データの整合性を保つ。

出典: Enforcing XOR (Either/Or) Fields in SurrealDB | Dev.to公開日:

ITニュース解説

SurrealDBでXOR制約(どちらか一方)を実装する方法

データベース設計において、レコードが2つのフィールドのうち1つだけを持ち、両方同時またはどちらも持たない状況がある。たとえば、リポジトリはユーザーまたは組織のいずれかが所有するが、両方同時、またはどちらでもない、ということはありえない。これはXOR(排他的論理和)制約と呼ばれる。

この状況は、ERモデリングではアーク関係としても知られている。ER図では、関係線に弧を描くことで示される。

SurrealDBでアーク関係をモデル化する方法を、リポジトリテーブルのスキーマ定義を通して説明する。

まず、いくつかのDEFINEステートメントを使用してスキーマを定義する。

1DEFINE TABLE repository SCHEMAFULL;
2
3DEFINE FIELD name ON repository TYPE string;
4-- XORチェックの前にフィールドを省略できるように`option<table_name>`を使用
5DEFINE FIELD user         ON repository TYPE option<record<user>>;
6DEFINE FIELD organization ON repository TYPE option<record<organization>>;

次に、XORチェックを行う。VALUE句を使用して値を設定し、XOR制約に違反した場合にTHROWを使用してエラーを返す。

1-- SurrealDBのフィールドはキーと値のペアで表現される
2-- `VALUE`はフィールドの生の "値" を設定するために使用され、"キー" は省略される
3DEFINE FIELD owner_type ON repository VALUE {
4    -- 1. どちらも設定されていないかを確認
5    IF user = NONE AND organization = NONE {
6        THROW "リポジトリには、ユーザーまたは組織のオーナーが必要"
7    }
8    -- 2. 両方設定されているかを確認
9    ELSE IF user != NONE AND organization != NONE {
10        THROW "リポジトリはユーザーと組織の両方のオーナーを持つことはできない"
11    };
12    -- 3. (オプション) バリデーションが成功した場合、オーナータイプを返す
13    RETURN IF user != NONE {
14        'user';
15    } ELSE {
16        'organization';
17    };
18};

考えられる4つのケースをテストする。

1-- ユーザーオーナーのみを持つリポジトリの作成
2CREATE repository SET
3    name = 'my_repo',
4    user = user:abdo;
5
6-- 組織オーナーのみを持つリポジトリの作成
7CREATE repository SET
8    name = 'my_repo',
9    organization = organization:abdo;
10
11-- エラー: ユーザーと組織の両方が設定されているため失敗する
12CREATE repository SET
13    name = 'hamadas_repo',
14    user = user:hamada,
15    organization = organization:hamada_org;
16-- スロー: 'エラーが発生しました: リポジトリはユーザーと組織の両方のオーナーを持つことはできません'
17
18-- エラー: ユーザーも組織も設定されていないため失敗する
19CREATE repository SET
20    name = 'hamada_repo';
21-- スロー: 'エラーが発生しました: リポジトリには、ユーザーまたは組織のオーナーが必要です'

SurrealDB Discordでjimpexによって提案された、より簡潔なアプローチがある。

1DEFINE FIELD owner ON repository TYPE record<user | organization>;
2DEFINE FIELD owner_type ON repository VALUE record::tb(owner);
3
4CREATE ONLY repository SET owner = user:andy;
5CREATE ONLY repository SET owner = organization:dunder_mifflin;
6CREATE ONLY repository SET owner = fail:test;

これは、ownerフィールドをユーザーまたは組織のレコードのいずれかとして定義し、owner_typeフィールドをownerフィールドのタイプに基づいて自動的に設定する方法だ。

リレーショナルデータベースの知識がある人向けに、SQLでの同等のロジックを示す。

1CREATE TABLE users (
2    id INT PRIMARY KEY,
3    name VARCHAR(255) NOT NULL
4);
5
6CREATE TABLE organizations (
7    id INT PRIMARY KEY,
8    name VARCHAR(255) NOT NULL
9);
10
11CREATE TABLE repository (
12    id INT PRIMARY KEY,
13    name VARCHAR(255) NOT NULL,
14
15    user_id INT,
16    organization_id INT,
17
18    FOREIGN KEY (user_id) REFERENCES users(id),
19    FOREIGN KEY (organization_id) REFERENCES organizations(id),
20
21    CONSTRAINT owner_xor_check CHECK (
22        (user_id IS NOT NULL AND organization_id IS NULL)
23        OR
24        (user_id IS NULL AND organization_id IS NOT NULL)
25    )
26);

このアプローチでは、バリデーションロジックをアプリケーションからスキーマ自体に移動するため、データベースが信頼できる唯一の情報源となる。CHECK制約は、データベースレベルでデータの整合性を保証するのに役立つ。

この記事では、アーク関係とSurrealDBでXORフィールドを実装する方法について解説した。

【ITニュース解説】Enforcing XOR (Either/Or) Fields in SurrealDB | いっしー@Webエンジニア