neo4j-cypher-skill について
neo4j-cypher-skillは、Neo4j利用者向けにCypherクエリの作成、最適化、デバッグを支援し、一般的なクエリパターンを扱います。実際のスキーマを先に確認し、ラベル、リレーションシップ型、プロパティを推測で補わない方針です。
主な機能
- 01Cypherクエリの作成とデバッグ
- 02スキーマに基づくラベル・プロパティ確認
- 03パスマッチングとサブクエリのパターン
- 04一括書き込みとクエリ最適化のガイド
- 05ベクトル・全文検索の構文を網羅
活用シーン
- 01グラフパターンマッチングクエリの作成
- 02Cypherクエリのエラー調査
- 03パストラバーサルやサブクエリの作成
- 04CSVの一括書き込みの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ neo4j-contrib/ neo4j-skills --skill neo4j-cypher-skillインストール後、Claude Code で/neo4j-cypher-skillを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/advanced-patterns.mdapoc.mdcypher-syntax.mdgraph-type.mdindexes.mdperformance.mdschema-guardrail.mdsyntax-traps.mdscripts/define_schema.pygenerate_schema.pyimport_neo4j_schema.py詳細
使用する場面
- Cypher クエリの作成、最適化、デバッグ
- グラフパターンマッチング、QPE、可変長パス
- ベクトル/全文検索、サブクエリ、バッチ書き込み、LOAD CSV
使用しない場面
- ドライバーの移行/API の変更 →
neo4j-migration-skill - DB 管理(ユーザー、設定、バックアップ) →
neo4j-cli-tools-skill - ベクトルと全文検索、またはその他のランキング対象ソースを組み合わせたハイブリッド検索 →
neo4j-vector-index-skill
GQL 準拠に関する注意:LET、FINISH、FILTER、INSERT は有効な Cypher 25 の句です(GQL 準拠を通じて導入され、主に Neo4j 2025.06 で利用可能になりました)。古いバージョンでは、代わりに WITH/(RETURN を省略)/WHERE/CREATE を使用してください。INSERT では、複数のラベルを & で区切る必要があり、動的なラベル/型はサポートされません。
事前確認
| ? | 判明している場合 | 不明な場合 |
|---|---|---|
プロジェクト内に <db-name>-schema.json がある | そのまま使用 — ライブ環境の調査は省略 | — |
| スキーマ(コンテキストまたはライブ DB から取得) | そのまま使用 | スキーマ優先プロトコルを実行 |
| Neo4j のバージョン | そのバージョンの機能を使用 | 2025.01 の安全な機能セットをデフォルトにする |
| 実行する(生成のみではない)か? | EXPLAIN と書き込みゲートを使用 | クエリが未検証であることを明記 |
スキーマが不明でツールもない場合は、コードブロックの外に、実行できないスケッチを作成します:
(<SOURCE_LABEL> {<KEY>: $value})-[:<REL_TYPE>]->(<TARGET_LABEL>)
推測した名前は絶対に補わないでください。もっともらしい推測は、そのままコピーされてしまいます。
デフォルト — すべてのクエリに適用
CYPHER 25— 最初のトークンとして指定します。UNIONの後やサブクエリ内では繰り返しません- スキーマを優先 — 作成前に確認します。スキーマがプロンプトに含まれている場合は、そのまま使用します
- 制約付きキーでのみ
MERGEを使用します。リレーションシップのMERGEは、すでにバインドされている端点に対してのみ使用します - ラベルのない
MATCH (n)は、バインド済みの場合、または後続にWHERE n:$($label)がある場合を除き禁止です - 探索的な読み取りではデフォルトで
LIMIT 25を指定します。高カーディナリティの操作(可変長トラバーサル、ファンアウトする MATCH、直積)の前にWITH n LIMITを適用します - コメントには
//のみを使用します —--は SQL の記法であり、無効です REPEATABLE ELEMENTS/DIFFERENT RELATIONSHIPSはパターンの末尾ではなく、MATCHの後に指定しますSHOWコマンドではWHEREより前にYIELDを指定します。一般的な Cypher 句(UNION/RETURNを含む)と組み合わせられます [2026.05] — ただし、SHOW DATABASESには引き続き system DB が必要です(USE systemを使用)。systemDB でのCALLでは、YIELDの後にWHEREを指定します [2026.07]- インラインのノード述語
(:Label WHERE p=x)は、MATCH内でのみ有効です WHEREを単独のUNWINDの後に置くことはできません —WITH x WHEREを使用します(a)-[:R]-(b)— 無向マッチは両方向にマッチし、二重カウントします。不明な場合を除き、有向を使用しますDETACH DELETE— ノードにリレーションシップがある場合、通常のDELETEはエラーになります
スタイル
| 要素 | 規則 |
|---|---|
| ノードラベル | PascalCase :Person |
| リレーションシップの型 | SCREAMING_SNAKE_CASE :KNOWS |
| プロパティ/変数 | camelCase firstName |
| 句 | 大文字 MATCH |
| ブール値/null | 小文字 true false null |
| 文字列 | シングルクォート。' を含む場合のみダブルクォート |
スキーマが正です。例の
:Person、:KNOWS、nameは説明用です — スキーマに記載された実際の名前に置き換えてください。
スキーマ優先プロトコル
優先順位:
-
プロジェクト内のどこかに
<db-name>-schema.jsonがある場合 → 直接読み込み、ファイル名とschema_retrieved_atを明示し、ライブ検査は省略します。大幅に古く、データベースに接続できる場合は、再取得を提案します。全ルール:references/schema-guardrail.md。- 存在確認 — ラベル/リレーションシップタイプ/プロパティはスキーマに含まれている必要があります。質問する前に同義語の解決を試みます
- プロパティ型 — まず意図を推測します(例:文字列と INTEGER の違いは NULL チェックかもしれません)。不明な場合にのみ質問します
- リレーションシップの方向 — 方向が誤っている場合は、黙って修正してその旨を記載します
- 同義語の対応付け — 一意に決まる場合は黙って解決します。曖昧な場合は最も可能性の高いものを選び、その旨を記載します。解決できない場合は質問します
スクリプト:
generate_schema.py(ライブ DB + APOC)、define_schema.py(DB 不要)、import_neo4j_schema.py(neo4j-graphrag-python、graph-schema-introspector、graph-schema-json-js-utils、mcp-neo4j-data-modelingを変換します)。 -
スキーマがコンテキスト内にある場合 → 使用し、検査は省略します。
-
スキーマがない場合 → 次を実行します:
CALL db.schema.visualization() YIELD nodes, relationships RETURN nodes, relationships;
SHOW INDEXES YIELD name, type, labelsOrTypes, properties, state WHERE state = 'ONLINE';
SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties;
SHOW PROCEDURES YIELD name RETURN split(name,'.')[0] AS namespace, count(*) AS procedures;
ラベルごとのプロパティ型 — まず APOC を確認します:
// If APOC available (preferred — use this):
CALL apoc.meta.schema() YIELD value RETURN value;
// No APOC AND database ≤ 100k nodes/rels only (expensive on large graphs):
CALL db.schema.nodeTypeProperties() YIELD nodeType, propertyName, propertyTypes, mandatory;
CALL db.schema.relTypeProperties() YIELD relType, propertyName, propertyTypes, mandatory;
クエリを返す前に検証します:ラベルが存在すること · リレーションシップタイプと方向が正しいこと · そのラベルにプロパティがあること · インデックスが ONLINE であること。
主なパターン
MERGE
// MERGE on constrained key; set extras in ON CREATE/ON MATCH
CYPHER 25
MATCH (a:Person {id: $a}) MATCH (b:Person {id: $b})
MERGE (a)-[r:KNOWS]->(b)
ON CREATE SET r.since = date()
ON MATCH SET r.lastSeen = date()
SET n = {} はすべてのプロパティを置き換えます。SET n += {} はマージします(部分更新に安全です)。更新には += を使用します。
WITH のスコープ
CYPHER 25
MATCH (a:Person)-[:KNOWS]->(b:Person)
WITH a, count(*) AS friends // b dropped here
WHERE friends > 5
RETURN a.name, friends ORDER BY friends DESC
WITH に列挙されていない変数はすべて破棄されます。WITH * はすべてを引き継ぎます。
サブクエリ — チートシート
EXISTS { (a)-[:R]->(b) } // boolean check
COUNT { (a)-[:R]->(b) WHERE a.x > 0 } // count
COLLECT { MATCH (a)-[:R]->(b) RETURN b.name } // collect list (full MATCH+RETURN required)
CALL (p) { MATCH (p)-[:ACTED_IN]->(m) RETURN m } // correlated subquery (explicit import)
OPTIONAL CALL (p) { ... } // nullable subquery
CALL { WITH x ... } は非推奨です → CALL (x) { ... } を使用します。COLLECT {} は常に 1 列を返します。
CALL IN TRANSACTIONS(一括書き込み)
CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
CALL (row) {
MERGE (p:Person {id: row.id}) SET p += row
} IN TRANSACTIONS OF 1000 ROWS ON ERROR CONTINUE REPORT STATUS AS s
入力ストリームはサブクエリの外側に置く必要があります。自動コミットのみです — beginTransaction() でラップしないでください。PERIODIC COMMIT は非推奨です。
DISJOINT BY [2026.06, Cypher 25] は、IN CONCURRENT TRANSACTIONS でロック競合を起こしやすいリソースを共有するバッチを順次スケジュールし、デッドロックを防ぎます — リレーションシップのインポート時に使用します:
CYPHER 25
LOAD CSV WITH HEADERS FROM 'file:///rels.csv' AS line
CALL (line) {
MATCH (a:Movie {id: line.movieId}), (b:Person {id: line.personId})
MERGE (b)-[:ACTED_IN]->(a)
} IN CONCURRENT TRANSACTIONS OF 1000 ROWS DISJOINT BY (line.movieId, line.personId)
DISJOINT BY (expr,...) はロックキー(外側のクエリの変数のみ)を宣言します。DISJOINT BY AUTO は静的解析によってキーを推測します。DISJOINT BY NONE は無効にします。dbms.cypher.transactions.default_subquery_batch_strategy をオーバーライドします。EXPLAIN/PROFILE では TransactionForeach の DISJOINT BY (...) にキーが表示されます。
QPE の基本
CYPHER 25
MATCH SHORTEST 1 (a:Person {name:'Alice'})(()-[:KNOWS]->()){1,}(b:Person {name:'Bob'})
RETURN b.name
// ACYCLIC [2026.03] — no repeated nodes within a path (prevents cycles)
CYPHER 25
MATCH p = ACYCLIC (start:Router {name: $from})-[:LINK]-+(end:Router {name: $to})
RETURN [n IN nodes(p) | n.name] AS route
ORDER BY length(p) LIMIT 5
量指定子はグループの外側に置きます:(pattern){N,M}。グループはノードで始まり、ノードで終わります。REPEATABLE ELEMENTS には上限のある {m,n} が必要です。ACYCLIC は、パス内でノードを繰り返せないことを意味します(デフォルトの DIFFERENT RELATIONSHIPS より制約が強くなります)。
マッチモード — MATCH の後に追加します:
DIFFERENT RELATIONSHIPS(デフォルト)— 各リレーションシップはパスごとに 1 回だけたどられますREPEATABLE ELEMENTS[2025.x] — ノード/リレーションシップを再訪できます。循環ルート、重みを最適化したパス、制約付きバックトラッキングに使用します。上限のある{m,n}が必要です
条件付き CALL サブクエリ [2025.06]
CYPHER 25
MATCH (move:Item {id: $id})
OPTIONAL MATCH (insertBefore:Item {id: $before})
OPTIONAL MATCH (insertAfter:Item {id: $after})
CALL (move, insertBefore, insertAfter) {
WHEN insertBefore IS NULL THEN {
MATCH (last:Item) WHERE NOT (last)-[:NEXT]->() AND last <> move
CREATE (last)-[:NEXT]->(move)
}
WHEN insertAfter IS NULL THEN {
CREATE (move)-[:NEXT]->(insertBefore)
}
ELSE {
CREATE (insertAfter)-[:NEXT]->(move)
CREATE (move)-[:NEXT]->(insertBefore)
}
}
書き込みロジックの if-else-if には WHEN…THEN…ELSE を使用します。相互排他的で、最初に一致した条件が採用されます。2025.06 より前のバージョンでは使用できません。
動的なリレーションシップタイプ [2025.x]
// Create/match/merge with dynamic rel type (must resolve to exactly one STRING)
CYPHER 25 CREATE (a:Node)-[:$($relType)]->(b:Node)
CYPHER 25 MATCH (a:Node)-[:$($relType)]->(b:Node) RETURN a.name, b.name
文字列補間 [2026.08, Cypher 25]
CYPHER 25
MATCH (p:Person {id: $id})
RETURN s"Hello, {p.name}, age {p.age}" AS greeting // S"..." and s'...' equivalent
- 各
{expr}はtoString()で変換されます MAP、LIST、NODE、PATH、RELATIONSHIPは拒否されます- リテラルの波括弧は
\{と\}でエスケープします - 補間文字列はネストできます
- 信頼できない値を
apoc.cypher.run*()に渡す Cypher テキストへ補間しないでください。代わりに$parametersを渡します
UUID 型 [2026.08, Cypher 25]
CYPHER 25
CREATE (sess:Session {sessionId: uuid()}); // random UUID value
CYPHER 25
MATCH (sess:Session {sessionId: uuid($uuidString)}) // STRING 8-4-4-4-12 → UUID
RETURN toString(sess.sessionId) AS sessionId, uuid.mostSignificantBits(sess.sessionId) AS msb
UUID プロパティには Neo4j 2026.08 以降が必要です。古いドライバーでは、プレースホルダーの MAP と警告 03N95 Neo.ClientNotification.UnknownType が返される場合があります。正確な対応状況はドライバーごとのスキルを確認してください(neo4j-driver-python-skill には >= 6.3 が必要です)。それ以外の場合は randomUUID() による STRING ID を引き続き使用します。
空間/Point
// WGS84 geographic point
SET n.coords = point({longitude: $lon, latitude: $lat})
// Distance in metres; requires POINT index for performance
MATCH (a:Place {name: $origin}) MATCH (b:Place)
RETURN b.name, point.distance(a.coords, b.coords) AS distM
ORDER BY distM LIMIT 10
// Bounding-box pre-filter (uses POINT index) then distance
MATCH (b:Place)
WHERE point.withinBBox(b.coords,
point({longitude: $west, latitude: $south}),
point({longitude: $east, latitude: $north}))
RETURN b.name, point.distance(b.coords, $origin) AS distM
POINT インデックスを作成します:CREATE POINT INDEX name IF NOT EXISTS FOR (n:Place) ON (n.coords)
集約のグルーピングキー
RETURN/WITH 内の非集約式は暗黙的なグルーピングキーです。GROUP BY は省略できます:
// actor + director are grouping keys; count(*) is the aggregate
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Person)
RETURN a.name, d.name, count(*) AS collaborations
ORDER BY collaborations DESC
WITH/RETURN の明示的な GROUP BY サブ句 [2026.07, Cypher 25] は、グルーピングキーを明示します。暗黙的なグルーピングに代わる、GQL に準拠した方法です。暗黙的なグルーピングも引き続き有効です:
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Person)
RETURN a.name, d.name, count(*) AS collaborations GROUP BY a.name, d.name
ORDER BY collaborations DESC
GROUP BY () = グルーピングキーなし(1 行);GROUP BY ALL = すべての非集約リターン項目がキーになります。プロジェクションに含まれないグルーピングキーは返されません。ルール → references/cypher-syntax.md。
count(n) は null 以外をカウントします。count(*) は null を含む行をカウントします。collect(DISTINCT expr) は重複を排除します。
count() は size(collect()) より高速です。count() は内部ストアを読み取りますが、collect() は最初にリストを作成します。
プロジェクション項目を参照する ORDER BY/WHERE サブ句の式が、変数または var.prop より複雑な場合は非推奨です [2026.07]。式に別名を付けてプロジェクションに含め、その別名で並べ替えてください。入力変数と名前が重複する場合も同様です。プロジェクション句で既に集約が行われていれば、ORDER BY/WHERE では、プロジェクションリストにない集約関数を呼び出せるようになりました。
よくある構文上の落とし穴(クエリが壊れる主な原因)
| 誤り | 正しい書き方 |
|---|---|
ORDER BY n.prop AS x DESC | ORDER BY n.prop DESC |
集約を行う RETURN の後で ORDER BY preAggVar | RETURN の別名を使用 |
count(r WHERE r.x=5) | sum(CASE WHEN r.x=5 THEN 1 ELSE 0 END) |
UNWIND list AS x WHERE x>5 | UNWIND list AS x WITH x WHERE x>5 |
least(a,b)/greatest(a,b) | CASE WHEN a<b THEN a ELSE b END |
-- comment | // comment |
shortestPath((a)-[*]->(b))(引き続き有効) | SHORTEST 1 (a)(()-[]->()){1,}(b) を推奨 |
id(n) | elementId(n) |
[:REL*1..5](引き続き有効) | (()-[:REL]->()){1,5} を推奨 |
CALL { WITH x ... } | CALL (x) { ... } |
COLLECT { (a)-[:R]->(b) } | COLLECT { MATCH ... RETURN b } |
SET n = {k:v} による部分更新 | SET n += {k:v} |
リレーションシップがある状態での DELETE n | DETACH DELETE n |
WHERE n.x = null | WHERE n.x IS NULL |
toInteger(null) はエラーになります | toIntegerOrNull(null) |
n.$key による動的プロパティ参照 | n[$key] |
SET n:$label | SET n:$($label) |
ZONED DATETIME >= date(...) → 0 行 | datetime(...) または .year アクセサーを使用 |
UTC として保存/比較する、末尾に Z が付いた ISO 文字列 | Neo4j では Z ≠ UTC — Z は UTC タイムゾーンではなくオフセットとして解析されます。プランナーと範囲インデックスでは異なる扱いになります。明示的に型変換してください:datetime({datetime: datetime('2025-09-10T03:43:00Z'), timezone: 'UTC'}) (neo4j#13519) |
FOREACH ... RETURN | UNWIND ... RETURN |
落とし穴の一覧 → references/syntax-traps.md
出力モードと書き込みゲート
デフォルトはパラメーター化クエリです。ノード全体や RETURN * ではなく、名前付きプロパティを返してください。
// RIGHT: agent gets named fields it can reason over
CYPHER 25 MATCH (n:Organization {name: $name}) RETURN n.name, n.founded, n.industry LIMIT 10
// WRONG: full node object wastes tokens, leaks all properties, agent can't extract fields cleanly
CYPHER 25 MATCH (n:Organization {name: $name}) RETURN n LIMIT 10
例外:スキーマ/診断クエリ(CALL db.schema.visualization()、SHOW INDEXES YIELD *、EXPLAIN)。オブジェクトそのものが目的の場合です。
検証ワークフロー:
- 書き込みの前に
EXPLAINを実行します。構文エラーやインデックスの不足を検出できます - 新しい読み取りクエリは、最初に
LIMIT 1を付けてテストします - 書き込みでは、読み取り部分を
RETURNとして検証してから、SET/CREATE/DELETEに置き換えます PROFILEで db ヒット数を測定し、AllNodesScan、CartesianProduct、Eagerがないか確認します
Query API v2(ドライバー不要。スキーマの調査、EXPLAIN、読み取り、書き込みに利用できます):
curl -X POST https://<instance>.databases.neo4j.io/db/<database>/query/v2 \
-u <user>:<password> -H "Content-Type: application/json" \
-d '{"statement": "EXPLAIN MATCH (n:Person {name: $name}) RETURN n", "parameters": {"name": "Alice"}}'
# Local: http://localhost:7474/db/<database>/query/v2
# Response: {"data": {"fields": [...], "values": [...]}} — prefix EXPLAIN to plan without executing
書き込み実行ゲート — エージェントが実行する場合(MCP/cypher-shell/HTTP)のみ適用します。ユーザーが実行するコード/スクリプト用の生成には適用しません:
EXPLAINを実行し、影響を受ける行数の見積もりを報告します- 実行前にユーザーの確認を待ちます
バージョン要件
バージョンが不明な場合は、デフォルトで 2025.01 で安全に使える機能を使用してください。
| 機能 | 最小バージョン | 代替手段 |
|---|---|---|
CYPHER 25、QPE、CALL (x) {} | 2025.01 | 2025 以降を必須とする |
マッチモード(DIFFERENT RELATIONSHIPS、REPEATABLE ELEMENTS) | 2025.01 | 2025 以降を必須とする |
動的ラベル $($expr)、coll.sort() | 2025.01 | APOC またはアプリ側 |
CONCURRENT TRANSACTIONS、REPORT STATUS | 2025.01 | 削除/省略 |
SEARCH 句 — VECTOR INDEX | 2026.01 | CALL db.index.vector.queryNodes(...)(2026.04 で非推奨) |
SEARCH 句 — FULLTEXT INDEX(WITH ANALYZER、SKIP/OFFSET) | 2026.09 | CALL db.index.fulltext.queryNodes(...)/queryRelationships(...) |
ACYCLIC パスマッチモード(パス内でノードの繰り返しなし) | 2026.03 | size(nodes(p)) = size(apoc.coll.toSet(nodes(p))) で後処理フィルター |
string.indexOf()、string.join()、string.regexReplace() | 2026.05 | apoc.text.* またはアプリ側 |
GQL エイリアス:FOR=UNWIND、PROPERTY_EXISTS=IS NOT NULL、IS [NOT] LABELED=n:Label、関数エイリアス(local_time、zoned_datetime、duration_between、collect_list など) | 2026.02–04 | GQL 準拠の場合のみ — Cypher の同等機能を使用してください。全一覧 → references/cypher-syntax.md |
GRAPH TYPE スキーマ DDL(ALTER CURRENT GRAPH TYPE SET/ADD/ALTER/DROP、SHOW CURRENT GRAPH TYPE) | 2026.02(プレビュー)、GA 2026.06 | 個別の CREATE CONSTRAINT/CREATE INDEX を使用 |
WITH/RETURN の GROUP BY サブ句(明示的なグループ化キー、GQL との整合性) | 2026.07 | 暗黙的なグループ化 — 投影に集約しない式を列挙 |
cardinality() — MAP のキー、LIST の要素、PATH のノードとリレーションシップ | 2026.07 | LIST/MAP のキーには size()、PATH には length() |
投影項目に含まれない ORDER BY/WHERE 内の集約関数(集約投影のみ) | 2026.07 | 集約結果をエイリアスとして投影し、そのエイリアスで並べ替え/フィルター |
system データベースでのプロシージャ呼び出しにおける YIELD の後の WHERE | 2026.07 | YIELD + RETURN を使用し、クライアント側でフィルター |
文字列補間 s"...{expr}..."/S"…" | 2026.08 | toString() または string.join() を使った + による連結 |
UUID 型、uuid()、uuid(name)、uuid(mostSigBits, leastSigBits)、uuid.mostSignificantBits()、uuid.leastSignificantBits() | 2026.08 | randomUUID() の STRING プロパティ |
null / 0 がゼロ除算エラーを発生させず null を返す | 2026.08 | CASE WHEN d = 0 THEN null ELSE n / d END |
マップ内包表記 {k: v IN map | keyExpr: valueExpr} | 2026.09 | apoc.map.fromPairs([k IN keys(m) | [k, m[k]]]) |
LIST、MAP、NODE、RELATIONSHIP、PATH に対する toString()、toStringList()、toStringOrNull() | 2026.09 | スカラー要素を個別に変換してから string.join() を使用 |
マップの加算 map1 + map2(マップのマージ) | 2026.10(Aura 2026 年 10 月) | apoc.map.merge(map1, map2) |
パフォーマンス
EXPLAIN/PROFILEで注意すべき兆候:AllNodesScan CartesianProduct NodeByLabelScan Eager
Eagerの修正 — 3つのアプローチ(機能する最もシンプルな方法を選択してください):
- 具体的なラベルを追加して、MATCHノードの読み取り/書き込みの曖昧さを解消します:
:Cityノードを書き込む際は、裸のMATCH (x)ではなくMATCH (x:CallingPoint)を使用します - 先に収集してから書き込みます:
WITH collect(u) AS users UNWIND users AS u ... - CALL IN TRANSACTIONS:各バッチを独立したトランザクションに分離します
ラベル推論 — 複数ラベルクエリでプランナーが選択性を過小評価する場合:[Neo4j 5]
CYPHER inferSchemaParts = most_selective_label
MATCH (admin:Administrator {name: $name}), (resource:Resource {name: $res})
MATCH p=(admin)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->(company)
RETURN count(p)
インデックスの要点:プロパティに対するすべてのMATCH/MERGE/WHEREには、検索対象プロパティのインデックスが必要です。インデックスがなければNeo4jはすべてのノードをスキャンします。インデックスが有効になるのはノードにラベルがある場合のみです — MATCH (n {prop: $v})ではインデックスは使用されず、MATCH (n:Label {prop: $v})では使用されます。制約のないMERGEにはアトミック性の保証がありません(並行する2つのMERGEによって重複が作成される可能性があります)。CONTAINS/ENDS WITHにはTEXTインデックスを使用してください(RANGEはサポートしていません)。EXPLAINでスキャンが表示された場合は、USING INDEX n:Label(prop)でプランを指定します。
ネストされたデータに対する連鎖したOPTIONAL MATCH → COLLECT { MATCH ... RETURN }に置き換えます。
動的ラベル($($label))→ AllNodesScan+Filterになります。可能な場合は静的ラベルを使用してください。
アンチパターンの全一覧 → references/performance.md
障害からの復旧
- 結果が0件:パラメーターの型を確認し、WHERE述語を1つずつ削除して、EXPLAINでインデックスの使用状況を確認します
- TypeErrors:
toIntegerOrNull()/toFloatOrNull()を使用し、IS NOT NULLでガードします - 変数がスコープ外:
WITHに列挙されていません →count(droppedVar)ではなくcount(*)を使用します - タイムアウト:AllNodesScanを修正 → 早い段階で
LIMITを追加 →CALL IN TRANSACTIONS OF 1000 ROWS - 実行時間の長いクエリの進行状況 [2026.03]:
SHOW TRANSACTIONS YIELD currentQuery, status, currentQueryProgress - DateTimeの不一致:
ZONED DATETIME >= date(...)→ 0件になります。datetime()または.yearを使用します ZサフィックスはUTCタイムゾーンと同じではありません:Zを含むISO文字列はUTCのタイムゾーンではなくUTCオフセットとして保存されます。ZとUTCで保存された値をまたぐ範囲クエリは0件になります。書き込み時に変換します:datetime({datetime: datetime($isoStr), timezone: 'UTC'})- 期間:
duration.between(d1,d2).daysは日数の構成要素です(1月1日→3月15日:14)であり、合計ではありません。合計日数にはduration.inDays(d1,d2).daysを使用します(73)。inMonths、inSecondsも同様です。.inDaysはduration値のアクセサーではありません Cannot merge node using null property value:MERGEキーがnullに解決されています — 先にパラメーターを検証してくださいIndexNotFoundError:SHOW INDEXES YIELD name, state WHERE state <> 'ONLINE'
参考資料
必要に応じて読み込んでください:
- references/indexes.md — インデックスの種類(RANGE/TEXT/FULLTEXT/POINT/COMPOSITE/LOOKUP)、制約、MERGE のロックセマンティクス、全文検索の Lucene 構文、インポート前の事前チェック
- references/cypher-syntax.md — 構文の完全なリファレンス:WITH、DELETE、ORDER BY、CASE、null、リスト、文字列、日付、空間/ポイント、LOAD CSV、サブクエリ、QPE、動的ラベル、SEARCH(ベクトル [2026.01]、全文検索 [2026.09]);条件付き CALL(WHEN/THEN/ELSE);ラベルパターン式;allReduce;NEXT 句;簡潔な CASE WHEN;normalize();文字列補間;UUID 型と
uuid()関数 [2026.08];マップ内包表記 [2026.09];インデックス/制約の種類の表;導入バージョンを注記した関数 - references/syntax-traps.md — 40 以上の構文上の落とし穴をまとめた表
- references/performance.md — アンチパターン、テキストインデックスと全文検索インデックス、Eager(3 つの解決策)、ラベル推論、バッチ処理のベストプラクティス、並列ランタイム
- references/advanced-patterns.md — REPEATABLE ELEMENTS のパターン、状態を持つトラバーサルの allReduce、複数停留地点の QPE、経路計画シミュレーション、DAG のクリティカルパス、時間ベースの不正検出コンポーネントグラフ、サイクル検出、OPTIONAL CALL
- references/apoc.md — APOC Core:リファクタリング、仮想グラフ、マージヘルパー、パスエクスパンダー、トリガー、コレクション、条件付き実行
- references/graph-type.md — GRAPH TYPE DDL(GA 2026.06):
ALTER CURRENT GRAPH TYPE SET/ADD/ALTER/DROP、SHOW CURRENT GRAPH TYPE [AS GRAPH]、要素型の構文、プロパティ型、制約、ラベルの含意、リレーションシップ型の適用、必要な権限
WebFetch
| 必要な情報 | URL |
|---|---|
| 句のセマンティクス | https://neo4j.com/docs/cypher-manual/25/clauses/{clause}/ |
| 関数シグネチャ | https://neo4j.com/docs/cypher-manual/25/functions/{type}/ |
| QPE/パス | https://neo4j.com/docs/cypher-manual/25/patterns/ |
| 空間/ポイント関数 | https://neo4j.com/docs/cypher-manual/25/functions/spatial/ |
| インデックス/制約のリファレンス | https://neo4j.com/docs/cypher-manual/25/indexes/ |
| チートシート全体 | https://neo4j.com/docs/cypher-cheat-sheet/25/all/ |
チェックリスト
- スキーマを確認するか、コンテキスト内で確認済みであることを確かめる
- すべてのトップレベルクエリに
CYPHER 25プレフィックスを付ける - リテラルではなく
$parametersを使用する - 探索的な読み取りには
LIMITを指定する(デフォルトは 25) -
EXPLAINを実行し、危険な兆候を解消する - 実行前に書き込み部分を
RETURNとして検証する - エージェントがクエリを生成するだけでなく実行する場合は、書き込み実行ゲートを適用する
-
MERGEは制約付きキーのみを対象にする - ラベルなしの
MATCH (n)を使用しない - 明示的なトランザクション内でスキーマ操作を行わない