Salesforce SOSLチュートリアル:開発者のための効率的な複数オブジェクト検索ガイド

背景と応用シーン

Salesforce開発者として、私たちは日々、膨大なデータの中から特定の情報を迅速に探し出すという課題に直面しています。Salesforceには強力なデータクエリ言語としてSOQL (Salesforce Object Query Language) がありますが、これは検索対象のオブジェクトとフィールドが明確な場合に最適です。しかし、ユーザーが「どのオブジェクトに存在するかわからないが、'ACME'というキーワードを含むレコードをすべて見つけたい」といった、より曖昧で広範な検索を要求するシナリオではどうでしょうか?

このような状況で真価を発揮するのが、SOSL (Salesforce Object Search Language)、日本語では「Salesforceオブジェクト検索言語」です。SOSLは、複数のオブジェクトにまたがって、テキストベースの検索を効率的に実行するために設計されています。まるでSalesforceプラットフォーム全体のGoogle検索のように機能します。

具体的な応用シーンとしては、以下のようなものが考えられます。

  • グローバル検索コンポーネントの実装: Lightning Web Component (LWC) や Auraコンポーネントで、ユーザーが入力した単一のキーワードで取引先、取引先責任者、商談、リードなどを横断的に検索するカスタム検索バーを構築する。
  • データクレンジングと重複検出: 特定の電話番号やメールアドレスが、取引先責任者、リード、またはカスタムオブジェクトに存在するかどうかを一括で確認する。
  • サポートコンソールの機能拡張: サポートエージェントが顧客から問い合わせを受けた際に、関連するキーワード(製品名、エラーコードなど)で過去のケース、ナレッジ記事、関連ドキュメントを一度に検索する機能を提供する。

SOQLが「データベースに対して構造化された問い合わせを行う」言語であるのに対し、SOSLは「検索エンジンに対して非構造化テキストの検索を依頼する」言語と理解すると、その違いが明確になります。開発者として、この二つのツールを適切に使い分けることが、パフォーマンスとユーザーエクスペリエンスに優れたアプリケーションを構築する鍵となります。


原理説明

SOSLがどのように機能するかを理解するためには、その検索メカニズムと構文を把握する必要があります。SOSLは、Salesforceがバックグラウンドで維持している検索インデックスを利用します。ユーザーがレコードを作成または更新すると、Salesforceはそのレコードの特定のフィールドの情報をトークン化(単語や記号に分割)し、検索インデックスに追加します。SOSLクエリが実行されると、SQLデータベースを一行一行スキャンするのではなく、この最適化されたインデックスに対して検索が行われるため、非常に高速なテキスト検索が可能になります。

デフォルトでインデックス化され、SOSLの検索対象となるフィールドは主に以下の通りです。

  • 標準オブジェクトの主要フィールド: Name、Phone、Emailなど
  • テキストエリア、ロングテキストエリア、リッチテキストエリア
  • 外部IDまたはユニーク属性を持つカスタムフィールド

SOSLの基本的な構文は以下のようになります。

FIND '検索キーワード' IN 検索グループ RETURNING オブジェクト(返すフィールド, ...), ...

FIND {SearchQuery}

検索したいテキストを指定します。ワイルドカード(* や ?)や論理演算子(AND, OR, AND NOT)を使用して、より複雑な検索クエリを組み立てることも可能です。例えば、`'ACME* AND (Tokyo OR Osaka)'` のように指定できます。ユーザーからの入力を動的に組み込む場合は、SOSLインジェクションを防ぐために `Search.escapeSingleQuotes()` メソッドでサニタイズすることが不可欠です。

IN <SearchGroup>

検索の範囲を指定します。以下のいずれかを指定できます。

  • ALL FIELDS: アクセス可能なすべてのテキストフィールドを検索します。
  • NAME FIELDS: オブジェクトの「名前」として定義されているフィールド(取引先名、氏名など)のみを検索します。
  • EMAIL FIELDS: Email形式のフィールドのみを検索します。
  • PHONE FIELDS: Phone形式のフィールドのみを検索します。
