LoginSignup
5
0

ApexでSOQLとSOSLとミックスさせて検索体験を向上させる

Last updated at Posted at 2023-12-24

はじめに

今回はApexで取引先名のレコード検索の検索体験を向上させるために
SOQLSOSLをミックスさせて検索してみようと思います。

SOSLとSOQLの違い

SOQLとSOSLは検索時の優先事項、1クエリで検索するオブジェトの数、Salesforce内の機能で使い分けが主な違いです。

SOQL SOSL
正式名称 Salesforce Object Query Language Salesforce Object Search Language
使用場所 リストビュー、レポート、Apex (グローバル、サイドバー、詳細) 検索、Apex
インデックス付けの実行 同期的 (カスタムインデックスまたは標準インデックス) 非同期的に実行。通常 2 ~ 3 分。9000 を超えるレコードを一度に読み込んだ場合、超過分は一括インデックスキューに移動される (低速)。
検索時の優先事項 正確性。条件に一致するすべての結果を返す。 関連性と速度。Google 検索に類似。最近参照されたレコードを優先。
検索範囲 一度に 1 オブジェクトを検索可能。 一度に複数のオブジェクトを検索可能。

引用:https://help.salesforce.com/s/articleView?id=000385852&type=1

なぜ、SOQLとSOSLを混ぜるの?

SOSLはグローバル検索に使われてるものなので基本は意図したものが返ってきますが
検索の特徴として文字列を単語区切りで認識しているので検索クエリによっては漏れてしまうことがあります。

Salesforce内での検索機能で意図したものが取れないと誤って重複レコードを作成してしまう要因にもなってしまいます。

サンプルとして3つのレコードを用意してみました。

検索クエリABだとSOQLとSOSLでどのレコードが取得できるでしょうか?

スクリーンショット 2023-12-24 9.45.19.png

以下の結果になりました。

SOQL SOQL
AABB、AAB、AABCC 検索結果なし

これは極端の例ですが、連続している言葉は1語として捉えるという仕様によるものです。

以下の記がとてもわかりやすく挙動をまとめてくれています。

SOQLとSOSLを併用するApex

SearchAccount.cls
public with sharing class SearchAccount {
    public SearchAccount(String searchKeyword, Integer limitNum) {
      // SOQLで検索
      List<Account> accountsBySoql = getAccountByNameSOQL(searchKeyword, limitNum);
      System.debug('----SOQL----');
      System.debug(accountsBySoql);
      System.debug(accountsBySoql.size());
      // SOSLで検索
      List<Account> accountsBySosl = getAccountByNameSOSL(searchKeyword, limitNum);
      System.debug('----SOSL----');
      System.debug(accountsBySosl);
      System.debug(accountsBySosl.size());
      // SOSL SOSLの検索結果をマージ
      Set<Account> accountSets = new Set<Account>();
      accountSets.addAll(accountsBySoql);
      accountSets.addAll(accountsBySosl);
      List<Account> deduplicationAccounts = new List<Account>(accountSets);

      System.debug('----検索結果----');
      System.debug(deduplicationAccounts);
    }

    private static List<Account> getAccountByNameSOQL(
      String searchKeyword,
      Integer limitNum
    ) {
      searchKeyword = '%' + searchKeyword + '%';
      List<Account> accounts = [
        SELECT Id, Name
        FROM Account
        WHERE
          Name LIKE :searchKeyword
        LIMIT :limitNum
      ];
  
      return accounts;
    }

    private static List<SObject> getAccountByNameSOSL(
      String searchKeyword,
      Integer limitNum
    ) {
      List<List<SObject>> searchSosl = [
        FIND :searchKeyword
        IN NAME FIELDS
        RETURNING
          Account(
            Id,
            Name
          LIMIT :limitNum)
      ];
      if (searchSosl.isEmpty()) {
        return null;
      }
  
      return searchSosl[0];
    }
}

結果のマージについて

  • SOQLとSOSLの検索結果をそれぞれ Listにする
  • Setに検索結果を入れて重複排除
  • SetをListに入れ直す

という作業で検索結果をマージしつつ、Listに変換しています。

// SOQLで検索
List<Account> accountsBySoql = getAccountByNameSOQL(searchKeyword, limitNum);
// SOSLで検索
List<Account> accountsBySosl = getAccountByNameSOSL(searchKeyword, limitNum);
// SOSL SOSLの検索結果をマージ
Set<Account> accountSets = new Set<Account>();
accountSets.addAll(accountsBySoql);
accountSets.addAll(accountsBySosl);
List<Account> deduplicationAccounts = new List<Account>(accountSets);

実行

開発者コンソールもしくは、SalesforceCLIで実行します。

SearchAccount searchAccount = new SearchAccount('AB', 100);

実行結果

SOSLで検索しきれなかったものはSOQLの曖昧検索によって取得できています。

09:45:41.49 (63328503)|USER_DEBUG|[5]|DEBUG|----SOQL----
+09:45:41.49 (63465997)|USER_DEBUG|[6]|DEBUG|(Account:{Id=0011Q00002aN3ygQAC, Name=AABB}, Account:{Id=0011Q00002aN3ylQAC, Name=AAB}, Account:{Id=0011Q00002aN3ybQAC, Name=AABCC})
09:45:41.49 (63576355)|USER_DEBUG|[7]|DEBUG|3
09:45:41.49 (160393968)|USER_DEBUG|[10]|DEBUG|----SOSL----
+09:45:41.49 (160447999)|USER_DEBUG|[11]|DEBUG|()
09:45:41.49 (160483678)|USER_DEBUG|[12]|DEBUG|0
09:45:41.49 (162147839)|USER_DEBUG|[19]|DEBUG|----検索結果----
+09:45:41.49 (162288799)|USER_DEBUG|[20]|DEBUG|(Account:{Id=0011Q00002aN3ygQAC, Name=AABB}, Account:{Id=0011Q00002aN3ylQAC, Name=AAB}, Account:{Id=0011Q00002aN3ybQAC, Name=AABCC})

どういう時にこのApexを使う?

取引先名を検索する
画面フローの検索ロジックとして使うといいと思います。

今回はSOSLでAccoutのみですが、Leadを加えればかなり利用者の検索したいレコードを取得できる確率が高まるのではないでしょうか

InvocableMethodアノテーションを付与すればフローのアクションとして利用できるようになります。

最後に

今回は自前でSOQLとSOSLを合わせましたが、
契約書とかの非構造データも検索できるようにすると面白そうです。
Salesforceが将来的にLLMの結果とかをグローバル検索に合わせて検索できるようにしてくれるかもしれませんね。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0