【Firebase】検索処理と検索結果でページング処理【タイムライン】
前書き
前回こういった記事を書きました。
次は検索機能を追加し、検索ワードに絞って、タイムライン機能を実装させたいといった内容です。
前回の記事を見ないと「なんのこっちゃ」になるかもしれません。
【実装】 Home.vue
<v-form color="primary"> <v-text-field v-model="searchWord" placeholder="1ワードまで" label="検索ワード(タイトル名)" type="text" /> <v-text-field v-model="searchUser" placeholder="1ユーザーまで" label="検索ユーザー" type="text" /> <p>検索タイトル「{{searchWordInput}}」 検索ユーザー「{{searchUserInput}}」で検索します!!</p> <v-btn @click="search" color="primary">検索</v-btn> </v-form>
data() { return { pagingToken: "", searchWord: "", searchUser: "", maps_data: null }; }, computed: { searchWordInput: function() { return this.searchWord; }, searchUserInput: function() { return this.searchUser; } }, }
検索フォームを作成しました。検索欄の中身が変わると、computedプロパティで検知してくれます。
search: async function() { this.pagingToken = ""; // 検索欄の中身が空白の場合→全検索 if (this.searchWordInput != "" || this.searchUserInput != "") { let data = await getSearchData( 3, this.searchWordInput, this.searchUserInput, this.pagingToken ); let buffData = await downloadImageToBox(data.BuffData); this.maps_data = buffData; this.pagingToken = data.nextPageToken; } // 検索欄の中身が空白でない場合→キーワード検索 else { let data = await getAllData(3, this.pagingToken); let buffData = await downloadImageToBox(data.BuffData); this.maps_data = buffData; this.pagingToken = data.nextPageToken; } }
ここでは検索ボタンを押した後の処理をmethodsに記述しています。
検索欄の中身が空白でない場合、キーワード検索を実行するようにします。
前回の記事で説明しましたが、pagingTokenはTLに表示されるべき最後の投稿の日時を格納しています。
ページング処理ではロードボタンを押した時に取得する投稿は、pagingTokenの時間情報を頼りに取得します。
しかし検索ボタンを押す際はpagingTokenをリセットする必要があります。
【実装】auth.js
export async function getSearchData(limit, searchWord, searchUser, pagingToken) { let nextToken = ""; let query = db.collection("comments").orderBy('createdAt') if (pagingToken != "") { const [seconds, nanoseconds] = pagingToken.split(':'); const timestamp = new firebase.firestore.Timestamp(seconds, nanoseconds); query = query.startAfter(timestamp); } if (searchUser == "" && searchWord != "") { query = query.where('title', '==', searchWord).limit(limit); } else if (searchUser != "" && searchWord == "") { query = query.where('displayName', '==', searchUser).limit(limit); } else if (searchUser != "" && searchWord != "") { query = query.where('displayName', '==', searchUser).where('title', '==', searchWord).limit(limit); } const result = await query.get().then((snapshot) => { if (snapshot.docs.length >= limit) { const last = snapshot.docs[snapshot.docs.length - 1]; const lastData = last.data(); const time = lastData.createdAt; nextToken = `${time.seconds}:${time.nanoseconds}`; } return { "BuffData": snapshot, "nextPageToken": nextToken }; }).catch(() => { alert("エラーが発見されました:データ取得時"); }); return result; }
解説
検索欄に文字が存在する場合に検索ボタンを押した時の処理です。
前回の全検索におけるページング処理と大体が同じ構造になっています。
検索処理
let query = db.collection("comments").orderBy('createdAt') ・・・ if (searchUser == "" && searchWord != "") { query = query.where('title', '==', searchWord).limit(limit); } else if (searchUser != "" && searchWord == "") { query = query.where('displayName', '==', searchUser).limit(limit); } else if (searchUser != "" && searchWord != "") { query = query.where('displayName', '==', searchUser).where('title', '==', searchWord).limit(limit); }
こちらでは取得したキーワード(ワードorユーザー)ごとに検索を行います。
正直もう少し綺麗なコードを書けるだろと思ったのですが、妥協しました。
また部分検索(Like検索)はFireStoreでは使えないようなので諦め。
キーワードの先頭部分一致は実現できるようですが、以下の記事を参考に。
結果
投稿ユーザーを”Nagoya”に指定すると、"名古屋"がタイトルの投稿になります。