一般的には `ALL FIELDS` が最も広範な検索を提供しますが、検索範囲を絞ることでパフォーマンスを向上させることができます。

RETURNING <ObjectsAndFields>

検索結果として返してほしいオブジェクトと、そのオブジェクトのどのフィールドを取得するかを指定します。SOQLのSELECT句と同様に、`Account(Name, Industry)` のように記述します。ここで指定したオブジェクトとフィールドのみが結果セットに含まれます。複数のオブジェクトを指定することも可能です (例: `RETURNING Account(Name), Contact(FirstName, LastName, Email)`)。

SOSLクエリはApex内で直接実行でき、その結果は `List<List<SObject>>` という特殊な形式で返されます。外側のリストは `RETURNING` 句で指定したオブジェクトに対応し、内側のリストがそのオブジェクトで見つかったレコードのリストとなります。


サンプルコード

ここでは、特定のキーワードに一致する取引先 (Account) と取引先責任者 (Contact) を同時に検索するApexコードの例を示します。このコードは、Salesforceの公式ドキュメントで提供されている標準的なSOSLの使用方法に基づいています。

// Apexクラスまたは匿名実行ウィンドウで実行可能なコード
// 検索キーワードを定義します。ワイルドカード '*' を使用して前方一致検索を行います。
String searchKeyword = 'Wingo*';

// SOSLクエリを文字列として構築します。
// FIND句でキーワードを指定し、IN句で検索範囲(全てのテキストフィールド)を指定します。
// RETURNING句で、検索結果として取得したいオブジェクトとフィールドを明記します。
// ここでは、AccountオブジェクトのIdとName、ContactオブジェクトのIdとFirstNameを取得します。
String soslQuery = 'FIND \'' + searchKeyword + '\' IN ALL FIELDS RETURNING Account(Id, Name), Contact(Id, FirstName)';

// Search.query() メソッドを使用して、動的に構築したSOSLクエリを実行します。
// 静的クエリ ([FIND ...]) ではなく動的クエリを使用することで、変数に基づいた検索が可能になります。
// 戻り値は List> 型です。
List> searchList = Search.query(soslQuery);

// SOSLの結果は、RETURNING句で指定されたオブジェクトの順序でリストに格納されます。
// この例では、最初のリスト (インデックス0) がAccountの結果、
// 2番目のリスト (インデックス1) がContactの結果となります。
List searchAccounts = (List)searchList[0];
List searchContacts = (List)searchList[1];

// 検索結果をデバッグログに出力して確認します。
System.debug('Found ' + searchAccounts.size() + ' accounts.');
for (Account acc : searchAccounts) {
    System.debug('Account Name: ' + acc.Name);
}

System.debug('Found ' + searchContacts.size() + ' contacts.');
for (Contact con : searchContacts) {
    System.debug('Contact FirstName: ' + con.FirstName);
}

このコードは、`'Wingo'` で始まる単語を、アクセス可能な全てのテキストフィールドから検索します。そして、一致したレコードが取引先であればそのIDと名前を、取引先責任者であればそのIDとファーストネームを返します。結果の `List<List<SObject>>` を正しく処理するためには、`RETURNING` 句で指定したオブジェクトの順序を覚えておき、適切にキャスト(型変換)する必要がある点に注意してください。


注意事項

SOSLは非常に強力ですが、その利用にはいくつかの制約と注意点があります。これらを理解しておくことは、安定したパフォーマンスのアプリケーションを開発する上で不可欠です。

ガバナ制限 (Governor Limits)

