Merge pull request #665 from langflow-ai/fix/filter_search
This commit is contained in:
commit
1df76aa51e
5 changed files with 721 additions and 672 deletions
36
frontend/app/api/queries/useGetAllFiltersQuery.ts
Normal file
36
frontend/app/api/queries/useGetAllFiltersQuery.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import {
|
||||||
|
type UseQueryOptions,
|
||||||
|
useQuery,
|
||||||
|
useQueryClient,
|
||||||
|
} from "@tanstack/react-query";
|
||||||
|
import type { KnowledgeFilter } from "./useGetFiltersSearchQuery";
|
||||||
|
|
||||||
|
export const useGetAllFiltersQuery = (
|
||||||
|
options?: Omit<UseQueryOptions<KnowledgeFilter[]>, "queryKey" | "queryFn">,
|
||||||
|
) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
async function getAllFilters(): Promise<KnowledgeFilter[]> {
|
||||||
|
const response = await fetch("/api/knowledge-filter/search", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ query: "", limit: 1000 }), // Fetch all filters
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = await response.json();
|
||||||
|
if (!response.ok || !json.success) {
|
||||||
|
// ensure we always return a KnowledgeFilter[] to satisfy the return type
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return (json.filters || []) as KnowledgeFilter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
return useQuery<KnowledgeFilter[]>(
|
||||||
|
{
|
||||||
|
queryKey: ["knowledge-filters", "all"],
|
||||||
|
queryFn: getAllFilters,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
queryClient,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Fuse from "fuse.js";
|
||||||
import { ArrowRight, Check, Funnel, Loader2, Plus } from "lucide-react";
|
import { ArrowRight, Check, Funnel, Loader2, Plus } from "lucide-react";
|
||||||
import { AnimatePresence, motion } from "motion/react";
|
import { AnimatePresence, motion } from "motion/react";
|
||||||
import {
|
import {
|
||||||
|
|
@ -19,7 +20,7 @@ import {
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { useFileDrag } from "@/hooks/use-file-drag";
|
import { useFileDrag } from "@/hooks/use-file-drag";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useGetFiltersSearchQuery } from "../../api/queries/useGetFiltersSearchQuery";
|
import { useGetAllFiltersQuery } from "../../api/queries/useGetAllFiltersQuery";
|
||||||
import type { KnowledgeFilterData } from "../_types/types";
|
import type { KnowledgeFilterData } from "../_types/types";
|
||||||
import { FilePreview } from "./file-preview";
|
import { FilePreview } from "./file-preview";
|
||||||
import { SelectedKnowledgeFilter } from "./selected-knowledge-filter";
|
import { SelectedKnowledgeFilter } from "./selected-knowledge-filter";
|
||||||
|
|
@ -80,19 +81,27 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||||
y: number;
|
y: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
// Fetch filters using the query hook
|
// Fetch all filters once when dropdown opens
|
||||||
const { data: availableFilters = [] } = useGetFiltersSearchQuery(
|
const { data: allFilters = [] } = useGetAllFiltersQuery({
|
||||||
filterSearchTerm,
|
enabled: isFilterDropdownOpen,
|
||||||
20,
|
});
|
||||||
{ enabled: isFilterDropdownOpen },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter available filters based on search term
|
// Use fuse.js for fuzzy search on client side
|
||||||
const filteredFilters = useMemo(() => {
|
const filteredFilters = useMemo(() => {
|
||||||
return availableFilters.filter((filter) =>
|
if (!filterSearchTerm) {
|
||||||
filter.name.toLowerCase().includes(filterSearchTerm.toLowerCase()),
|
return allFilters.slice(0, 20); // Return first 20 when no search term
|
||||||
);
|
}
|
||||||
}, [availableFilters, filterSearchTerm]);
|
|
||||||
|
const fuse = new Fuse(allFilters, {
|
||||||
|
keys: ["name", "description"],
|
||||||
|
threshold: 0.3, // 0.0 = perfect match, 1.0 = match anything
|
||||||
|
includeScore: true,
|
||||||
|
minMatchCharLength: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = fuse.search(filterSearchTerm);
|
||||||
|
return results.map((result) => result.item).slice(0, 20);
|
||||||
|
}, [allFilters, filterSearchTerm]);
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
accept: {
|
accept: {
|
||||||
|
|
@ -534,7 +543,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
||||||
Searching: @{filterSearchTerm}
|
Searching: @{filterSearchTerm}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{availableFilters.length === 0 ? (
|
{allFilters.length === 0 ? (
|
||||||
<div className="px-2 py-3 text-sm text-muted-foreground">
|
<div className="px-2 py-3 text-sm text-muted-foreground">
|
||||||
No knowledge filters available
|
No knowledge filters available
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Plus } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useGetAllFiltersQuery } from "@/app/api/queries/useGetAllFiltersQuery";
|
||||||
import {
|
import type { KnowledgeFilter } from "@/app/api/queries/useGetFiltersSearchQuery";
|
||||||
type KnowledgeFilter,
|
|
||||||
useGetFiltersSearchQuery,
|
|
||||||
} from "@/app/api/queries/useGetFiltersSearchQuery";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
type FilterColor,
|
type FilterColor,
|
||||||
type IconKey,
|
type IconKey,
|
||||||
|
|
@ -37,13 +34,9 @@ export function KnowledgeFilterList({
|
||||||
selectedFilter,
|
selectedFilter,
|
||||||
onFilterSelect,
|
onFilterSelect,
|
||||||
}: KnowledgeFilterListProps) {
|
}: KnowledgeFilterListProps) {
|
||||||
const [searchQuery] = useState("");
|
|
||||||
const { startCreateMode } = useKnowledgeFilter();
|
const { startCreateMode } = useKnowledgeFilter();
|
||||||
|
|
||||||
const { data, isFetching: loading } = useGetFiltersSearchQuery(
|
const { data, isFetching: loading } = useGetAllFiltersQuery();
|
||||||
searchQuery,
|
|
||||||
20,
|
|
||||||
);
|
|
||||||
|
|
||||||
const filters = data || [];
|
const filters = data || [];
|
||||||
|
|
||||||
|
|
@ -87,7 +80,7 @@ export function KnowledgeFilterList({
|
||||||
</div>
|
</div>
|
||||||
) : filters.length === 0 ? (
|
) : filters.length === 0 ? (
|
||||||
<div className="text-[13px] text-muted-foreground pb-2 pt-3 ml-4">
|
<div className="text-[13px] text-muted-foreground pb-2 pt-3 ml-4">
|
||||||
{searchQuery ? "No filters found" : "No saved filters"}
|
No saved filters
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filters.map((filter) => (
|
filters.map((filter) => (
|
||||||
|
|
|
||||||
10
frontend/package-lock.json
generated
10
frontend/package-lock.json
generated
|
|
@ -30,6 +30,7 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
"fuse.js": "^7.1.0",
|
||||||
"lucide-react": "^0.525.0",
|
"lucide-react": "^0.525.0",
|
||||||
"motion": "^12.23.12",
|
"motion": "^12.23.12",
|
||||||
"next": "15.5.7",
|
"next": "15.5.7",
|
||||||
|
|
@ -3429,6 +3430,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fuse.js": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-nonce": {
|
"node_modules/get-nonce": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
"fuse.js": "^7.1.0",
|
||||||
"lucide-react": "^0.525.0",
|
"lucide-react": "^0.525.0",
|
||||||
"motion": "^12.23.12",
|
"motion": "^12.23.12",
|
||||||
"next": "15.5.7",
|
"next": "15.5.7",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue