QiitaトレンドをリニューアルしてQiiTrendを作った

約2年前にQiitaトレンドというクライアントサイドだけで動くツールを作りましたが、この度、名称をQiiTrend(キートレンド)と改めWebサービスとしてリニューアルして公開しました。

- 旧版
    - [Qiitaトレンド](http://bl.ocks.org/tag1216/raw/819ded0722cedf75996f/)
    - [React.js使ってQiitaトレンド作ってみた - Qiita](http://qiita.com/tag1216/items/5ab9ca42528d428ad72e)
- 新版
    - [QiiTrend](https://qiitrend.herokuapp.com/)

# QiiTrendとは

Qiitaの投稿件数を時系列チャートで表示するサービスです。
Googleトレンドのようにプログラム言語やフレームワークの人気の動向を調べることができます。

<img width="600" alt="QiiTrend_トップ画面_2017_02_27.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/09b0c45e-a401-13dd-2388-c1e735bfdae0.png">

<img width="600" alt="QiiTrend_チャート画面_2017_02_27.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/152358ea-889b-d012-2f9d-1edae879819b.png">

# 使い方

## 検索クエリーの入力

<img width="400" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/a60dab6b-56ee-aeac-ede5-aaf17d096f9b.png">


チャートに表示したい投稿の検索条件を検索ボックスに入力します。
ここは[Qiitaと同じ検索オプション](http://qiita.com/search)が使えます。
例えば、Pythonタグが付いた投稿について調べたい場合は、「tag:python」と入力してEnterを押します。
チャート画面に遷移してしばらくすると投稿件数の推移がチャートに表示されます。

尚、何も入力しなかった場合はQiitaの全投稿件数がチャートに表示されます。

## 複数項目の比較
<img width="300" alt="貼り付けた画像_2017_02_27_23_11.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/f2f68e7e-bf0d-e0f1-b3ff-1a9dc0e74411.png">

左上の「検索クエリーの追加」で検索条件の入力してEnterを押すと、現在表示しているチャートにラインが追加されます。

## 表示期間の変更
<img width="300" alt="貼り付けた画像_2017_02_27_23_10.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/7653f0f3-a171-8c9a-7084-9210b8d0e292.png">

表示期間は現在を基準にして過去3ヶ月から全期間(2011年以降)まで選択できます。

月単位だとデータが多いので取得に時間がかかり全て取得できない場合があります。

<img width="300" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/59dc2472-7e4a-a5d3-4656-dcdf3af08456.png">

この場合はしばらくしてからブラウザを再読み込みしてください。

## モードの変更

<img width="300" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/5449caaa-0ecc-1b92-2535-a8bf2f85672e.png">

「投稿数」を表示するモードと「全投稿に対する割合」を表示するモードを選択できます。

Qiitaの年間投稿数は毎年増加している為、「投稿数」でチャートを表示するとどれも右肩上がりになってしまい、実際に人気が上がっているのか下がっているのかわかりません。
また、チャートの一番右側はまだ期間の途中である為、値が小さくなってしまいます。

「全投稿に対する割合」で表示することでこの２つの問題を解決できます。

<img width="600" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/e0338a2a-f31b-5663-c70b-9a25b8cbef85.png">

<img width="600" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/27ef11cd-b47e-7a36-6166-4c01d8ba7b82.png">

## チャートの共有

<img width="535" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/7f998afb-6226-2bbd-8efc-d894f48c1c6a.png">

検索クエリーや表示期間といったチャートの条件はブラウザのURLに反映されています。
URLをそのままコピーすればチャートを共有することができます。

## Qiitaアカウントでログインしてアクセス数上限を暖和する

短期間にアクセスを繰り返していると次の様なメッセージが表示されることがあります。

<img width="400" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/4cbb5453-a9ea-b1d2-91cd-c99b9a618c72.png">

QiiTrendでは検索クエリーの追加などチャートの表示内容を変更する度にサーバーにアクセスします。
未ログイン状態では、1時間あたりのアクセス可能数はIPアドレス毎に30回までに制限しています。

Qiitaアカウントにログインするとユーザー毎に1時間あたり500回までに暖和されます。

ログインは右上のボタンからOAuth認証で行います。
<img width="400" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/9f447c20-ffb7-25a7-5850-1c174e985546.png">

ログイン後はQiitaアカウントのアイコンが表示されます。
<img width="400" alt="QiiTrend.png" src="https://qiita-image-store.s3.amazonaws.com/0/35192/a04a08b2-8992-f150-45a4-ba3ed635292d.png">

# 中身の話

## システム構成

システム構成は次の様になっています。

- フロントアプリ
    - Reactで作ったSPA(シングルページアプリケーション)です。
- バックエンド
    - PythonのDjangoで作ったAPIサーバーです。
    - Herokuで稼働しています。
- キャッシュ
    - Redisに投稿件数をキャッシュしています。

## データ取得の仕組み

チャートに表示する投稿件数のデータは次の様な仕組みで取得しています。

```
Client              QiiTrend               Qiita 
+---------+   (1)   +------------+         +-----------+
|Front App|+------->| Backend    |         |Search Page|
|         |<-------+|            |   (4)   |           |
|         |   (3)   |            |+------->|           |
|         |         |            |         |           |
|         |   (6)   |            |         |           |
|         |+------->|            |         |           |
|         |<-------+|            |         |           |
+---------+   (7)   +------------+         +-----------+
                      +        +
                      |        |
                   (2)|        |(5)
                      |        |
                      V        V
                    +------------+
                    + Redis      +
                    +------------+
```

1. ユーザーがクエリーの追加などを行うと、フロントアプリはバックエンドへ投稿件数取得のリクエストを送ります。
2. バックエンドはRedisのキャッシュから投稿件数を取得します。
3. この時キャッシュに値がなかった場合でもデータのないレスポンスを即返します。
4. バックエンドはリクエストとは別スレッドでQiitaから投稿件数を取得します。
5. Qiitaから取得した投稿件数はキャッシュに保存します。
6. フロントアプリは、サーバーからのレスポンスにデータが含まれていなかったら、数秒後に再度リクエストを送ります。
7. 今度はキャッシュに値があるのでデータの含まれているレスポンスを受け取り、チャートを表示します。

## 投稿件数の取得方法

ユーザーが入力した検索クエリーを、バックエンドでは日付付きの検索クエリーに変換します。

[tag:python] -> [tag:python created:2017],[tag:python created:2016] ...

これをQiitaの検索ページにリクエストして件数を取得しています。

QiitaAPI(/api/v2/items/)ではなくQiitaの検索ページ( http://qiita.com/search )から取得している理由は、Qiita APIでは以下の様な不都合が生じてしまうためです。

- 認証済み状態でAPIで投稿を取得すると、認証ユーザーの限定共有投稿も含まれてしまう。
- ユーザー毎に取得した投稿件数の値が変わってしまうので、取得した投稿件数をキャッシュして全ユーザーで共有するのは好ましくない。
- 未認証状態で投稿を取得すれば限定共有投稿は含まれないが、IPアドレス毎に1時間60回までという制限があり、サーバーサイドで使うのは無理。

尚、Qiitaへのアクセスはフロントからのリクエストを処理するスレッドとは別にしてあり、秒間リクエスト数を制限しています。
これにより、QiiTrendに大量のアクセスがあったとしてもQiitaへ過度な負荷がかからないようにしてあります。

## 使っているもの

*がついているものは今回初めて触った技術です。

- バックエンド
    - [Python3.5](https://www.python.jp/)
    - [Django](http://djangoproject.jp/)
    - [Django REST framework](http://www.django-rest-framework.org/)
    - *[django-webpack-loader](https://github.com/owais/django-webpack-loader)
- データベース
    - [PostgreSQL](https://www.postgresql.org/)
    - *[Redis](https://redis.io/)
- フロントエンド
    - *[npm](https://www.npmjs.com/)
    - *[webpack](https://webpack.github.io/)
    - *[Babel](https://babeljs.io/)
    - *[ES2015](https://babeljs.io/learn-es2015/)
    - [React](https://facebook.github.io/react/)
    - *[React Router](https://github.com/ReactTraining/react-router)
    - *[Material-UI](http://www.material-ui.com/)
    - *[Recharts](http://recharts.org/)
    - *[react-ga](https://github.com/react-ga/react-ga)
- サーバー
    - *[Heroku](https://heroku.com/)

今回初めて触れる技術が多く、特にフロント側のビルドでわからないことが多く苦労しました。
ただ実装はES2015で書けるようになったのでやりやすかったです。

# 今後の展開

今後の展開については3つの方向で考えています。

## トレンドチャートの拡充

今回作ったトレンドチャート自体の機能拡充として次のようなことを考えています。

- 順位で表示するモードの追加
    - [2015年Qiitaランキング - Qiita Blog](http://blog.qiita.com/post/136855781724/qiita2015ranking) のように順位で表示するモードを追加しようかと思ってます。

## トレンドチャート以外の機能

具体的には考えていませんが、何か追加するかもしれません。

## Qiita以外に展開

今回作った仕組みは、日付で検索できるAPIや検索ページを提供しているサービスであればQiita以外にも適用できます。
GitHubやStackOverflowなど、エンジニア向けサービスでは日付検索を提供しているものが多いので、これら向けのものも作るかもしれません。