engagingnewsproject / misinfo-dashboard

2 stars 0 forks source link

Search bar not working #44

Open luukee opened 5 days ago

luukee commented 5 days ago

For Admin & Agency user login.

Test on the dev site: https://dev-truthsleuthlocal.netlify.app. The search bar should do a dynamic (as you type) search of the reports content.

Watch the video

luukee commented 5 days ago

1. Set up Firestore Search Functionality

Firestore does not have a full-text search capability by default. However, you can implement a basic search with queries that filter data by field values. For more advanced full-text search, you can integrate services like Algolia or ElasticSearch. In this example, I'll show how to implement a basic search based on field matching within the Firestore reports collection.

Assume your reports collection has fields like title, description, and tags.

2. Create a Firestore Query Function

First, create a function that searches the Firestore reports collection based on a search term (e.g., searching by title or description):

import { useState } from 'react'
import { db } from '../firebase' // Adjust to your Firebase config path
import { collection, query, where, getDocs } from 'firebase/firestore'

const searchReports = async (searchTerm) => {
  const q = query(
    collection(db, 'reports'),
    where('title', '>=', searchTerm), // Start searching by title
    where('title', '<=', searchTerm + '\uf8ff') // End range for search term
  )

  const querySnapshot = await getDocs(q)
  let results = []
  querySnapshot.forEach((doc) => {
    results.push({ id: doc.id, ...doc.data() })
  })
  return results
}

This function queries the reports collection, filtering by the title field using Firestore's range queries. The \uf8ff ensures it matches titles starting with the search term.

3. Create a Search Component

Now, create a SearchBar component that will allow users to type in a search query and show the results.

import { useState } from 'react'

const SearchBar = () => {
  const [searchTerm, setSearchTerm] = useState('')
  const [searchResults, setSearchResults] = useState([])

  const handleSearch = async (e) => {
    e.preventDefault()
    if (!searchTerm.trim()) return // Ignore empty searches

    const results = await searchReports(searchTerm)
    setSearchResults(results)
  }

  return (
    <div>
      <form onSubmit={handleSearch}>
        <input
          type="text"
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          placeholder="Search reports..."
        />
        <button type="submit">Search</button>
      </form>

      {/* Display search results */}
      <ul>
        {searchResults.map((result) => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  )
}

export default SearchBar

4. Usage in a Page Component

You can now import and use this SearchBar component in any Next.js page where you'd like to offer search functionality:

import SearchBar from '../components/SearchBar'

const ReportsPage = () => {
  return (
    <div>
      <h1>Search Reports</h1>
      <SearchBar />
    </div>
  )
}

export default ReportsPage

5. Optimizing for Larger Search

For larger datasets or if you want to search multiple fields (e.g., title, description, tags), you may want to consider using Firestore's array-contains for tags or combining multiple queries, but this can get limited with Firestore's querying capabilities.

For more advanced searching (e.g., partial matching, fuzzy search), integrating with Algolia would be ideal, as it offers robust full-text search capabilities and can index your Firestore collection.

Optional: Debouncing Search Queries

To avoid too many Firestore queries, you can implement debouncing so the query runs only after the user has stopped typing for a short period:

import { useState, useEffect } from 'react'

const SearchBar = () => {
  const [searchTerm, setSearchTerm] = useState('')
  const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm)
  const [searchResults, setSearchResults] = useState([])

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedSearchTerm(searchTerm)
    }, 300)

    return () => {
      clearTimeout(handler)
    }
  }, [searchTerm])

  useEffect(() => {
    if (debouncedSearchTerm) {
      searchReports(debouncedSearchTerm).then(setSearchResults)
    }
  }, [debouncedSearchTerm])

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search reports..."
      />
      <ul>
        {searchResults.map((result) => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  )
}

This ensures that Firestore queries are sent only after the user has paused typing for 300ms.