surrealdb について
SurrealDB アプリケーション向けのクエリ生成スキルです。SurrealQL の作成に加え、スキーマや関係、認証、パフォーマンスの設計を支援します。
主な機能
- 01CRUD とグラフクエリの生成
- 02テーブル、フィールド、関係の設計
- 03認証と権限ルールの作成
- 04インデックスとトランザクションの計画
- 05クエリのデバッグと最適化
活用シーン
- 01SurrealDB のデータモデル作成
- 02グラフ走査やリアルタイムクエリの構築
- 03既存クエリの移行や最適化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tyrchen/ claude-skills --skill surrealdbインストール後、Claude Code で/surrealdbを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/graph_relationships.mdschema_patterns.mdsecurity_auth.mdsurrealql_syntax.md詳細
SurrealDB - 本番環境に対応したクエリ生成ツール
ドキュメント、グラフ、リレーショナルパターンを含むマルチモデルデータベースアプリケーション向けに、SurrealQL を使用して堅牢で高品質な本番環境対応の SurrealDB クエリと操作を生成します。
このスキルを使用する場面
ユーザーが次のことを求めている場合に、このスキルを使用してください。
- SurrealQL クエリの作成(SELECT、CREATE、UPDATE、DELETE、UPSERT)
- データベーススキーマの設計(SCHEMAFULL/SCHEMALESS テーブル、フィールド定義)
- リレーションシップのモデル化(レコードリンク、RELATE によるグラフエッジ、ネストデータ)
- 認証の実装(DEFINE ACCESS、SCOPE、権限、RBAC)
- パフォーマンス最適化のためのインデックスの作成
- DEFINE FUNCTION を使用したカスタム関数の作成
- LIVE クエリを使用したリアルタイムアプリケーションの構築
- データの整合性を保つためのトランザクションの実装
- SQL/NoSQL から SurrealDB への移行
- 既存の SurrealQL のデバッグまたは最適化
SurrealQL クイックリファレンス
基本的なステートメント構文
-- SELECT with graph traversal
SELECT *, ->friends->person AS mutual_friends FROM person:alice;
-- CREATE with specific ID
CREATE person:john SET name = 'John', age = 30;
-- UPDATE with operators
UPDATE person SET age += 1, tags += 'senior' WHERE age >= 65;
-- DELETE with conditions
DELETE person WHERE active = false;
-- UPSERT (create if not exists, update if exists)
UPSERT user:[email protected] SET email = '[email protected]', visits += 1;
-- RELATE for graph edges
RELATE person:alice->follows->person:bob SET since = time::now();
データ型
-- Basic types
string, int, float, bool, datetime, duration, decimal, uuid
-- Complex types
array, object, record<table>, option<type>
-- Special types
geometry (point, line, polygon), bytes, null, none
基本的な関数
-- Time functions
time::now() -- Current timestamp
time::floor(datetime, 1d) -- Floor to day
duration::from::days(7) -- Create duration
-- String functions
string::is::email($value) -- Validate email
string::concat($a, ' ', $b) -- Concatenate
string::split($s, ',') -- Split to array
string::lowercase($s) -- Lowercase
-- Array functions
array::len($arr) -- Array length
array::push($arr, $item) -- Add to array
array::distinct($arr) -- Remove duplicates
array::flatten($arr) -- Flatten nested arrays
-- Crypto functions
crypto::argon2::generate($password) -- Hash password
crypto::argon2::compare($hash, $password) -- Verify password
-- Math functions
math::sum($arr) -- Sum values
math::mean($arr) -- Average
math::max($arr) -- Maximum
-- Record functions
record::id($record) -- Get record ID
record::table($record) -- Get table name
-- Type functions
type::is::string($val) -- Type check
type::thing($table, $id) -- Create record ID
SurrealDB クエリ作成の手順
ステップ 1:データモデルを理解する
SurrealQL を記述する前に、次の点を確認してください。
- データ構造はどのようなものですか?(ドキュメント、グラフ、リレーショナル、またはハイブリッドですか?)
- どのようなリレーションシップがありますか?(1 対多、多対多、グラフのトラバーサルですか?)
- どのようなアクセスパターンですか?(読み取り中心、書き込み中心、リアルタイムですか?)
- どのような一貫性要件がありますか?(結果整合性、強整合性、トランザクション整合性ですか?)
ステップ 2:スキーマ戦略を選択する
SCHEMAFULL - 次の場合に使用します。
- データ構造が明確に定義されている
- 型安全性が重要である
- 検証ルールが必要である
- 本番環境のワークロードである
SCHEMALESS - 次の場合に使用します。
- 迅速なプロトタイピングを行う
- データ構造が進化する
- 柔軟なドキュメントストレージが必要である
-- SCHEMAFULL with validation
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON user TYPE string ASSERT string::is::email($value);
DEFINE FIELD password ON user TYPE string;
DEFINE FIELD created_at ON user TYPE datetime DEFAULT time::now();
DEFINE FIELD status ON user TYPE string DEFAULT 'active'
ASSERT $value IN ['active', 'inactive', 'suspended'];
-- SCHEMALESS (flexible)
DEFINE TABLE event SCHEMALESS;
ステップ 3:リレーションシップを設計する
適切なリレーションシップモデルを選択します。
レコードリンク - シンプルで直接的な参照:
-- One-to-many via array of record IDs
CREATE user:alice SET
name = 'Alice',
friends = [user:bob, user:carol];
-- Fetch with link resolution
SELECT *, friends.* FROM user:alice;
グラフエッジ(RELATE) - メタデータを伴う複雑なリレーションシップ:
-- Create relationship with properties
RELATE user:alice->follows->user:bob SET
since = time::now(),
notifications = true;
-- Traverse graph
SELECT
->follows->user AS following,
<-follows<-user AS followers
FROM user:alice;
-- Multi-hop traversal
SELECT ->follows->user->follows->user AS friends_of_friends
FROM user:alice;
埋め込みドキュメント - 非正規化データ:
CREATE order SET
customer = { name: 'Alice', email: '[email protected]' },
items = [
{ product: 'Widget', quantity: 2, price: 29.99 },
{ product: 'Gadget', quantity: 1, price: 49.99 }
],
total = 109.97;
ステップ 4:認証を実装する
DEFINE ACCESS によるレコードレベルのアクセス制御:
-- Define user access
DEFINE ACCESS user_auth ON DATABASE TYPE RECORD
SIGNUP (
CREATE user SET
email = $email,
password = crypto::argon2::generate($password),
created_at = time::now()
)
SIGNIN (
SELECT * FROM user
WHERE email = $email
AND crypto::argon2::compare(password, $password)
)
DURATION FOR TOKEN 24h, FOR SESSION 7d;
-- Define table permissions
DEFINE TABLE post SCHEMAFULL
PERMISSIONS
FOR select WHERE published = true OR author = $auth.id
FOR create WHERE $auth.id != NONE
FOR update WHERE author = $auth.id
FOR delete WHERE author = $auth.id;
ステップ 5:インデックスで最適化する
-- Unique index
DEFINE INDEX unique_email ON user FIELDS email UNIQUE;
-- Composite index
DEFINE INDEX order_lookup ON order FIELDS customer, status;
-- Full-text search index
DEFINE ANALYZER english TOKENIZERS blank FILTERS lowercase, snowball(english);
DEFINE INDEX content_search ON article FIELDS content
SEARCH ANALYZER english BM25;
-- Verify index usage
EXPLAIN SELECT * FROM user WHERE email = '[email protected]';
ステップ 6:トランザクションを記述する
BEGIN TRANSACTION;
-- Transfer funds between accounts
LET $amount = 100;
UPDATE account:alice SET balance -= $amount;
UPDATE account:bob SET balance += $amount;
CREATE transaction SET
from = account:alice,
to = account:bob,
amount = $amount,
timestamp = time::now();
COMMIT TRANSACTION;
一般的なクエリパターン
CRUD 操作
検証付きの作成:
CREATE user CONTENT {
email: '[email protected]',
name: 'John Doe',
roles: ['user'],
metadata: {
source: 'signup',
ip: '192.168.1.1'
}
};
フィルタリングとページネーションを伴う選択:
SELECT * FROM user
WHERE status = 'active'
AND created_at > time::now() - 30d
ORDER BY created_at DESC
LIMIT 20
START 0;
演算子を使用した更新:
-- Increment/decrement
UPDATE user:alice SET login_count += 1;
-- Array manipulation
UPDATE user:alice SET tags += 'premium', tags -= 'trial';
-- Conditional update
UPDATE user SET status = 'inactive'
WHERE last_login < time::now() - 90d;
アップサートパターン:
UPSERT user:[email protected] SET
email = '[email protected]',
last_seen = time::now(),
visits += 1;
グラフクエリ
ソーシャルネットワーク - 友達の友達:
SELECT
id,
name,
array::distinct(->follows->user->follows->user) AS suggested_friends
FROM user:alice
WHERE suggested_friends != user:alice;
E コマース - 商品のおすすめ:
-- Find products bought by users who bought this product
SELECT
<-purchased<-user->purchased->product AS related_products,
count() AS frequency
FROM product:widget123
GROUP BY related_products
ORDER BY frequency DESC
LIMIT 10;
ナレッジグラフ - 再帰的なトラバーサル:
-- Find all ancestors up to 5 levels
SELECT
->parent->(1..5)->category AS ancestors
FROM category:electronics;
分析クエリ
集計:
SELECT
status,
count() AS total,
math::mean(age) AS avg_age,
math::min(created_at) AS first_created
FROM user
GROUP BY status;
時系列分析:
SELECT
time::floor(timestamp, 1h) AS hour,
count() AS events,
math::sum(value) AS total_value
FROM metrics
WHERE timestamp > time::now() - 24h
GROUP BY hour
ORDER BY hour;
サブクエリと計算フィールド
SELECT 内のサブクエリ:
SELECT
*,
(SELECT count() FROM post WHERE author = $parent.id) AS post_count,
(SELECT VALUE title FROM post WHERE author = $parent.id LIMIT 5) AS recent_posts
FROM user;
複雑なクエリでの LET(CTE の代替):
LET $active_users = (SELECT id FROM user WHERE status = 'active');
LET $recent_orders = (SELECT * FROM order WHERE created_at > time::now() - 7d);
SELECT * FROM $recent_orders
WHERE customer IN $active_users.id;
スキーマ設計パターン
ネストオブジェクトを含むユーザープロフィール
DEFINE TABLE user SCHEMAFULL;
DEFINE FIELD email ON user TYPE string ASSERT string::is::email($value);
DEFINE FIELD password ON user TYPE string;
DEFINE FIELD profile ON user TYPE object;
DEFINE FIELD profile.name ON user TYPE string;
DEFINE FIELD profile.avatar ON user TYPE option<string>;
DEFINE FIELD profile.bio ON user TYPE option<string>;
DEFINE FIELD settings ON user TYPE object DEFAULT {};
DEFINE FIELD settings.notifications ON user TYPE bool DEFAULT true;
DEFINE FIELD settings.theme ON user TYPE string DEFAULT 'light';
DEFINE FIELD created_at ON user TYPE datetime DEFAULT time::now();
DEFINE FIELD updated_at ON user TYPE datetime VALUE time::now();
DEFINE INDEX unique_email ON user FIELDS email UNIQUE;
E コマーススキーマ
-- Products
DEFINE TABLE product SCHEMAFULL;
DEFINE FIELD name ON product TYPE string;
DEFINE FIELD description ON product TYPE string;
DEFINE FIELD price ON product TYPE decimal;
DEFINE FIELD inventory ON product TYPE int DEFAULT 0;
DEFINE FIELD categories ON product TYPE array<record<category>>;
DEFINE FIELD active ON product TYPE bool DEFAULT true;
DEFINE INDEX product_search ON product FIELDS name, description
SEARCH ANALYZER blank BM25;
-- Orders
DEFINE TABLE order SCHEMAFULL;
DEFINE FIELD customer ON order TYPE record<user>;
DEFINE FIELD items ON order TYPE array;
DEFINE FIELD items.*.product ON order TYPE record<product>;
DEFINE FIELD items.*.quantity ON order TYPE int;
DEFINE FIELD items.*.price ON order TYPE decimal;
DEFINE FIELD status ON order TYPE string DEFAULT 'pending'
ASSERT $value IN ['pending', 'processing', 'shipped', 'delivered', 'cancelled'];
DEFINE FIELD total ON order TYPE decimal;
DEFINE FIELD created_at ON order TYPE datetime DEFAULT time::now();
DEFINE INDEX order_customer ON order FIELDS customer;
DEFINE INDEX order_status ON order FIELDS status, created_at;
グラフリレーションシップスキーマ
-- Define edge table
DEFINE TABLE follows SCHEMAFULL TYPE RELATION;
DEFINE FIELD in ON follows TYPE record<user>;
DEFINE FIELD out ON follows TYPE record<user>;
DEFINE FIELD since ON follows TYPE datetime DEFAULT time::now();
DEFINE FIELD notifications ON follows TYPE bool DEFAULT true;
-- Prevent duplicate follows
DEFINE INDEX unique_follow ON follows FIELDS in, out UNIQUE;
カスタム関数
-- Calculate user engagement score
DEFINE FUNCTION fn::engagement_score($user_id: record<user>) {
LET $posts = (SELECT count() FROM post WHERE author = $user_id);
LET $comments = (SELECT count() FROM comment WHERE author = $user_id);
LET $likes_received = (SELECT count() FROM like WHERE post.author = $user_id);
RETURN ($posts * 5) + ($comments * 2) + $likes_received;
};
-- Usage
SELECT *, fn::engagement_score(id) AS score FROM user;
-- Validate and normalize email
DEFINE FUNCTION fn::normalize_email($email: string) {
IF !string::is::email($email) {
THROW "Invalid email format";
};
RETURN string::lowercase(string::trim($email));
};
-- Pagination helper
DEFINE FUNCTION fn::paginate($table: string, $page: int, $per_page: int) {
LET $offset = ($page - 1) * $per_page;
RETURN (SELECT * FROM type::table($table) LIMIT $per_page START $offset);
};
リアルタイム(LIVE クエリ)
-- Subscribe to changes on a table
LIVE SELECT * FROM post WHERE published = true;
-- Subscribe to specific record changes
LIVE SELECT * FROM user:alice;
-- Subscribe with graph traversal
LIVE SELECT *, ->comments->comment AS comments FROM post;
-- Kill a live query
KILL $live_query_id;
パフォーマンスに関するベストプラクティス
1. スキャンではなく特定のレコード ID を使用する
-- FAST: Direct ID access
SELECT * FROM user:alice;
-- SLOW: Table scan
SELECT * FROM user WHERE id = 'alice';
2. 必要なフィールドのみを選択する
-- BETTER: Specific fields
SELECT name, email FROM user;
-- AVOID: All fields when not needed
SELECT * FROM user;
3. インデックスを効果的に使用する
-- Create index for common queries
DEFINE INDEX active_users ON user FIELDS status, created_at;
-- Query uses index
SELECT * FROM user
WHERE status = 'active'
ORDER BY created_at DESC;
4. 操作を一括処理する
-- BETTER: Single batch insert
INSERT INTO log [
{ level: 'info', message: 'Start' },
{ level: 'info', message: 'Processing' },
{ level: 'info', message: 'Complete' }
];
-- AVOID: Multiple separate inserts
5. 関連する操作にはトランザクションを使用する
BEGIN TRANSACTION;
-- Multiple related operations
COMMIT TRANSACTION;
避けるべき一般的なアンチパターン
1. フィールドの存在確認が不足している
-- WRONG: Field might not exist
SELECT * FROM user WHERE profile.name = 'John';
-- CORRECT: Check existence first (in application logic)
-- Or use SCHEMAFULL tables with proper defaults
2. N+1 クエリ問題
-- WRONG: Fetching related data in loops
-- (in application: for each user, fetch their posts)
-- CORRECT: Use record links and fetch in one query
SELECT *, posts.* FROM user FETCH posts;
3. 適切なリレーションシップモデルを使用していない
-- WRONG: String IDs for relationships
CREATE user SET friend_ids = ['alice', 'bob'];
-- CORRECT: Record links
CREATE user SET friends = [user:alice, user:bob];
4. グラフトラバーサルでデータを過剰に取得する
-- WRONG: Unbounded traversal
SELECT ->*->* FROM user:alice;
-- CORRECT: Bounded traversal
SELECT ->(1..3)->follows->user FROM user:alice;
デバッグとテスト
クエリの実行を説明する
EXPLAIN SELECT * FROM user WHERE email = '[email protected]';
EXPLAIN FULL SELECT * FROM user WHERE email = '[email protected]';
テーブル情報を確認する
INFO FOR TABLE user;
INFO FOR DB;
INFO FOR NS;
パラメーターを使用してクエリをテストする
LET $email = '[email protected]';
SELECT * FROM user WHERE email = $email;
出力形式
SurrealDB クエリを生成する際は、必ず次の内容を提示してください。
- 適切な構文を用いた SurrealQL ステートメント一式
- テーブル/フィールドを作成する場合はスキーマ定義
- クエリパターンに応じたインデックスの推奨
- テスト用のサンプルデータ
- 設計上の判断に関する説明
- 関連する場合はパフォーマンスに関する考慮事項
リファレンスファイル
- SurrealQL 構文リファレンス - 言語の完全なリファレンス
- スキーマパターン - 一般的なスキーマ設計
- グラフのリレーションシップ - リレーションシップのモデリングパターン
- セキュリティと認証 - 認証と権限
バージョン互換性
- SurrealDB 2.x: 最新の機能、GraphQL のサポート、パフォーマンスの向上
- SurrealDB 1.x: レガシーバージョン。
scopeをaccessの代わりに使用します
クエリを生成する前に、対象の SurrealDB のバージョンを必ず確認してください。