Salesforceプラットフォームでは、リソースの公平な利用を保証するためにガバナ制限が設けられています。SOSLも例外ではありません。

  • 1トランザクションあたりのSOSLクエリ数: 同期Apexでは20回、非同期Apexでは100回という制限があります。SOQLと同様に、ループ内でSOSLクエリを発行することは絶対に避けるべきです。
  • 返されるレコード数の上限: 1回のSOSLクエリで返されるレコードの合計は最大2,000件です。これを超える結果が見つかった場合でも、2,000件で切り捨てられます。大規模なデータセットを扱う場合は、この上限を考慮した設計が必要です。

権限と共有ルール

SOSLクエリの結果は、実行ユーザーの権限と共有設定に完全に準拠します。ユーザーがアクセス権を持たないオブジェクトやレコードは、たとえ検索キーワードに一致したとしても結果セットには含まれません。これは、データセキュリティを維持するための重要な仕様です。

検索インデックスの遅延

レコードが作成または更新されてから、その内容が検索インデックスに反映されるまでには、わずかな時間がかかる場合があります。通常は数秒から数分ですが、システムの負荷が高い状況では遅延が長くなる可能性もあります。そのため、Apexトリガーなどでレコードを挿入した直後に同じトランザクション内でSOSL検索をかけても、その新しいレコードがヒットしないことがある点に留意してください。

SOSLインジェクション

動的にSOSLクエリを構築する際、ユーザーが入力した値を直接クエリ文字列に連結すると、SOSLインジェクションと呼ばれるセキュリティ脆弱性を生む可能性があります。悪意のあるユーザーが特殊な文字列を入力することで、意図しない検索を実行させたり、エラーを発生させたりする恐れがあります。これを防ぐため、ユーザー入力は必ず `Search.escapeSingleQuotes()` メソッドを使用してエスケープ処理を行ってください。

String userInput = 'Test\'s Company';
String sanitizedInput = Search.escapeSingleQuotes(userInput);
String soslQuery = 'FIND \'' + sanitizedInput + '\' IN ALL FIELDS RETURNING Account';
List> searchList = Search.query(soslQuery);

まとめとベストプラクティス

SOSLは、Salesforce開発者が持つべき強力なツールキットの一部です。その特性を正しく理解し、SOQLと適切に使い分けることで、ユーザーにとって価値のある、高機能な検索体験を提供できます。

最後に、SOSLを利用する上でのベストプラクティスをまとめます。

  1. SOSL vs. SOQLの判断基準を明確にする:
    • SOSLを使う時: どのオブジェクトにあるか不明なテキストデータを検索したい場合。複数のオブジェクトを一度に検索したい場合。
    • SOQLを使う時: 検索対象のオブジェクトが1つに特定できている場合。Idや数値、日付など、テキスト以外の条件で厳密な絞り込みを行いたい場合。関連オブジェクトのデータを取得したい場合 (親子リレーションクエリ)。
  2. 検索範囲と返すフィールドを限定する: パフォーマンス向上のため、可能な限り `IN ALL FIELDS` よりも `IN NAME FIELDS` など、より具体的な検索グループを指定しましょう。また、`RETURNING` 句では、本当に必要なフィールドのみを指定し、不必要なデータを取得しないように心がけてください。
  3. ガバナ制限を常に意識する: Apexトリガーやループ内でのSOSL発行は避け、一括処理(バルク化)を徹底してください。1回のクエリで多くの作業を完了できるように設計することが重要です。
  4. セキュリティを最優先する: ユーザー入力から動的にSOSLクエリを生成する場合は、`Search.escapeSingleQuotes()` の利用を徹底し、SOSLインジェクションのリスクを排除してください。
  5. ユーザーエクスペリエンスを考慮する: 検索結果が0件の場合や、2,000件の上限に達した場合の挙動を考慮し、ユーザーに分かりやすいフィードバックを表示するUIを設計しましょう。

これらの原則を守りながらSOSLを使いこなすことで、Salesforceプラットフォームの能力を最大限に引き出し、より洗練されたアプリケーションを構築できるでしょう。

コメント