Full-text search is an essential feature for modern applications, enabling users to find relevant data quickly and efficiently. While Firebase Firestore doesn’t support full-text search, we can implement basic search functionality using Firestore queries.
The Search Functionality
In Vue project,
- Create new folder name composables
- In that folder create a new file getCollectionSearch.js and getCollection.js:
getCollectionSearch.js
import { projectFirestore } from "@/firebase/config";
import {
collection,
onSnapshot,
query,
orderBy,
endAt,
startAt,
limit,
} from "firebase/firestore";
import { ref, watchEffect } from "vue";
const getCollectionSearch = (collectionName, selector, field) => {
const documents = ref([]);
const error = ref(null);
const isPending = ref(false);
let querySnapshot;
const collectionRef = collection(projectFirestore, `${collectionName}`);
if (selector) {
querySnapshot = query(
collectionRef,
orderBy(field),
startAt(selector),
endAt(selector + "\uf8ff"),
limit(100)
);
} else {
querySnapshot = query(
collectionRef,
orderBy("createdAt", "desc"),
limit(100)
);
}
const unsubscribe = onSnapshot(
querySnapshot,
(snapshot) => {
isPending.value = true;
const results = [];
snapshot.docs.forEach((doc) => {
results.push({ id: doc.id, ...doc.data() });
});
documents.value = results;
isPending.value = false;
error.value = null;
},
(err) => {
console.log("err", err);
error.value = err.message;
isPending.value = false;
documents.value = null;
}
);
watchEffect((onInvalidate) => {
onInvalidate(() => unsubscribe());
});
return {
documents,
error,
isPending,
};
};
export default getCollectionSearch;
getCollection.js
import { collection, query, getDocs } from "firebase/firestore";
import { projectFirestore } from "@/firebase/config";
import { onMounted, ref, watchEffect } from "vue";
export const getCollectionQuery = (collectionName, whereDoc) => {
const collectionRef = collection(projectFirestore, collectionName);
const documents = ref([]);
const isLoading = ref(true)
// Create a query based on whereDoc (if provided)
let queryRef = collectionRef;
if (whereDoc) {
queryRef = query(collectionRef, ...whereDoc);
}
const unsubscribe = async () => {
try {
const snapshot = await getDocs(queryRef);
const data = [];
snapshot.forEach((doc) => {
data.push({ id: doc.id, ...doc.data() });
});
documents.value = data;
console.log(documents.value)
} catch (error) {
console.error("Error in getCollectionQuery:", error);
}finally{
isLoading.value = false
}
}
onMounted(() => {
unsubscribe()
})
watchEffect((onInvalidate) => {
onInvalidate(() => unsubscribe());
});
return {
documents,
isLoading
};
};
Usage in a Vue Component
Here’s how you can integrate the getCollectionSearch
and getCollection function into a Vue component:
<template>
<div class="container">
<h1>Full Text Search Using Firebase</h1>
<input v-model="searchText" type="text" placeholder="Type here..." />
<div v-if="!isLoading" class="list">
<div v-for="item in categories" :key="item.id">{{ item.name }}</div>
</div>
<div v-if="isLoading" class="loading">
<p>Loading....</p>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { getCollectionQuery } from "./composables/getCollection";
import getCollectionSearch from "./composables/getCollectionSearch";
const categories = ref([]);
const searchText = ref("");
const { documents, isLoading } = getCollectionQuery("categories", []);
watch(documents, () => {
if (documents.value.length > 0) {
categories.value = documents.value;
}
});
watch(searchText, () => {
if (searchText.value) {
const { documents: docs } = getCollectionSearch(
`categories`,
searchText.value.trim().toLocaleLowerCase(),
"nameLowerCase"
);
watch(docs, () => {
categories.value = docs.value;
});
} else {
categories.value = documents.value;
}
});
</script>
Optimizing Full-Text Search
For more advanced search capabilities, consider integrating Firebase with Algolia, a third-party search service offering:
- Full-text search
- Synonym matching
- Ranking and faceting