Salesforce SOSLの基礎とSOQLとの違い

# これは何？
この記事は「[24日後に立派なSalesforceエンジニアになるWEBエンジニア Advent Calendar 2022](https://qiita.com/advent-calendar/2022/kakakaori)」の21日目の記事です4日後に立派なSalesforceエンジニアになるために今日はSOSLについて学んでいきます。

# SOSLとは？SOQLとの違いは？
昨日学んだSOQLがSalesforce Object Query Languageの略だったのに対して、今日学んでいく[SOSL](https://trailhead.salesforce.com/ja/content/learn/modules/apex_database/apex_database_sosl)はSalesforce Object Search Languageの略。SOQL同様にレコードを取得するのに使うが、SOQLと違って、SOSLでは1つのクエリで複数のオブジェクトを対象にした検索ができる。
「SFDC」という単語を含む項目を持つ取引先と取引先責任者を検索するサンプルの場合は以下のように記述する。
```sql:
FIND 'SFDC' IN ALL FIELDS RETURNING Account(Name), Contact(FirstName,LastName)
```
SOQLで`SLECT Id FROM CONTACT WHERE NAME = 'SFDC'`としたときは`Name`が`SFDC`に完全一致したもののみが抽出されるが、SOSLでは`SFDC`を含むものを抽出してくるのもSOQLとの違いである。

# 基本的な構文

```sql:
FIND 'SearchQuery' 
[ IN SearchGroup ]
[ RETURNING FieldSpec [[ toLabel(fields)] [convertCurrency(Amount)] [FORMAT()]] ]
[ WITH DivisionFilter ]
[ WITH DATA CATEGORY DataCategorySpec ]
[ WITH SNIPPET[(target_length=n)] ]
[ WITH NETWORK NetworkIdSpec ]
[ WITH PricebookId ]
[ WITH METADATA ]
[ LIMIT n ]
```
ちなみに上記はApexでの書き方で、このとき取得した結果には複数のオブジェクトのレコードが含まれるため`List<List<SObject>>`このように各オブジェクトごとのList型で返ってくるのもSOQLと異なる部分である。
クエリエディタから使う場合は基本的な構文は同じだが`{SearchQuery}`の指定だけ`''`ではなくて`{}`になるので注意。

今回はこの中からよく使うものをピックアップしてみていくが、ここに挙げる以外の詳細な指定方法については[SOSL の構文](https://developer.salesforce.com/docs/atlas.ja-jp.soql_sosl.meta/soql_sosl/sforce_api_calls_sosl_syntax.htm)で詳しく書かれている。
```sql:
FIND[IN SearchGroup] [RETURNING ObjectsAndFields]
```

## `FIND 'SearchQuery'`
ここには検索したいワードが入る。
例に挙げたように`SFDC`と１単語で指定することもできるしORやANDで複数を指定することもできる。`john AND smith`のように指定すると`john`と`smith`の両方を含むもののみが抽出される。

## `IN SearchGroup`
検索の範囲を指定する。何も指定しないとデフォルトは`ALL FIELDS`になる。
それぞれ指定した内容によって以下の範囲を検索対象とする。
| SearchGroup | 対象範囲 |
|:-|:-|
|  ALL FIELDS	 | 検索可能なすべての項目  |
| EMAIL FIELDS	  |  メール項目のみ |
|  NAME FIELDS	 | 標準オブジェクトの名前のみ  |
|  PHONE FIELDS	 |  電話番号項目のみ |
| SIDEBAR FIELDS	|サイドバーのドロップダウンリストに表示される有効なレコード | 

## `RETURNING ObjectsAndFields`
テキスト検索結果で返される情報を指定することができる。RETURNINGに指定できるのは以下の要素。
```sql:
FIND 'SFDC' IN ALL FIELDS RETURNING Account(Name), Contact(FirstName,LastName)
```
こちらの例の場合だと、条件に一致した取引先オブジェクトの`Name`、取引先責任者オブジェクトの`FirstName`と`LastName`が返ってくることになる。

| ObjectsAndFields | 内容 |指定の仕方の例|
|:-|:-|:-|
| ObjectTypeName  | オブジェクト名  | ・CustomObject_c<br/> ・Contact, Lead
| FieldList  | 項目名  |・CustomObject_c(CustomField_c)<br/>・Contact(FirstName, LastName)|
| USING ListView=  | 特定のリストビュー内で検索  |・Account(Id, Name USING ListView=ListViewName)|
| WHERE conditionExpression  | 特定の条件で絞り込む  |・Account (Name, Industry WHERE Name like 'test')
| ORDER BY  | 返される結果の順序付け  |・Account (Name, Industry ORDER BY Name)
| LIMIT n  |  レコードの最大件数 |・Contact(FirstName, LastName LIMIT 10), Account(Name, Industry)
| OFFSET n  | 開始行オフセット  |・Account (Name, Industry OFFSET 25)

# Apexから扱う場合のサンプルコード
記事の中で、取得した結果には複数のオブジェクトのレコードが含まれるため`List<List<SObject>>`このように各オブジェクトごとのList型で返ってくると書いた部分について少し確認をしておく。

```apex:
String soslFindClause = 'Wingo OR SFDC';
List<List<sObject>> searchList = [FIND :soslFindClause 
                                  IN ALL FIELDS
                                  RETURNING Account(Name),Contact(FirstName,LastName,Department)];

Account[] searchAccounts = (Account[])searchList[0];
Contact[] searchContacts = (Contact[])searchList[1];
System.debug('Found the following accounts.');
for (Account a : searchAccounts) {
    System.debug(a.Name);
}
System.debug('Found the following contacts.');
for (Contact c : searchContacts) {
    System.debug(c.LastName + ', ' + c.FirstName);
}
```
実行結果。
![スクリーンショット 2022-12-17 22.05.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200812/0bf267af-9b79-30a9-0abc-30201a845eff.png)
意図した通りSOSLの結果は`List<List<SObject>>`になっており、それぞれ０番目と１番目のListに`Account[]`と`Contact[]`が含まれていた。

# SOQLとSOSLのどっちを使うべき？
## SOQLを使うべき
取得したいデータがどのオブジェクトに存在しているかがわかっていて検索するオブジェクトを特定できる場合はSOQLを使うのが良い。例えば以下のようなケース。
- 1つのオブジェクト、または相互に関連する複数のオブジェクトからデータを取得する。
- 指定された条件を満たすレコードを数える。
- クエリに含まれる結果を並び替える。
- 数値、日付、またはチェックボックス項目を指定してデータを取得する。

## SOSLを使うべき
取得したいデータがどのオブジェクトまたは項目に存在しているかわからず検索するオブジェクトが特定できない場合はSOSLを使うのが良い。例えば以下のようなケース。
- 項目内に存在することがわかっている、特定用語のデータを取得する。
- 複数のオブジェクトおよび項目を効率的に検索し取得する。

# 最後に
今日は昨日のSOQLに続きSOSLについてみていきました。それぞれ特徴があるので、利用シーンに合わせて使い分けていきましょう。
明日はVisualforceのところで触れ忘れていたコントローラについてみていきます。