Implement similarity search

This commit is contained in:
Nicolas Mowen 2024-06-22 15:29:48 -06:00
parent 2418111cd4
commit ebcae504cd
3 changed files with 67 additions and 18 deletions

View File

@ -16,10 +16,12 @@ import { toast } from "sonner";
type SearchDetailDialogProps = { type SearchDetailDialogProps = {
search?: SearchResult; search?: SearchResult;
setSearch: (search: SearchResult | undefined) => void; setSearch: (search: SearchResult | undefined) => void;
setSimilarity?: () => void;
}; };
export default function SearchDetailDialog({ export default function SearchDetailDialog({
search, search,
setSearch, setSearch,
setSimilarity,
}: SearchDetailDialogProps) { }: SearchDetailDialogProps) {
const { data: config } = useSWR<FrigateConfig>("config", { const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false, revalidateOnFocus: false,
@ -31,6 +33,7 @@ export default function SearchDetailDialog({
const [desc, setDesc] = useState(search?.description); const [desc, setDesc] = useState(search?.description);
// we have to make sure the current selected search item stays in sync
useEffect(() => setDesc(search?.description), [search]); useEffect(() => setDesc(search?.description), [search]);
const formattedDate = useFormattedTimestamp( const formattedDate = useFormattedTimestamp(
@ -129,7 +132,17 @@ export default function SearchDetailDialog({
: `${apiHost}api/events/${search.id}/thumbnail.jpg` : `${apiHost}api/events/${search.id}/thumbnail.jpg`
} }
/> />
<Button>Find Similar</Button> <Button
onClick={() => {
setSearch(undefined);
if (setSimilarity) {
setSimilarity();
}
}}
>
Find Similar
</Button>
</div> </div>
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">

View File

@ -38,7 +38,13 @@ export default function Search() {
// search api // search api
const [similaritySearch, setSimilaritySearch] = useState<SearchResult>();
useEffect(() => { useEffect(() => {
if (similaritySearch) {
setSimilaritySearch(undefined);
}
if (searchTimeout) { if (searchTimeout) {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
} }
@ -53,22 +59,43 @@ export default function Search() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [search]); }, [search]);
const { data: searchResults, isLoading } = useSWR<SearchResult[]>( const searchQuery = useMemo(() => {
searchTerm.length > 0 if (searchTerm.length == 0) {
? [ return null;
"events/search", }
{
query: searchTerm, if (similaritySearch) {
cameras: searchSearchParams["cameras"], return [
labels: searchSearchParams["labels"], "events/search",
zones: searchSearchParams["zones"], {
before: searchSearchParams["before"], query: similaritySearch.id,
after: searchSearchParams["after"], cameras: searchSearchParams["cameras"],
include_thumbnails: 0, labels: searchSearchParams["labels"],
}, zones: searchSearchParams["zones"],
] before: searchSearchParams["before"],
: null, after: searchSearchParams["after"],
); include_thumbnails: 0,
search_type: "thumbnail",
},
];
}
return [
"events/search",
{
query: searchTerm,
cameras: searchSearchParams["cameras"],
labels: searchSearchParams["labels"],
zones: searchSearchParams["zones"],
before: searchSearchParams["before"],
after: searchSearchParams["after"],
include_thumbnails: 0,
},
];
}, [searchTerm, searchSearchParams, similaritySearch]);
const { data: searchResults, isLoading } =
useSWR<SearchResult[]>(searchQuery);
const previewTimeRange = useMemo<TimeRange>(() => { const previewTimeRange = useMemo<TimeRange>(() => {
if (!searchResults) { if (!searchResults) {
@ -164,6 +191,7 @@ export default function Search() {
allPreviews={allPreviews} allPreviews={allPreviews}
isLoading={isLoading} isLoading={isLoading}
setSearch={setSearch} setSearch={setSearch}
setSimilaritySearch={setSimilaritySearch}
onUpdateFilter={onUpdateFilter} onUpdateFilter={onUpdateFilter}
onOpenSearch={onOpenSearch} onOpenSearch={onOpenSearch}
/> />

View File

@ -18,6 +18,7 @@ type SearchViewProps = {
allPreviews?: Preview[]; allPreviews?: Preview[];
isLoading: boolean; isLoading: boolean;
setSearch: (search: string) => void; setSearch: (search: string) => void;
setSimilaritySearch: (search: SearchResult) => void;
onUpdateFilter: (filter: SearchFilter) => void; onUpdateFilter: (filter: SearchFilter) => void;
onOpenSearch: (item: SearchResult) => void; onOpenSearch: (item: SearchResult) => void;
}; };
@ -29,6 +30,7 @@ export default function SearchView({
allPreviews, allPreviews,
isLoading, isLoading,
setSearch, setSearch,
setSimilaritySearch,
onUpdateFilter, onUpdateFilter,
onOpenSearch, onOpenSearch,
}: SearchViewProps) { }: SearchViewProps) {
@ -52,7 +54,13 @@ export default function SearchView({
return ( return (
<div className="flex size-full flex-col pt-2 md:py-2"> <div className="flex size-full flex-col pt-2 md:py-2">
<Toaster closeButton={true} /> <Toaster closeButton={true} />
<SearchDetailDialog search={searchDetail} setSearch={setSearchDetail} /> <SearchDetailDialog
search={searchDetail}
setSearch={setSearchDetail}
setSimilarity={
searchDetail && (() => setSimilaritySearch(searchDetail))
}
/>
<div className="relative mb-2 flex h-11 items-center justify-between pl-2 pr-2 md:pl-3"> <div className="relative mb-2 flex h-11 items-center justify-between pl-2 pr-2 md:pl-3">
<Input <Input