Amplify GraphQLで[Int]型のフィールドでContainsオペレーターを利用する

結論

AWS Appsyncのスキーマで該当する型(ModelIntInput)を手動でアップデートし、contains: Intを追加する。

はじめに

AWS AmplifyではデータベースとしてGraphQLを利用することができ、ユーザはGraphQLスキーマを定義するだけでAppSyncやDynamoDBの設定をすることなく利用することができます。 しかし、クエリのfilterで、[Int]型のフィールドに対して、本来利用ができるはずのcontainsオペレータが利用ができず苦しんだので備忘録を残しておきます。

Version

"aws-amplify": "^5.3.3"

Example schema

以下のようなUserテーブルがschema.graphqlとして定義されているとします。

type User @model @auth(rules: [{ allow: public }]) {
  id: ID!
  favoriteNumbers: [Int]!
}

ここで、

  • id: ユーザに一意に付与されるID
  • favoriteNumbers: ユーザが好きな数字のリスト

とします。 こちらのスキーマ定義をamplify push apiすると、User tableに対するqueryやmutationを実行することができます。

Contains operator

ここで、User tableからUserのリストを取得することを考えます。 GraphQLクエリは以下のようになるでしょう。

const listUsers = /* GraphQL */ `
  query ListUsers($filter: ModelUserFilterInput, $limit: Int, $nextToken: String) {
    listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
      items {
        id
        favoriteNumbers
      }
    }
  }
`;

ここで、「ある特定の数字が好きなユーザのリストを取得する」ことを考えます。すると、各レコードのfavoriteNumbersに格納されている[Int]型の値の中に特定のInt値が含まれているかどうかを確認する必要があります。

AmplifyでGraphQLを利用する時には裏側でDynamoDBを利用することになりますので、DynamoDBでこのような演算子が利用できるかを確認します。

Comparison operator and function reference - Amazon DynamoDB

こちらに、

A List that contains a particular element within the list.

とあることから、containsというオペレータを利用することで、[Int]に特定の要素が含まれているレコードを取得することができそうです。 しかし、上記のlistUsersを用いてクエリを実行しても、うまくfliterが効かないです。 以下は、ある特定の数字numをお気に入りにしているユーザの一覧を取得するための関数fetchUsersです。

import { API } from "aws-amplify";

const listUsers = /* GraphQL */ `
  query ListUsers($filter: ModelUserFilterInput, $limit: Int, $nextToken: String) {
    listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
      items {
        id
        favoriteNumbers
      }
    }
  }
`;

const fetchUsers = async (num)=> {
  const variables = {
    filter: { favoriteNumbers: { contains: num } },
  };
  const response = await API.graphql({ query: listUsers, variables: variables });
}

AppSync

AppSyncは、AWSフルマネージドサーバーレス GraphQL API サーバーです。 該当するAPIの「スキーマ」を確認し、フィールド favoriteNumbersに対応するスキーマを確認すると

input ModelIntInput {
    ne: Int
    eq: Int
    le: Int
    lt: Int
    ge: Int
    gt: Int
    between: [Int]
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
}

となっていることが分かります。どうやら、ここで利用可能なオペレータを定義しているようです。 ここでcontainsオペレータを手で追加してください。そうすればcontains オペレータを用いたfilterが有効になります。

input ModelIntInput {
    ne: Int
    eq: Int
    le: Int
    lt: Int
    ge: Int
    gt: Int
    between: [Int]
    contains: Int
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
}

注意

このAppSyncスキーマamplify push apiを実行するたびにリセットがされるため、containsオペレータを利用するためには毎回手動でAppSyncのスキーマを編集しなければいけません。 なお、Int型以外のString型に対しては自動でcontainsオペレータが追加されますので上記の操作は必要ありません。