🦋

Serverpod ~JOIN機能なしで二段階検索

2023/10/02に公開

Future versions of Serverpod will add support for automatic joins and database views.

これを待ちならが他のコードを書いていたのだけれど、先に進めなくなってきたので、
二段階検索、実装してみる。
外部キーを使えば簡単、みたいな話なのだが、外部キーって「親子関係」がある。
親削除したら、子どももみんな削除だぜ、みたいな。(無効にもできるらしいけど)
なので今回は、単純にフラットな関係の二つのtableを、中間tableで繋ぎます。

まずtableAとtableABに同時にデータを入れる。

例えば「2003年5月9日、小惑星探査機はやぶさ打ち上げ」がtableAに入る。
「JAXA」「内之浦宇宙空間観測所」はtableBに入っている。
これらは当然、別のデータとも繋がっているので、多対多の関係になる。
tableBのIDが引数になっている。

  Future<int> save(Confirm confirm) async {
    if (***) {
      try {
        var principal = Principal(
		******
        );
        var principalId = await client.principal.addPrincipal(principal);

	     //ここまでがtableAへの入力。最後の行で取ったIdを使う。
	     //繋ぐべきtableBのデータのIdのList<int>は既に取ってある。
	     //これはFlutter側の話なので、今回は割愛

        //tableABへの入力
        if (confirm.selectedOrgId.isNotEmpty) {
          for (var orgId in confirm.selectedOrgId) {
            var pOrgs = PrincipalOrgs(
                principal_id: principalId, org_id: orgId);
            var principalOrgsId = await client.principalOrgs.addPOrgs(pOrgs);
            debugPrint('Added Orgs involved : $principalOrgsId');
          }
        }
     }

次はJAXAをkeywordに、いろいろな宇宙探査について検索する。

endpointに関数を設定する。
これはtableAに対応するendpointに書いた。
もちろんListなので複数の用語でor検索が可能。
and検索は未対応。

//tableBのIdでtableAを検索する関数
Future<List<Principal>> getPrincipalByOrgsId(Session session, {List<int>? orgIds}) async {
  if (orgIds == null || orgIds.isEmpty) {
    return Future.value([]); // Return empty list if no orgIds are provided
  }

  // Step 1: Get principalIds from PrincipalOrganisation using orgIds
  var whereClausePrincipalOrganisations;
  for (var orgId in orgIds) {
    if (whereClausePrincipalOrganisations == null) {
      whereClausePrincipalOrganisations = PrincipalOrgs.t.org_id.equals(orgId);
    } else {
      whereClausePrincipalOrganisations = whereClausePrincipalOrganisations | PrincipalOrgs.t.org_id.equals(orgId);
    }
  }

  var PrincipalOrgsResults = await PrincipalOrgs.find(session, where: (_) => whereClausePrincipalOrganisations);
  var principalIds = PrincipalOrgsResults.map((row) => row.principal_id).toList();

  // Step 2: Get Principals using principalIds
  var whereClausePrincipal;
  for (var principalId in principalIds) {
    if (whereClausePrincipal == null) {
      whereClausePrincipal = Principal.t.id.equals(principalId); // Assuming the id field in Principal table is named 'id'
    } else {
      whereClausePrincipal = whereClausePrincipal | Principal.t.id.equals(principalId);
    }
  }

  return await Principal.find(
    session,
    where: (_) => whereClausePrincipal,
    orderBy: Principal.t.point,
  );
}

で、Flutterから呼ぶのはこれだけ。
JAXAのIdは既に取れているという前提。これも今回は割愛。

  fetchPrincipalByOrgsId({List<int>? listOrgIds}) async {
    try {
      _principal = await client.principal.getPrincipalByOrgsId(orgIds: listOrgIds);
      print("Getting principal with OrgIds: $listOrgIds");
      notifyListeners();
    } on Exception catch (e) {
      debugPrint('$e');
    }
  }

これで、JAXA関連のいろんなデータが年代順に並ぶ。
これが最適解かどうかわからないけど、ちゃんと動くので。
いずれJOIN機能に差し替えるまでこれでいこうと思う。

Flutter大学

Discussion