- 個人ブログのタグで絞り込みの書き方がイケてないのを思い出した
- APIによるタグの絞り込みが方法がわからなかったので下記のように全記事を取得してきてjsでフィルタをしていた。記事が少ない&ページングもしてない今は良いが、記事が増えたら破綻するので解消する。
- リポジトリ
export const getArticleByTag = async (slug: string) => { const { items } = await client.getContents<Article>({ appUid: 'manas-diary', modelUid: 'article', query: { select: ['_id', 'title', 'slug', 'tags', 'body', 'icon', 'summary'], }, }) const tagItems = items.filter( (item) => item.tags.filter((tag) => tag.slug === slug).length ) return tagItems }
結論
- 絞り込みは『コンテンツID(
_id
)』で実施する - slugはIDではない
export const getArticleByTag = async (tagId: string) => { const { items } = await client.getContents<Article>({ appUid: 'manas-diary', modelUid: 'article', query: { select: ['_id', 'title', 'slug', 'tags', 'body', 'icon', 'summary'], tags: { in: [tagId], }, }, }) return items }
結論が出るまで
気づくまで
公式にできそうな雰囲気のことは書いてある
- Newt CDN API | Newt API Reference
- Filtersの項のInclusionを使えばよさそうだが、slugを投げても投げてもかえってこない。エラーが出る。
ふと気づく。『slugって、idじゃないかも』
タグの詳細を確認。名前とslugしかない。ここで撤退してはいけない。左上のページを開くをクリックする。
ページを開いた後。右側、ステータスの青いラベルのすこし下に『コンテンツID』が存在している。コードに直貼りで試したところ絞り込みが動いた。つまり、『絞り込みで必要になるIDはコンテンツIDのこと』である。
今のコードを理解しなおす
- NewtとNext.jsを利用してブログを作成するを参考にペタペタ構築したのだが、この記事の
getArticleBySlug
の理解が間違っていた。私はこれを『IDで取得する処理(内部的にarticle/[slug]
のようなものになる)』と理解していた。 - だがQueryに入っているということは、CDN APIのドキュメントのQueriesの欄に該当する
- つまりここはEquality operatorで合致するslugにフィルタされているだけ
export const getArticleBySlug = async (slug: string) => { const article = await client.getFirstContent<Article>({ appUid: 'manas-diary', modelUid: 'article', query: { slug, select: [ '_id', 'title', 'slug', 'tags', 'body', 'icon', 'summary', 'publishDate', ], }, }) return article }
- その点を理解しなおしたうえで、Queriesの下の注意をしっかり読む。
参照先に対してクエリをかけたい場合、_id に対してのみクエリをかけられる仕様となっております。その他のフィールドに対してクエリをかけることはできません。
- 先のコードの通り
_id
というフィールドはコード上にはりつけていたがしっかり認識してなかった - というわけで再掲になるが、下記のように修正。
getArticleBysTag
の引数はslug
ではなくtagId
に変更- queryにtagsでの絞り込みを追加。
- このブログではtagsは複数参照フィールドなので、一致検索ではなく包含検索で行う。複数参照フィールドは一致検索ができない。
export const getArticleByTag = async (id: string) => { const { items } = await client.getContents<Article>({ appUid: 'manas-diary', modelUid: 'article', query: { select: ['_id', 'title', 'slug', 'tags', 'body', 'icon', 'summary'], tags: { in: [id], }, }, }) return items }
おわり
- おわり。コピペするときも自分が何を書いたのかちゃんと理解しようね案件だった
- 特定の1つの記事をとるのに、Newtのように絞り込む手法と
GET /article/id
とする手法があると思うが、どちらが良いか甲乙つけがたいなと思う。- バックエンドで0件確認して404にしたとて、フロントでも404だから………という処理が入るので、『SQLでとれたデータをそのまま返す』の方がバックエンド・フロントエンド統一した流れで見るとシンプルで良いかもしれない。
- ちなみにslugって何?についてはMDNでは
通常は URL の最後にある Web アドレスの固有の識別部分
と説明されている- なら、ID同じでも良くない??という気持ちもあるが、確かに『DB上で管理するためのID』と『スコープ内で固有の人間が読める識別子』は分けておいた方がシステム的に良い気がする。納得した。