Salesforce SQL (正しくは SOQL) 実行例

本記事では Salesforce への接続の方法とかではなく、各モデルに対するアクセスの方法とかの例をあげます。
他の SQL は使ったことあるけど SOQL ってなんやねん、という初心者向けです。数ヶ月前の自分がほしかったものをただ作っています。細かいところ間違ってたらすみません

例の中では、API 実行クライアントは https://github.com/heroku/databasedotcom の、なんだか古そうなバージョンを用いています。**非推奨**なので、どうしても移行はできないけど分からないことがあるって人だけこの記事を読んでください。 Ruby で叩いていて、実行例中の `client` メソッドが、接続用のオブジェクトを返してくれています

# 全モデル?での共通事項

* `xxxx__c` のように、 `__c` がついているのは、カスタムに作成したフィールド(RDBでいうところのカラム?)です（ SalesForce の標準的なフィールドではない)
* `xxxx__r` のように、 `__r` がついているのは、よくわからないが SELECT に指定しても取得できない
* Id フィールドはどのモデルにも必ずあるようです

## アクセス例

レコード1件について、Id フィールドを取ってみます

```rb
> user = client.query("SELECT Id FROM User Limit 1").first
=> #<SFDC_Models::User:0x00555ce678fbe8
# ~~~~ (中略) ~~~~
 @CreatedById=nil,
 @CreatedDate=nil,
 @CurrentStatus=nil,
 @DefaultGroupNotificationFrequency="N",
 @DelegatedApproverId=nil,
 @Department=nil,
 @DigestFrequency="N",
 @Division=nil,
 @Email=nil,
# ~~~~ (中略) ~~~~
 @FullPhotoUrl=nil,
 @Id="xxxx",
 @IsActive=nil,
# ~~~~ (後略) ~~~~
```

Id と、なぜだかいくつかの Frequency というフィールドに値がセットされて取得できています。他のフィールド名を見ると、確かに人を表しているような雰囲気がします。

悲しいことに Salesforce の API は、 `SELECT * FROM` のように全カラムを要求することはできないので、ほしいフィールド名を並べる必要があります

フィールド一覧を頑張って生成してみました

```rb
> attributes = {}
> model = 'User'
> model_methods = client.query("SELECT Id FROM #{model} Limit 1").first.methods
> attributes[model] = model_methods.select { |m| m[/^[A-Z].+=$/] }.map { |m| m.to_s.sub(/=/, '') }.reject { |m| m[/__r$/] || model_methods.include?("#{m}Id=".to_sym) }.join(',')
```

無理やりですね。。。名前がアッパーキャメルであること、代入メソッドが定義されていること、Id によって関連モデルを参照できること、を利用して抽出してます。これは、他に何かいい方法があるのかもしれません

# 各モデルについて

## User
Salesforce にログインしたりすることのできるユーザーを表しています。

データを作ったりすると、CreatedById に User の Id が入ります。

## Account
お客様・取引先を表します。

### AccountFeed
Account に対して Feed を持っています。このお客様とはどういうやりとりをした、ということをこちらのみのメモとして記録することができるようになっています。

AccountFeed は API で作ることができますが、AccountFeed 自体に対して create をコールすることはできず、 FeedItem を create します。AccountFeed の親クラスのようなものと考えると分かりやすいと思います (あくまで喩えで、「継承先でなぜパブリックメソッドが呼べないのか」とかは考えないでください...)

作成の際の引数の ParentId に、対象の Account の Id を入れると、 AccountFeed を作ることができます。

```rb
> client.create("FeedItem", { "Body" => "テスト","ParentId" => "xxxx","Type" => "TextPost" }) 
=> #<SFDC_Models::FeedItem:0x0055d4ad20a3c8
 @Body="テスト",
# ~~~~ (中略) ~~~~
 @Id="yyyy",
 @InsertedById=nil,
# ~~~~ (中略) ~~~~
 @ParentId="xxxx",
 @RelatedRecordId=nil,
 @SystemModstamp=nil,
 @Title=nil,
 @Type="TextPost">
```

指定したキーと Id のフィールドだけがセットされて返却されました(ちなみに、SELECT 文にて全ての Field を指定すると、 LastModifiedDate など自動でセットされそうなデータも取れます)。

ちなみに InsertedById を指定することはできませんでした。当たり前っちゃ当たり前ですかね。InsertedBy は、 API の実行ユーザになります。

```
Unable to create/update fields: InsertedById.Please check the security settings of this field and verify that it is read/write for your profile or permission set.
```


## Case
お問い合わせ、つまりは Account とのやりとりを表します。


# 遭遇したエラー

## IS NOT

`IS NOT` は使えず `!=` はいけそうです

```rb:失敗
> client.query("SELECT Id FROM Case WHERE sample__c IS NOT NULL Limit 1")
Databasedotcom::SalesForceError: 
Subject FROM Case WHERE sample__c IS NOT NULL Limit 1
                                    ^
ERROR at Row:1:Column:48
unexpected token: 'IS'
from /usr/src/app/vendor/bundle/ruby/2.3.0/gems/databasedotcom-1.3.5/lib/databasedotcom/client.rb:376:in `ensure_expected_response'
```

```rb:成功
> client.query("SELECT Id FROM Case WHERE sample__c != NULL Limit 1")
=> [#<SFDC_Models::Case:0x007f993fe71708
# ~~~~ (後略) ~~~~
```

## FeedItem の参照は Id が必要

```rb
> client.query("SELECT Id FROM FeedItem Limit 1").first
Databasedotcom::SalesForceError: Implementation restriction: FeedItem requires a filter by Id
from /usr/src/app/vendor/bundle/ruby/2.3.0/gems/databasedotcom-1.3.5/lib/databasedotcom/client.rb:376:in `ensure_expected_response'
```

AccountFeed だと要らないです。複数のモデルを束ねた?インターフェースであることに起因しているのでしょうか。