Apex_SOSLでグローバル検索(匿名実行)

Salesforceの開発者コンソール（Open Execute Anonymous Window）を使用して、匿名実行でグローバル検索するコード作成したよ！

全オブジェクト、Apexクラスなどのコードも対象にするよ！

以下、サンプルコードだよ！開発者コンソールの匿名実行で実行してみてね！
```
// すべてのSObject（標準オブジェクト、カスタムオブジェクト含む）の定義を取得し、
// キーにAPI名、値にSchema.SObjectTypeを持つMapとして返される
Map<String, Schema.SObjectType> globalDescribe = Schema.getGlobalDescribe();

// 取得したMapからオブジェクトAPI名のリストを作成
List<String> objectNames = new List<String>(globalDescribe.keySet());

// オブジェクトAPI名をアルファベット順にソート
objectNames.sort();

// 検索したいワード
String searchKeyword = 'Test';
// シングルクォートをエスケープして文字列を生成
String soslKeyword = String.escapeSingleQuotes(searchKeyword);
// ワイルドカード（*）を付ける
soslKeyword = '*' + soslKeyword + '*';
System.debug('検索値：　' + soslKeyword);

// SOSL文の「RETURNING句」を組み立てるための変数を初期化
String returningClause = 'RETURNING ';

// ソートしたオブジェクト名のリストを走査
for (String objectName : objectNames) {
    // このオブジェクトの詳細情報を取得
    Schema.DescribeSObjectResult objDescribe = globalDescribe.get(objectName).getDescribe();

    // 検索可能かどうか判定（isSearchableがtrueのものだけを対象にする）
    if (objDescribe.isSearchable()) {
        // 「オブジェクト名(Id,」の部分をRETURNING句に追加
        returningClause += objectName + '(Id, ';

        // 全フィールド定義を取得（キーがフィールドAPI名、値がフィールド定義）
        Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap();

        // Nameフィールドが見つかったかどうかのフラグ
        Boolean foundNameField = false;

        // 全フィールド名を走査して「Name」と一致するものがあるか確認
        for (String fieldName : fieldMap.keySet()) {
            // フィールド名がName（大小文字無視）なら
            if (fieldName.toLowerCase() == 'name') {
                // RETURNING句にNameを追加
                returningClause += fieldName;

                // フラグを立ててループを抜ける
                foundNameField = true;
                break;
            }
        }

        // Nameフィールドが無かった場合は文字列型またはテキストエリア型のフィールドを検索
        if (!foundNameField) {
            for (Schema.SObjectField field : fieldMap.values()) {
                // 各フィールドの詳細を取得
                Schema.DescribeFieldResult fieldDescribe = field.getDescribe();

                // String型またはTextArea型なら
                if (fieldDescribe.getType() == Schema.DisplayType.String 
                    || fieldDescribe.getType() == Schema.DisplayType.TextArea) {
                    // RETURNING句にフィールド名を追加
                    returningClause += fieldDescribe.getName();
                    // 一つ見つかったら抜ける
                    break;
                }
            }
        }

        // 1つのオブジェクト分のRETURNING句を区切る（カンマとスペースを加える）
        returningClause += '), ';
    }
}

// 末尾に残っている「, 」を削除してRETURNING句を整える
returningClause = returningClause.removeEnd(', ');

// 検索キーワードを動的に組み込んだSOSL文の作成
String soslQuery = 'FIND {' + soslKeyword + '} IN ALL FIELDS ' + returningClause;
        

// 組み立てたSOSL文を実行し、検索結果を「List<List<SObject>>」形式で受け取る
List<List<SObject>> searchResults = Search.query(soslQuery);

// 各オブジェクトごとの検索結果のリストを走査
for (List<sObject> objectResults : searchResults) {
    // レコード1件ごとに処理
    for (sObject obj : objectResults) {
        // レコードが属するオブジェクトのAPI名を取得
        String objectApiName = obj.getSObjectType().getDescribe().getName();

        // レコードが属するオブジェクトのラベル名を取得
        String objectLabel = obj.getSObjectType().getDescribe().getLabel();

        // レコードのIdを取得
        String id = obj.Id;

        // Nameや文字列型フィールドの値を保持する変数を用意
        String nameOrLabel = '';

        // 改めてこのレコードのオブジェクトの詳細情報を取得
        Schema.DescribeSObjectResult objDescribe = obj.getSObjectType().getDescribe();

        // フィールド定義のマップを取得（API名 -> 定義オブジェクト）
        Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap();

        // Nameフィールドを見つけたかどうかのフラグ
        Boolean foundNameField = false;

        // 全フィールド名を走査してNameフィールドがあるか確認
        for (String fieldName : fieldMap.keySet()) {
            // フィールド名がNameなら
            if (fieldName.toLowerCase() == 'name') {
                // 実際の値をobjから取得してnameOrLabelに格納
                nameOrLabel = String.valueOf(obj.get(fieldName));
                // フラグをtrueにしてループを抜ける
                foundNameField = true;
                break;
            }
        }

        // Nameフィールドが無い場合は文字列型かテキストエリア型を探索
        if (!foundNameField) {
            for (Schema.SObjectField field : fieldMap.values()) {
                // フィールドの詳細を取得
                Schema.DescribeFieldResult fieldDescribe = field.getDescribe();

                // 文字列型かテキストエリア型か判定
                if (fieldDescribe.getType() == Schema.DisplayType.String 
                    || fieldDescribe.getType() == Schema.DisplayType.TextArea) {
                    // objから値を取り出してnameOrLabelに格納
                    nameOrLabel = String.valueOf(obj.get(fieldDescribe.getName()));
                    break;
                }
            }
        }

        // デバッグログにオブジェクトAPI名、ラベル名、Id、名前フィールド(または文字列フィールド)を出力
        System.debug(
            'オブジェクト API名 = ' + objectApiName
            + ' (オブジェクト ラベル = ' + objectLabel + ' ), '
            + 'Id = ' + id
            + ', Name/Label = ' + nameOrLabel
        );
    }
}
```

匿名実行だと、RETURNING省略できなかったから作成してみた！
何か参考になれば嬉しいな！