Skip to content

[Experiment] Implement a Unified Search#102

Open
vibhatha wants to merge 2 commits intoLDFLK:mainfrom
vibhatha:feat-experimental-search
Open

[Experiment] Implement a Unified Search#102
vibhatha wants to merge 2 commits intoLDFLK:mainfrom
vibhatha:feat-experimental-search

Conversation

@vibhatha
Copy link
Member

@vibhatha vibhatha commented Feb 1, 2026

This is an experimental work on adding a basic Search functionality.

Summary

Implemented a full-text search feature that allows users to search across persons, departments, ministers, and datasets using the GI-SERVICE backend.

Features

  • Search Bar in the main header for quick access from any page
  • Mixed Results View displaying all entity types sorted by relevance score
  • Entity Type Indicators with distinct colors and icons for each type (Person, Department, Minister, Dataset)
  • Smart Navigation - clicking a result navigates to the appropriate detail page:
    • Person → Person Profile page
    • Department → Department Profile page
    • Minister → Organization page with date context and name filter
    • Dataset → Data page with full category breadcrumb
  • Loading States with spinners for search and dataset navigation
  • Error Handling with user-friendly messages and fallback navigation

Files Added

  • src/services/searchServices.js - API service for search and dataset categories
  • src/hooks/useSearch.js - React Query hook with caching
  • src/components/SearchBar.jsx - Header search input component
  • src/pages/SearchPage/screens/SearchPage.jsx - Search results page

Files Modified

  • src/pages/HomePage/screens/HomePage.jsx - Added SearchBar and search tab routing

API Integration

  • GET /v1/search?q={query} - Main search endpoint
  • GET /v1/data/datasets/{datasetId}/categories - Dataset category hierarchy for navigation

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @vibhatha, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces an experimental, foundational search capability to the application. It provides users with a new search bar in the header and a dedicated results page, allowing them to discover various entities like individuals, organizations, and datasets. The implementation focuses on robust data fetching and dynamic routing to enhance user navigation and information retrieval.

Highlights

  • New Search Functionality: Introduced a basic search feature, enabling users to search for persons, departments, ministries, and datasets across the application.
  • Dedicated Search Components: Added a reusable SearchBar component for input and a SearchPage to display and manage search results, including dynamic navigation to entity-specific pages.
  • API Integration for Search: Implemented new service functions (search and getDatasetCategories) and a useSearch React Query hook to efficiently fetch and manage search data from the backend.
  • Dependency Lock File Updates: Modified package-lock.json to reflect changes in dependency resolution, specifically removing the 'peer: true' property from several entries.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a basic search functionality, which is a valuable addition. The implementation is well-structured across new components, hooks, and services. My review includes suggestions to enhance robustness, maintainability, and performance by consistently using URLSearchParams for query string construction, memoizing expensive computations, and ensuring component state remains synchronized with the URL.

const navigate = useNavigate();
const [searchParams] = useSearchParams();
const initialQuery = searchParams.get("q") || "";
const [query, setQuery] = useState(initialQuery);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The query state is initialized from the URL but doesn't update if the URL changes (e.g., using browser back/forward buttons). This can lead to a stale query in the search bar. Using a useEffect hook will keep the component's state synchronized with the URL's q parameter.

Don't forget to import useEffect from react.

  const [query, setQuery] = useState(initialQuery);

  useEffect(() => {
    setQuery(searchParams.get("q") || "");
  }, [searchParams]);

e.preventDefault();
const trimmedQuery = query.trim();
if (trimmedQuery.length >= 2) {
navigate(`/search?q=${encodeURIComponent(trimmedQuery)}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve code robustness and readability, it's better to use the URLSearchParams API for constructing query strings. This automatically handles URL encoding and avoids potential issues with manual string concatenation.

      navigate(`/search?${new URLSearchParams({ q: trimmedQuery })}`);

Comment on lines +55 to +85
const buildDatasetUrl = (datasetName, categories) => {
// Filter to Category types only, reverse to get root → child order
const categoryChain = categories
.filter((c) => c.kind?.major === "Category")
.reverse();

if (categoryChain.length === 0) return "/data";

const lastCategoryId = categoryChain[categoryChain.length - 1].id;

// Build breadcrumb array
const breadcrumb = categoryChain.map((cat) => ({
label: formatCategoryName(cat.name),
path: `/data?categoryIds=${encodeURIComponent(JSON.stringify([cat.id]))}`,
}));

// Add dataset as final breadcrumb
breadcrumb.push({
label: datasetName,
path: `/data?datasetName=${encodeURIComponent(datasetName)}&categoryIds=${encodeURIComponent(JSON.stringify([lastCategoryId]))}`,
});

// Build final URL
const params = new URLSearchParams({
categoryIds: JSON.stringify([lastCategoryId]),
datasetName: datasetName,
breadcrumb: JSON.stringify(breadcrumb),
});

return `/data?${params.toString()}`;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function can be simplified and made more robust by consistently using URLSearchParams to build query strings. This approach automatically handles URL encoding, making the code cleaner and less prone to errors.

const buildDatasetUrl = (datasetName, categories) => {
  // Filter to Category types only, reverse to get root → child order
  const categoryChain = categories
    .filter((c) => c.kind?.major === "Category")
    .reverse();

  if (categoryChain.length === 0) return "/data";

  const lastCategoryId = categoryChain[categoryChain.length - 1].id;

  // Build breadcrumb array
  const breadcrumb = categoryChain.map((cat) => {
    const params = new URLSearchParams({ categoryIds: JSON.stringify([cat.id]) });
    return {
      label: formatCategoryName(cat.name),
      path: `/data?${params.toString()}`,
    };
  });

  // Add dataset as final breadcrumb
  const datasetParams = new URLSearchParams({
    datasetName,
    categoryIds: JSON.stringify([lastCategoryId]),
  });
  breadcrumb.push({
    label: datasetName,
    path: `/data?${datasetParams.toString()}`,
  });

  // Build final URL
  const finalParams = new URLSearchParams({
    categoryIds: JSON.stringify([lastCategoryId]),
    datasetName,
    breadcrumb: JSON.stringify(breadcrumb),
  });

  return `/data?${finalParams.toString()}`;
};

Comment on lines +101 to +103
const sortedResults = data?.results
? [...data.results].sort((a, b) => (b.match_score || 0) - (a.match_score || 0))
: [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The sortedResults array is recalculated on every render. To optimize performance, you can memoize this calculation using the useMemo hook. This will prevent re-sorting the results unless the data from the search query changes. Remember to import useMemo from react.

  const sortedResults = useMemo(
    () =>
      data?.results
        ? [...data.results].sort((a, b) => (b.match_score || 0) - (a.match_score || 0))
        : [],
    [data]
  );

e.preventDefault();
const trimmedQuery = localQuery.trim();
if (trimmedQuery.length >= 2) {
navigate(`/search?q=${encodeURIComponent(trimmedQuery)}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's a good practice to use URLSearchParams to construct query strings. This handles encoding automatically and makes the code more robust and readable.

      navigate(`/search?${new URLSearchParams({ q: trimmedQuery })}`);

Comment on lines 136 to 138
navigate(
`/organization?filterByName=${encodeURIComponent(result.name)}&viewMode=Grid&filterByType=all`
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better code clarity and to ensure correct URL encoding, it's recommended to use URLSearchParams for building the query string.

        const params = new URLSearchParams({
          filterByName: result.name,
          viewMode: "Grid",
          filterByType: "all",
        });
        navigate(`/organization?${params.toString()}`);

@vibhatha vibhatha changed the title [Experiment] Basic Search [Experiment] Implement a Unified Search Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant