この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは!コンサル部のテウです。
前回に続いて 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.graphql
の SearchablePostFilterInput は下のようにアップデートされます。
...
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