SurrealDB は、JOIN しないで効率よくデータが取れることを売りにしているところがある。 この機能は Record Links と紹介されている。 が、親から子は非常に取りやすいのだが、子から親の方向に辿ろうとすると、苦しいものだったとわかった。
今回は、もう1つのグラフエッジを作成してデータ連携させる RELATE を試したのでメモしておく。 こちらの方が、よほど親->子、子->親も階層が深くても取りやすかった。
参考
動作 RELATE Deno 向けSDKにはドキュメントに、RELATEを使えるメソッドの設定はないようなので .query
を使って設定していく。
app-10.ts(抜粋) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 type Place = { id?: string ; name : string ; address : string ; }; type Food = { id?: string ; name : string ; price : number ; currency : string ; }; type FoodWithProductionArea = Omit <Food , "productionArea" > & { productionArea : Place };const [placeA] = await db.create <Place >("place" , { name : "tokyo" , address : "JPN-0000-0000" , }); const [apple] = await db.create <Food >("food" , { name : "apple" , price : 100 , currency : "JPY" , }); let result = await db.query ( ` RELATE $from->productionArea->$to; RELATE $to->production->$from; ` , { from : apple.id , to : placeA.id , } ); console .log (JSON .stringify (result, null , 2 ));console .log (await db.select ("productionArea" ));
RELATE
を使うと関連付けの名前のテーブルを作成し、関連付ける2つのレコードを in
と out
のふたつで管理されている。
この、エッジを使ってデータの引き当てをすると次のようになる。
app-11.ts(抜粋) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 await db.query ( `RELATE $from->productionArea->$to;` , { from : apple.id , to : placeA.id , } ); let result = await db.query <FoodWithProductionArea []>( ` SELECT *, ->productionArea->place.* FROM $id ` , { id : apple.id , } ); console .log (JSON .stringify (result[0 ], null , 2 ));result = await db.query <FoodWithProductionArea []>( ` SELECT *, <-productionArea<-food.* as food FROM $id ` , { id : placeA.id , } ); console .log (JSON .stringify (result[0 ], null , 2 ));
->
と <-
を使って、親子関係無く引き当てができる。 キーへ ->
が入ってくるので、適宜 as を使う方がきれいに収まる(見える)。
RELATE を一意にする 何も対応しないと RELATE は、多重に貼れてしまう。なのでこんなことが起きる。
app-12.ts(抜粋) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 await db.query ( ` RELATE $from->productionArea->$to; RELATE $from->productionArea->$to; RELATE $from->productionArea->$to; ` , { from : apple.id , to : placeA.id , } ); let result = await db.query <FoodWithProductionArea []>( ` SELECT *, ->productionArea->place.* FROM $id ` , { id : apple.id , } ); console .log (JSON .stringify (result[0 ], null , 2 ));
RELATEが多重になっているので、placeが3件出てしまう。
これについては、ドキュメントに対応が記載されており、unique index を貼る方法が紹介されている。
app-12-1.ts(抜粋) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 await db.query ( ` DEFINE INDEX unique_relate_productionArea ON TABLE productionArea COLUMNS in, out UNIQUE; ` ); const relateResult = await db.query ( ` RELATE $from->productionArea->$to; RELATE $from->productionArea->$to; RELATE $from->productionArea->$to; ` , { from : apple.id , to : placeA.id , } ); console .log (relateResult);let result = await db.query <FoodWithProductionArea []>( ` SELECT *, ->productionArea->place.* FROM $id ` , { id : apple.id , } ); console .log (JSON .stringify (result[0 ], null , 2 ));
以上の設定で多重に引き当ててしまうことはなくなった。
グラフエッジの削除 他にも機能はあるが最後にグラフエッジを削除してみる。
公式のドキュメントよりも、Issue の方が親切だった。
Documentation: Unrelating a relation
その上で DELETE $from->productionArea WHERE out=$to;
。 もしくは DELETE FROM $from->productionArea WHERE out=$to;
は動かなかった。
app-13.ts(抜粋) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 const [placeA] = await db.create <Place >("place" , { name : "tokyo" , address : "JPN-0000-0000" , }); const [apple] = await db.create <Food >("food" , { name : "apple" , price : 100 , currency : "JPY" , }); await db.query (`RELATE $from->productionArea->$to;` , { from : apple.id , to : placeA.id , }); let result = await db.query <FoodWithProductionArea []>( `SELECT *, ->productionArea->place.* FROM $id` , { id : apple.id , } ); console .log (JSON .stringify (result[0 ], null , 2 ));await db.query (`DELETE productionArea WHERE in = $from AND out=$to;` , { from : apple.id , to : placeA.id , }); result = await db.query <FoodWithProductionArea []>( `SELECT *, ->productionArea->place.* FROM $id` , { id : apple.id , } ); console .log (JSON .stringify (result[0 ], null , 2 ));
issue を見ると DELETE productionArea WHERE in = $from AND out=$to;
の形式は、効率が悪いらしい。 が、先に uniq index を貼って有れば、in out で検索しても(index がRDBでイメージするもの相当なら)十分パフォーマンスが出そうではある。
SurrealDB をもう少しいじってみた。これだけでも全体はまだ触れていないが、積極的に使いたい。
では。