【AWS Amplify ノウハウ】 2. enumタイプは Search APIで使用できません

2020.07.30

こんにちは!コンサル部のテウです。

前回に続いて AWS Amplify シリーズの2回目の記事です。今回も入門者を対象としていないため、Amplifyの基礎には触れず Amplifyを直接プロジェクトに活用できそうなTipに集中して書きたいと思います。

今回は特に、Search APIを使う時、enumタイプに対する制約と、この制約から離れるための間接的な解決策(workaround)を提示します。すごく簡単なTipになるかもしれませんが、あらかじめ読んでおくと手間がかからないかもですね!

それでは始めます!:)

ん??なにができないって??

AmplifyのAPIをGraphQLタイプに選択し(実際には裏側で AppSyncが使われる)、@searchable ディレクティブを GraphQL Schemaに追加すると、Amplify CLIが自動で Search関連APIを生成してくれます。

type Post @model @searchable {
  id: ID!
  title: String!
  content: String!
  createdAt: AWSDateTime
  updatedAt: AWSDateTime
}

より具体的にすると、上記のような Postタイプに @searchable ディレクティブを追加し amplify push すると、AppSyncに登録可能なもっと詳しいSchemaファイルに変換されます。実際に amplify/backend/api/${API名}/build/schema.graphql ファイルを開いてみると、amplify/backend/api/${API名}/schema.graphql よりもっと詳しいSchemaファイルを確認することができます。

この時、Queryタイプを見ると、

...

type Query {
  getPost(id: ID!): Post
  listPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String): ModelPostConnection
  searchPosts(filter: SearchablePostFilterInput, sort: SearchablePostSortInput, limit: Int, nextToken: String): SearchablePostConnection
}

...

のように searchPosts() が自動に生成されたことが確認できます。 SearchablePostFilterInput タイプの filter パラメーターをインプットされますが、この時、 SearchablePostFilterInput を探して確認すると、

...

input SearchablePostFilterInput {
  id: SearchableIDFilterInput
  title: SearchableStringFilterInput
  content: SearchableStringFilterInput
  createdAt: SearchableStringFilterInput
  updatedAt: SearchableStringFilterInput
  and: [SearchablePostFilterInput]
  or: [SearchablePostFilterInput]
  not: SearchablePostFilterInput
}
 
...

のように、Post タイプに宣言しておいたすべての id, title, content, createdAt, updatedAt フィールドが searchable filter に登録されたことが確認できます。

さて、最初の Postタイプを一度下のように変更して amplify push を実行してみます。

type Post @model @searchable {
  id: ID!
  title: String!
  content: String!
 
  hit: Int!
  postType: PostTypeEnum!
  authorEmail: AWSEmail!
  postUrl: AWSURL!
 
  createdAt: AWSDateTime
  updatedAt: AWSDateTime
}

enum PostTypeEnum {
  ANNOUNCEMENT
  NORMAL
  ADVERTISEMENT
}
$ amplify push

そうすると、build/schema.graphqlSearchablePostFilterInput は下のようにアップデートされます。

...
input SearchablePostFilterInput {
  id: SearchableIDFilterInput
  title: SearchableStringFilterInput
  content: SearchableStringFilterInput
  hit: SearchableIntFilterInput
  authorEmail: SearchableStringFilterInput
  postUrl: SearchableStringFilterInput
  createdAt: SearchableStringFilterInput
  updatedAt: SearchableStringFilterInput
  and: [SearchablePostFilterInput]
  or: [SearchablePostFilterInput]
  not: SearchablePostFilterInput
}
...

あれ?ちょっと待って!?ここでおかしいことに気づきましたか?

PostTypeEnum タイプの postType フィールドに対するフィルターが生成されておりません! Amplify 公式ドキュメントから調べてみると、

A complete list of searchable operations per GraphQL type supported as of today

enum は @searchable でサポートしていないですね。。

数日前までもAmplify GithubのIssuesではこの機能をサポートしてください!という要請があふれております。

Unable to search on enum field with @searchable #3248

どう解決すればいいでしょうか??

実は、私も特にすごいノウハウやTipがあるわけではないです。ただし、このようなissueがあることを広めて、私がどのようにこんなissueを回避してAmplifyを活用しているのかに対する些細な意見をシェアしたくてブログを書いています。

同じフィールド、違うタイプを使用する

結論から言いますと、私は下記のような String タイプのフィールドをもう一つ追加することで workaround のアプローチをしています。

type Post @model @searchable {
  id: ID!
  title: String!
  content: String!
 
  hit: Int!
  postType: PostTypeEnum!
  authorEmail: AWSEmail!
  postUrl: AWSURL!
 
  createdAt: AWSDateTime
  updatedAt: AWSDateTime
 
  ## temporary searchable fields
  _postType: String! # postType を Searchable フィルターとして使うための臨時フィルター
  ...
}

enum PostTypeEnum {
  ANNOUNCEMENT
  NORMAL
  ADVERTISEMENT
}

つまり、データを生成するとき、PostTypeEnumタイプの変数を下にようにアサインし、データを生成することです!

...
postInput.postType = PostTypeEnum.NORMAL;
postInput._postType = PostTypeEnum.NORMAL;
...

実際に、javascript では enumタイプがStringと同じく扱われるので、このようなアプローチが動作します。そして、検索時にはStringタイプとして宣言された _postType フィールドを活用し、 _postType でフィルターリングが可能です。

こんな厄介なことをしろーって?

もちろん PostTypeフィールドを生成/修正するたびにこのような作業をすることは厄介なことだと思いますが、Amplifyを活用するターゲットは迅速にMVPを作ろうとする初期スタートアップだということを考えると、これぐらいの厄介さは十分耐えられるのではないかと思っております。Schema宣言だけでもさらに厄介なCRUD APIはもちろん、Search APIまで自動で生成してくれるからです!

最後に

今回のブログでは、Search API を活用する時、 enum タイプで宣言されたフィールドが Searchable フィルターに含まれないことと、これを解決するための簡単な workaround についてシェアしました!

以上、コンサル部のテウでした!:D