- 個人ブログのタグで絞り込みの書き方がイケてないのを思い出した
- 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
}
結論が出るまで
気づくまで
今のコードを理解しなおす
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』と『スコープ内で固有の人間が読める識別子』は分けておいた方がシステム的に良い気がする。納得した。