グラフ パターン マッチングを使用すると、直感的で視覚的な構文を使用して、検索するデータの構造を記述できます。 テーブルを結合する代わりに、リレーションシップ自体 (エッジで接続されたノード) のようなパターンを記述します。 この記事では、Microsoft Fabricのグラフで一般的なシナリオの GQL パターン クエリを記述する方法について説明します。
この例では、 ソーシャル ネットワークのサンプル データセットを使用します。 完全なパターン構文リファレンスについては、 GQL グラフ パターンを参照してください。
この記事を使って、ノード、エッジ、パスのパターンを構築し組み合わせる方法を学びましょう。 関係のない隣接やエンティティの検索など、すぐに適応できるタスクについては、「 一般的なGQLクエリを書く」を参照してください。 フィルタリング、グループ化、集約のワークフローについては、「 グラフデータのフィルターおよび集計」を参照してください。
前提条件
- ソーシャルネットワークのサンプルデータセットから作成されたグラフ項目で、ノードタイプ、エッジタイプ、プロパティはソーシャルネットワークスキーマの例で説明されています。
- 基本的な
MATCHとRETURNクエリに関する知識。 GQL 言語ガイドを参照してください。
直接的な関係を照合する
基本パターンは、ノードタイプ、特定のエッジタイプ、および別のノードタイプと一致します。 構文はリレーションシップの図のようになります。
たとえば、会社と組み合わせて最大 100 人を検索するには、次の操作を行います。
MATCH (p:Person)-[:workAt]->(c:Company)
RETURN p.firstName, p.lastName, c.name
LIMIT 100
方向がわからない場合、または方向を気にしない場合は、無向エッジ パターンを使用します。 たとえば、接続を開始したユーザーに関係なく、最大 100 人の相互知人を見つけるには、次のようにします。
MATCH (a:Person)-[:knows]-(b:Person)
RETURN a.firstName, b.firstName
LIMIT 100
Note
グラフでは現在、無向エッジの作成はサポートされていませんが、 -[:label]- 構文を使用して任意の方向にエッジを照会できます。
インライン WHERE を使用してパターンをフィルター処理する
どのノードやエッジがマッチに参加できるかを条件で定義したとき、パターン内に WHERE を配置します。 完了したマッチを後処理するために文レベルの MATCH ... WHERE 条件を使い、前の文で生成された行に条件が適用される場合は別の FILTER を用いましょう。 述語の配置は最短経路の結果を変えることがあります。 詳細については、「 パス選択の前後に述語を置く」を参照してください。
たとえば、1990 年より前に生まれ、名前が "A" で始まる会社で働いている人を見つけるには、次のようにします。
MATCH (p:Person WHERE p.birthday < 19900101)-[:workAt]->(c:Company WHERE c.name STARTS WITH 'A')
RETURN p.firstName, p.lastName, c.name
エッジ プロパティでフィルター処理して、一致するリレーションシップを制限します。 たとえば、2010 年以降に会社で働き始めた人のみを返すには、次のようにします。
MATCH (p:Person)-[w:workAt WHERE w.workFrom >= 2010]->(c:Company)
RETURN p.firstName, c.name, w.workFrom
マルチホップの関係にマッチする
可変長パターンを使用して、1 つの式で複数のホップを走査します。
{min,max}構文でホップの最小数と最大数を指定します。
たとえば、Aliceを中心にして、友人関係の距離が2から4の範囲にある最大100人のユーザーを見つけるには、次のようにします。
MATCH (src:Person WHERE src.firstName = 'Alice')-[:knows]->{2,4}(dst:Person)
RETURN dst.firstName, dst.lastName
LIMIT 100
Alice から最大 100 の即時および 2 次接続 (1 つまたは 2 つのホップ) を検索するには:
MATCH (src:Person WHERE src.firstName = 'Alice')-[:knows]->{1,2}(dst:Person)
RETURN DISTINCT dst.firstName, dst.lastName
LIMIT 100
パス モードを使用したトラバーサルの制御
デフォルトでは、GQLは WALKを使用しており、ノードやエッジの繰り返しが可能となります。 より厳格な元素の一意性が必要な場合は別のパスモードを使いましょう。
| パス モード | Behavior | 次の場合に使用... |
|---|---|---|
WALK |
ノードとエッジの繰り返し使用を許可します(デフォルト) | すべての同じ散歩が有限の範囲内で行われることが望ましいです。 |
TRAIL |
繰り返しのエッジはありません | 同じエッジを辿り直したくはありませんが、同じノードが異なるエッジを通って現れることもあります。 |
SIMPLE |
共有された開始ノードと終了ノードを除き、重複するノードはなく、重複する辺もありません | パスの途中にノードを複数回表示する必要はありませんが、開始に近いパスを許可します。 ループを検出するのに役立ちます。 |
ACYCLIC |
繰り返しノードやエッジはありません | パス内の任意の場所に 2 回以上ノードが表示されていないことを保証する必要があります。 厳密な階層、系列、またはノードを再訪すると正しくない結果が生成されるトラバーサルに使用します。 |
WALK は最も制限の厳しいモードであり、 ACYCLIC は最も制限が厳しいモードです。
SIMPLEおよびACYCLICにおいて、エッジの一意性はノードの一意性から導かれます。
SIMPLEパスは最初のノードに戻ることで閉じることができますが、それでもエッジを再利用することはできません。
違いを説明するために、Alice → Bob → Carol → Bob のパスを考えてみましょう。
- WALK — このパスを許可します。 ノードとエッジは自由に繰り返すことができます。
- TRAIL — このパスを許可します。 Bob は 2 回表示されますが、使用される各エッジは異なるリレーションシップ (Alice→Bob と Carol→Bob は異なるエッジであるため、エッジは繰り返されません。
- SIMPLE — このパスをブロックします。 Bobは複数回現れますが、SIMPLEでは、ノードがパスの始まりと終わりの両方(閉ループ)である場合にのみ、そのノードを繰り返すことができます。 ここでは Alice が始まり、Bob が最後なので、例外は適用されません。
- ACYCLIC — このパスをブロックします。 Bob はパス内の任意の場所に複数回表示されます。
次の例では、 TRAIL を使用して、4 つのホップ内で Alice のネットワークで到達可能な最初の 100 人の各ユーザーにつながる個別のパスの数をカウントする方法を示します。
MATCH TRAIL (src:Person WHERE src.firstName = 'Alice')-[:knows]->{1,4}(dst:Person)
LET personId = dst.id
RETURN personId, dst.firstName, dst.lastName, count(*) AS pathCount
GROUP BY personId, dst.firstName, dst.lastName
ORDER BY pathCount DESC
LIMIT 100
ACYCLICを使用して、各ユーザーが一意であるパスを通じて、Alice から4つのホップ以内で到達できる最大100人を返します。
MATCH ACYCLIC (src:Person WHERE src.firstName = 'Alice')-[:knows]->{1,4}(dst:Person)
RETURN dst.firstName, dst.lastName
LIMIT 100
デフォルトの REPEATABLE ELEMENTS マッチモードでは、同じグラフパターン内の別々のパスでエッジを再利用できます。 すべての経路にまたがるすべての辺が異なる場合、 DIFFERENT EDGES または DIFFERENT RELATIONSHIPS を使用します。
MATCH DIFFERENT EDGES
(p:Person)-[:knows]->(friend:Person),
(p)-[:knows]->(other:Person)
RETURN p.firstName, friend.firstName, other.firstName
LIMIT 100
最短経路を1つ返す
ALL はデフォルトのパス探索であり、すべての一致するパスを返します。
ANY SHORTESTを使って、各送信元-宛先ペアに対して最短経路を1つ返します。 以下のクエリは、アリスからボブへの最短経路を1つ見つけます。
MATCH p = ANY SHORTEST
(src:Person WHERE src.firstName = 'Alice')-[:knows]->{1,4}
(dst:Person WHERE dst.firstName = 'Bob')
RETURN p, path_length(p) AS hopCount
複数の最短経路が重なると、どの経路が返されるかは決定論的ではありません。
ALL SHORTEST
ANYパスサーチはサポートされていません。
述語の配置は、 ANY SHORTESTの結果を変えることがあります。 インラインノードおよびエッジ述語は適格パスを制約し、ポストフィルターは長いパスを選ばずに選択されたパスを除去できます。
パス選択の前または後に述語を配置するを参照してください。
ヒント
予測可能なクエリコストには有限の上限を好みます。 無制限 ALL WALK パターンはサポートされていません。
TRAIL、SIMPLE、またはACYCLICを持つ無界パターンは要素の一意性によって終了しますが、それでも多くの経路を戻すことができます。 Unbounded ANY SHORTEST WALK は特定のクエリ形状のみをサポートします。
現在の制限事項を参照してください。
変数の再利用を使用して共有エンティティを表現する
パターンの 2 つの部分で同じ変数を再利用すると、暗黙的な等値制約が作成されます。両方の参照が同じノードと一致する必要があります。 この手法を使用すると、"共有された 3 番目のエンティティを介して接続されているエンティティを見つける" を表現できます。
たとえば、お互いを知り、同じ会社で働いている人を最大 100 組見つけるには、次のようにします。
MATCH (c:Company)<-[:workAt]-(a:Person)-[:knows]-(b:Person)-[:workAt]->(c)
RETURN a.firstName, b.firstName, c.name
LIMIT 100
変数 c は両方の workAt ターゲットに再利用されるため、クエリは両方のユーザーが互いを認識 し 、同じ会社で働いているペアのみを返します。
同じ投稿が好きな人を最大 100 組見つけるには:
MATCH (a:Person)-[:likes]->(post:Post)<-[:likes]-(b:Person)
WHERE a.id < b.id
RETURN a.firstName, b.firstName, post.id
LIMIT 100
ヒント
WHERE a.id < b.id条件により、重複するペア (Alice + Bob と Bob + Alice) が結果に表示されなくなります。
複数のパターンを結合する
1 つの MATCHで複数のパターンをコンマで区切って一覧表示します。 パターンが同じグラフ要素をバインドしなければならない場合は変数を再利用します。
たとえば、職場と住んでいる都市の両方と共に最大 100 人を見つけるには、次のようにします。
MATCH (p:Person)-[:workAt]->(c:Company),
(p)-[:isLocatedIn]->(city:City)
RETURN p.firstName, c.name AS company, city.name AS city
LIMIT 100
共有変数 p は、2 つのパターンを接続します。 各結果行は、会社と市区町村を持つ 1 人のユーザーを表します。
連結していないパターンも有効ですが、それらはマッチのデカルト積を形成します。 意図的にすべての組み合わせが必要な時だけ使ってください。 パフォーマンスの指針については、「 効率的な結合のために共有変数を使う」を参照してください。
省略可能なリレーションシップを一致させる
すべてのノードにリレーションシップが存在しない可能性がある場合は、 OPTIONAL MATCH を使用します。 一致しない行は、SQL NULLと同様に、LEFT JOIN値で保持されます。
たとえば、会社名を持つ最大 100 人を返します。これには、雇用主がいないユーザー (つまり、会社の列の NULL を返すユーザー) が含まれます。
MATCH (p:Person)
OPTIONAL MATCH (p)-[:workAt]->(c:Company)
RETURN p.firstName, p.lastName, c.name AS company
LIMIT 100
IS NULL後にOPTIONAL MATCHを使用して、会社で働いていない最大 100 人を検索します。
MATCH (p:Person)
OPTIONAL MATCH (p)-[:workAt]->(c:Company)
FILTER c IS NULL
RETURN p.firstName, p.lastName
LIMIT 100