# HOW TO GRAPHQLでGraphQLの勉強をはじめました その⑩
# Filtering, Pagination & Sorting
Subscriptionの章が思いのほか時間がかかってしまったので今日はやめておこうと思ったのですが、SAMI-Tのダンスホールレゲエミックスを聴いてたら調子出てきたので更に進めてみることにしました。
今回でチュートリアルとしては最後でLinkをfeedクエリを使って取ってくる時にフィルタリングしたりページネーションしたりしましょうというもの。
# Filtering
Prismaによって手間なく実装することができるとのこと。 最初のステップはfeedの定義にfilterの文字列を受け付けるようにする。そして、今回はURLもしくはdescriptionに該当する文字列が含まれるものをfilteringしてくるという実装にします、と。
まずはschema.graphqlのfeedのクエリに(filter: String)というを追加する。
type Query {
  info: String!
  feed(filter: String): [Link!]!
}
そして、feedリゾルバで新しいパラメーターをクライアントが送れるようにする。
async function feed(parent, args, context, info) {
  const where = args.filter ? {
    OR: [
      { description_contains: args.filter },
      { url_contains: args.filter },
    ],
  } : {}
  const links = await context.prisma.links({
    where
  })
  return links
}
だいぶ変わった感ある。filterに文字列があったら〜という形で、もしなければwhereは空になるので、何もフィルターはかからないというような実装。
ってことで、さっそくテストしてみます。 👇のようにQLでフィルタリングすることが出来ました。

もちろんyosidaがURLに入っているレコードも👇こんなクエリで
query {
  feed(filter:"yoshida") {
    id
  	description
    url
    postedBy {
      id
      name
    }
  }
}
👇こんな風に取得できます。
{
  "data": {
    "feed": [
      {
        "id": "ck7yczojg73ns0981xit5ua4k",
        "description": "blog web site",
        "url": "yoshida.red",
        "postedBy": {
          "id": "ck7wsgfxg50jn0981qfwdhnqc",
          "name": "Alice"
        }
      }
    ]
  }
}
# Pagination
ページネーションの実装はトリッキーなところがあって、メジャーなやり方としては以下の2つ。(まぁアクセスのされ方とか、サーバーとデータベースのどっちのリソースが潤沢かとかによっても変わってくるヤツですよねぇ)
- Limit-Offset: スタートの行番号から何番目まで取ってくるというやりかた
 - Cursor-based: 取得したリストにあるユニークID(カーソル)を使って行うやりかた
 
Prismaは両方サポートしているけど、今回はLimit-Offsetで。
- limit: firstと呼ぶ。指定されたインデックスから最初のx件を取得的な。lastを指定することもできる(最後のx件を取得〜とか)
 - start index: skipと呼ぶ。指定されていなければ最初からなので0。ページネーションはリストの最初からはじめる(もしlastを使っていたら最後)
 
ということで、feedクエリでskipとfirstを受け取れるようにschema.graphqlを変更していきます。
type Query {
  info: String!
  feed(filter: String, skip: Int, first: Int): [Link!]!
}
そして、Query.jsでskipとfirstを使えるように(ものすごく直感的ですね。。)
  const links = await context.prisma.links({
    where,
    skip: args.skip,
    first: args.first
  })
あとは諸々Prismaがやってくれるのでテストするだけですよ、と。
👇4件skipして次の2件を取得とか。お手軽すぎる、、

# Sorting
Prismaでは結果のリストをソートすることが出来ます。例えばLinkをアルファベット順にurlもしくはdescriptionで並べるとか。Hacker NewsのAPIとしてはどの順番で並べたいかをenumで列挙しておけばクライアントがどれを使うかを決めてそれを使って並べることができる、と。
といことで、schema.graphqlに以下を追加
enum LinkOrderByInput {
  description_ASC
  description_DESC
  url_ASC
  url_DESC
  createdAt_ASC
  createdAt_DESC
}
そして、feedクエリでどれでorderByをするか指定出来るように。
type Query {
  info: String!
  feed(filter: String, skip: Int, first: Int, orderBy: LinkOrderByInput): [Link!]!
}
リゾルバの実装はページネーションの時と同様に orderBy: args.orderBy を付けてあげる。
async function feed(parent, args, context, info) {
  const where = args.filter ? {
    OR: [
      { description_contains: args.filter },
      { url_contains: args.filter },
    ],
  } : {}
  const links = await context.prisma.links({
    where,
    skip: args.skip,
    first: args.first,
    orderBy: args.orderBy
  })
  return links
}
実際にURLで並べる形で叩いてみると、たしかにそれっぽい感じになっております👇

# Returning the total amount of Link elements
最後に何個のLinkがデータベースに保存されているかを返すAPIを実装しましょう。FeedをLinkのリストとカウントを持つTypeにします。まずはschema.graphql
type Query {
  info: String!
  feed(filter: String, skip: Int, first: Int, orderBy: LinkOrderByInput): Feed!
}
type Feed {
  links: [Link!]!
  count: Int!
}
そしてfeedリゾルバ。なんかPrismaさん無双感あるけれども、select count(*)とか発行しないよね?とかは思ったりはしないでもない。
  // 1. filtering, ordering, そしてpaginationのパラメータを使ってLinkを取得
  const links = await context.prisma.links({
    where,
    skip: args.skip,
    first: args.first,
    orderBy: args.orderBy,
  })
  // 2. linksConnectionクエリを使ってカウントを取得
  const count = await context.prisma
    .linksConnection({
      where,
    })
    .aggregate()
    .count()
  // linksとcountをラップしたオブジェクトがFeedタイプ(GraphQLスキーマで定義した)
  return {
    links,
    count,
  }
ということで、最後はcountも取得するクエリを投げてみます。

段々PrismaとGraphQLに慣れてきた感あると思ったら終わりかーってことで、少しさみしい気分。