mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Add ability to filter based on search type (#13641)
This commit is contained in:
		
							parent
							
								
									03ff3e639f
								
							
						
					
					
						commit
						cae11cbb86
					
				@ -274,7 +274,7 @@ def event_ids():
 | 
				
			|||||||
@EventBp.route("/events/search")
 | 
					@EventBp.route("/events/search")
 | 
				
			||||||
def events_search():
 | 
					def events_search():
 | 
				
			||||||
    query = request.args.get("query", type=str)
 | 
					    query = request.args.get("query", type=str)
 | 
				
			||||||
    search_type = request.args.get("search_type", "text", type=str)
 | 
					    search_type = request.args.get("search_type", "thumbnail,description", type=str)
 | 
				
			||||||
    include_thumbnails = request.args.get("include_thumbnails", default=1, type=int)
 | 
					    include_thumbnails = request.args.get("include_thumbnails", default=1, type=int)
 | 
				
			||||||
    limit = request.args.get("limit", 50, type=int)
 | 
					    limit = request.args.get("limit", 50, type=int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -358,7 +358,7 @@ def events_search():
 | 
				
			|||||||
    thumb_ids = {}
 | 
					    thumb_ids = {}
 | 
				
			||||||
    desc_ids = {}
 | 
					    desc_ids = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if search_type == "thumbnail":
 | 
					    if search_type == "similarity":
 | 
				
			||||||
        # Grab the ids of events that match the thumbnail image embeddings
 | 
					        # Grab the ids of events that match the thumbnail image embeddings
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            search_event: Event = Event.get(Event.id == query)
 | 
					            search_event: Event = Event.get(Event.id == query)
 | 
				
			||||||
@ -386,29 +386,34 @@ def events_search():
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        thumb_result = context.embeddings.thumbnail.query(
 | 
					        search_types = search_type.split(",")
 | 
				
			||||||
            query_texts=[query],
 | 
					
 | 
				
			||||||
            n_results=limit,
 | 
					        if "thumbnail" in search_types:
 | 
				
			||||||
            where=where,
 | 
					            thumb_result = context.embeddings.thumbnail.query(
 | 
				
			||||||
        )
 | 
					                query_texts=[query],
 | 
				
			||||||
        # Do a rudimentary normalization of the difference in distances returned by CLIP and MiniLM.
 | 
					                n_results=limit,
 | 
				
			||||||
        thumb_ids = dict(
 | 
					                where=where,
 | 
				
			||||||
            zip(
 | 
					 | 
				
			||||||
                thumb_result["ids"][0],
 | 
					 | 
				
			||||||
                context.thumb_stats.normalize(thumb_result["distances"][0]),
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					            # Do a rudimentary normalization of the difference in distances returned by CLIP and MiniLM.
 | 
				
			||||||
        desc_result = context.embeddings.description.query(
 | 
					            thumb_ids = dict(
 | 
				
			||||||
            query_texts=[query],
 | 
					                zip(
 | 
				
			||||||
            n_results=limit,
 | 
					                    thumb_result["ids"][0],
 | 
				
			||||||
            where=where,
 | 
					                    context.thumb_stats.normalize(thumb_result["distances"][0]),
 | 
				
			||||||
        )
 | 
					                )
 | 
				
			||||||
        desc_ids = dict(
 | 
					            )
 | 
				
			||||||
            zip(
 | 
					
 | 
				
			||||||
                desc_result["ids"][0],
 | 
					        if "description" in search_types:
 | 
				
			||||||
                context.desc_stats.normalize(desc_result["distances"][0]),
 | 
					            desc_result = context.embeddings.description.query(
 | 
				
			||||||
 | 
					                query_texts=[query],
 | 
				
			||||||
 | 
					                n_results=limit,
 | 
				
			||||||
 | 
					                where=where,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            desc_ids = dict(
 | 
				
			||||||
 | 
					                zip(
 | 
				
			||||||
 | 
					                    desc_result["ids"][0],
 | 
				
			||||||
 | 
					                    context.desc_stats.normalize(desc_result["distances"][0]),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    results = {}
 | 
					    results = {}
 | 
				
			||||||
    for event_id in thumb_ids.keys() | desc_ids:
 | 
					    for event_id in thumb_ids.keys() | desc_ids:
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ import FilterSwitch from "./FilterSwitch";
 | 
				
			|||||||
import { FilterList } from "@/types/filter";
 | 
					import { FilterList } from "@/types/filter";
 | 
				
			||||||
import { CalendarRangeFilterButton } from "./CalendarFilterButton";
 | 
					import { CalendarRangeFilterButton } from "./CalendarFilterButton";
 | 
				
			||||||
import { CamerasFilterButton } from "./CamerasFilterButton";
 | 
					import { CamerasFilterButton } from "./CamerasFilterButton";
 | 
				
			||||||
import { SearchFilter } from "@/types/search";
 | 
					import { SearchFilter, SearchSource } from "@/types/search";
 | 
				
			||||||
import { DateRange } from "react-day-picker";
 | 
					import { DateRange } from "react-day-picker";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SEARCH_FILTERS = ["cameras", "date", "general"] as const;
 | 
					const SEARCH_FILTERS = ["cameras", "date", "general"] as const;
 | 
				
			||||||
@ -103,6 +103,7 @@ export default function SearchFilterGroup({
 | 
				
			|||||||
      cameras: Object.keys(config?.cameras || {}),
 | 
					      cameras: Object.keys(config?.cameras || {}),
 | 
				
			||||||
      labels: Object.values(allLabels || {}),
 | 
					      labels: Object.values(allLabels || {}),
 | 
				
			||||||
      zones: Object.values(allZones || {}),
 | 
					      zones: Object.values(allZones || {}),
 | 
				
			||||||
 | 
					      search_type: ["thumbnail", "description"] as SearchSource[],
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
    [config, allLabels, allZones],
 | 
					    [config, allLabels, allZones],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@ -178,12 +179,18 @@ export default function SearchFilterGroup({
 | 
				
			|||||||
          selectedLabels={filter?.labels}
 | 
					          selectedLabels={filter?.labels}
 | 
				
			||||||
          allZones={filterValues.zones}
 | 
					          allZones={filterValues.zones}
 | 
				
			||||||
          selectedZones={filter?.zones}
 | 
					          selectedZones={filter?.zones}
 | 
				
			||||||
 | 
					          selectedSearchSources={
 | 
				
			||||||
 | 
					            filter?.search_type ?? ["thumbnail", "description"]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          updateLabelFilter={(newLabels) => {
 | 
					          updateLabelFilter={(newLabels) => {
 | 
				
			||||||
            onUpdateFilter({ ...filter, labels: newLabels });
 | 
					            onUpdateFilter({ ...filter, labels: newLabels });
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
          updateZoneFilter={(newZones) =>
 | 
					          updateZoneFilter={(newZones) =>
 | 
				
			||||||
            onUpdateFilter({ ...filter, zones: newZones })
 | 
					            onUpdateFilter({ ...filter, zones: newZones })
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          updateSearchSourceFilter={(newSearchSource) =>
 | 
				
			||||||
 | 
					            onUpdateFilter({ ...filter, search_type: newSearchSource })
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
      {isMobile && mobileSettingsFeatures.length > 0 && (
 | 
					      {isMobile && mobileSettingsFeatures.length > 0 && (
 | 
				
			||||||
@ -211,16 +218,20 @@ type GeneralFilterButtonProps = {
 | 
				
			|||||||
  selectedLabels: string[] | undefined;
 | 
					  selectedLabels: string[] | undefined;
 | 
				
			||||||
  allZones: string[];
 | 
					  allZones: string[];
 | 
				
			||||||
  selectedZones?: string[];
 | 
					  selectedZones?: string[];
 | 
				
			||||||
 | 
					  selectedSearchSources: SearchSource[];
 | 
				
			||||||
  updateLabelFilter: (labels: string[] | undefined) => void;
 | 
					  updateLabelFilter: (labels: string[] | undefined) => void;
 | 
				
			||||||
  updateZoneFilter: (zones: string[] | undefined) => void;
 | 
					  updateZoneFilter: (zones: string[] | undefined) => void;
 | 
				
			||||||
 | 
					  updateSearchSourceFilter: (sources: SearchSource[]) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
function GeneralFilterButton({
 | 
					function GeneralFilterButton({
 | 
				
			||||||
  allLabels,
 | 
					  allLabels,
 | 
				
			||||||
  selectedLabels,
 | 
					  selectedLabels,
 | 
				
			||||||
  allZones,
 | 
					  allZones,
 | 
				
			||||||
  selectedZones,
 | 
					  selectedZones,
 | 
				
			||||||
 | 
					  selectedSearchSources,
 | 
				
			||||||
  updateLabelFilter,
 | 
					  updateLabelFilter,
 | 
				
			||||||
  updateZoneFilter,
 | 
					  updateZoneFilter,
 | 
				
			||||||
 | 
					  updateSearchSourceFilter,
 | 
				
			||||||
}: GeneralFilterButtonProps) {
 | 
					}: GeneralFilterButtonProps) {
 | 
				
			||||||
  const [open, setOpen] = useState(false);
 | 
					  const [open, setOpen] = useState(false);
 | 
				
			||||||
  const [currentLabels, setCurrentLabels] = useState<string[] | undefined>(
 | 
					  const [currentLabels, setCurrentLabels] = useState<string[] | undefined>(
 | 
				
			||||||
@ -229,6 +240,9 @@ function GeneralFilterButton({
 | 
				
			|||||||
  const [currentZones, setCurrentZones] = useState<string[] | undefined>(
 | 
					  const [currentZones, setCurrentZones] = useState<string[] | undefined>(
 | 
				
			||||||
    selectedZones,
 | 
					    selectedZones,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					  const [currentSearchSources, setCurrentSearchSources] = useState<
 | 
				
			||||||
 | 
					    SearchSource[]
 | 
				
			||||||
 | 
					  >(selectedSearchSources);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const trigger = (
 | 
					  const trigger = (
 | 
				
			||||||
    <Button
 | 
					    <Button
 | 
				
			||||||
@ -256,10 +270,14 @@ function GeneralFilterButton({
 | 
				
			|||||||
      allZones={allZones}
 | 
					      allZones={allZones}
 | 
				
			||||||
      selectedZones={selectedZones}
 | 
					      selectedZones={selectedZones}
 | 
				
			||||||
      currentZones={currentZones}
 | 
					      currentZones={currentZones}
 | 
				
			||||||
 | 
					      selectedSearchSources={selectedSearchSources}
 | 
				
			||||||
 | 
					      currentSearchSources={currentSearchSources}
 | 
				
			||||||
      setCurrentZones={setCurrentZones}
 | 
					      setCurrentZones={setCurrentZones}
 | 
				
			||||||
      updateZoneFilter={updateZoneFilter}
 | 
					      updateZoneFilter={updateZoneFilter}
 | 
				
			||||||
      updateLabelFilter={updateLabelFilter}
 | 
					 | 
				
			||||||
      setCurrentLabels={setCurrentLabels}
 | 
					      setCurrentLabels={setCurrentLabels}
 | 
				
			||||||
 | 
					      updateLabelFilter={updateLabelFilter}
 | 
				
			||||||
 | 
					      setCurrentSearchSources={setCurrentSearchSources}
 | 
				
			||||||
 | 
					      updateSearchSourceFilter={updateSearchSourceFilter}
 | 
				
			||||||
      onClose={() => setOpen(false)}
 | 
					      onClose={() => setOpen(false)}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
@ -308,10 +326,14 @@ type GeneralFilterContentProps = {
 | 
				
			|||||||
  allZones?: string[];
 | 
					  allZones?: string[];
 | 
				
			||||||
  selectedZones?: string[];
 | 
					  selectedZones?: string[];
 | 
				
			||||||
  currentZones?: string[];
 | 
					  currentZones?: string[];
 | 
				
			||||||
 | 
					  selectedSearchSources: SearchSource[];
 | 
				
			||||||
 | 
					  currentSearchSources: SearchSource[];
 | 
				
			||||||
  updateLabelFilter: (labels: string[] | undefined) => void;
 | 
					  updateLabelFilter: (labels: string[] | undefined) => void;
 | 
				
			||||||
  setCurrentLabels: (labels: string[] | undefined) => void;
 | 
					  setCurrentLabels: (labels: string[] | undefined) => void;
 | 
				
			||||||
  updateZoneFilter?: (zones: string[] | undefined) => void;
 | 
					  updateZoneFilter?: (zones: string[] | undefined) => void;
 | 
				
			||||||
  setCurrentZones?: (zones: string[] | undefined) => void;
 | 
					  setCurrentZones?: (zones: string[] | undefined) => void;
 | 
				
			||||||
 | 
					  setCurrentSearchSources: (sources: SearchSource[]) => void;
 | 
				
			||||||
 | 
					  updateSearchSourceFilter: (sources: SearchSource[]) => void;
 | 
				
			||||||
  onClose: () => void;
 | 
					  onClose: () => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export function GeneralFilterContent({
 | 
					export function GeneralFilterContent({
 | 
				
			||||||
@ -321,15 +343,62 @@ export function GeneralFilterContent({
 | 
				
			|||||||
  allZones,
 | 
					  allZones,
 | 
				
			||||||
  selectedZones,
 | 
					  selectedZones,
 | 
				
			||||||
  currentZones,
 | 
					  currentZones,
 | 
				
			||||||
 | 
					  selectedSearchSources,
 | 
				
			||||||
 | 
					  currentSearchSources,
 | 
				
			||||||
  updateLabelFilter,
 | 
					  updateLabelFilter,
 | 
				
			||||||
  setCurrentLabels,
 | 
					  setCurrentLabels,
 | 
				
			||||||
  updateZoneFilter,
 | 
					  updateZoneFilter,
 | 
				
			||||||
  setCurrentZones,
 | 
					  setCurrentZones,
 | 
				
			||||||
 | 
					  setCurrentSearchSources,
 | 
				
			||||||
 | 
					  updateSearchSourceFilter,
 | 
				
			||||||
  onClose,
 | 
					  onClose,
 | 
				
			||||||
}: GeneralFilterContentProps) {
 | 
					}: GeneralFilterContentProps) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      <div className="scrollbar-container h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden">
 | 
					      <div className="scrollbar-container h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden">
 | 
				
			||||||
 | 
					        <div className="my-2.5 flex flex-col gap-2.5">
 | 
				
			||||||
 | 
					          <FilterSwitch
 | 
				
			||||||
 | 
					            label="Thumbnail Image"
 | 
				
			||||||
 | 
					            isChecked={currentSearchSources?.includes("thumbnail") ?? false}
 | 
				
			||||||
 | 
					            onCheckedChange={(isChecked) => {
 | 
				
			||||||
 | 
					              const updatedSources = currentSearchSources
 | 
				
			||||||
 | 
					                ? [...currentSearchSources]
 | 
				
			||||||
 | 
					                : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              if (isChecked) {
 | 
				
			||||||
 | 
					                updatedSources.push("thumbnail");
 | 
				
			||||||
 | 
					                setCurrentSearchSources(updatedSources);
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                if (updatedSources.length > 1) {
 | 
				
			||||||
 | 
					                  const index = updatedSources.indexOf("thumbnail");
 | 
				
			||||||
 | 
					                  if (index !== -1) updatedSources.splice(index, 1);
 | 
				
			||||||
 | 
					                  setCurrentSearchSources(updatedSources);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <FilterSwitch
 | 
				
			||||||
 | 
					            label="Description"
 | 
				
			||||||
 | 
					            isChecked={currentSearchSources?.includes("description") ?? false}
 | 
				
			||||||
 | 
					            onCheckedChange={(isChecked) => {
 | 
				
			||||||
 | 
					              const updatedSources = currentSearchSources
 | 
				
			||||||
 | 
					                ? [...currentSearchSources]
 | 
				
			||||||
 | 
					                : [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              if (isChecked) {
 | 
				
			||||||
 | 
					                updatedSources.push("description");
 | 
				
			||||||
 | 
					                setCurrentSearchSources(updatedSources);
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                if (updatedSources.length > 1) {
 | 
				
			||||||
 | 
					                  const index = updatedSources.indexOf("description");
 | 
				
			||||||
 | 
					                  if (index !== -1) updatedSources.splice(index, 1);
 | 
				
			||||||
 | 
					                  setCurrentSearchSources(updatedSources);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <DropdownMenuSeparator />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        <div className="mb-5 mt-2.5 flex items-center justify-between">
 | 
					        <div className="mb-5 mt-2.5 flex items-center justify-between">
 | 
				
			||||||
          <Label
 | 
					          <Label
 | 
				
			||||||
            className="mx-2 cursor-pointer text-primary"
 | 
					            className="mx-2 cursor-pointer text-primary"
 | 
				
			||||||
@ -351,6 +420,7 @@ export function GeneralFilterContent({
 | 
				
			|||||||
        <div className="my-2.5 flex flex-col gap-2.5">
 | 
					        <div className="my-2.5 flex flex-col gap-2.5">
 | 
				
			||||||
          {allLabels.map((item) => (
 | 
					          {allLabels.map((item) => (
 | 
				
			||||||
            <FilterSwitch
 | 
					            <FilterSwitch
 | 
				
			||||||
 | 
					              key={item}
 | 
				
			||||||
              label={item.replaceAll("_", " ")}
 | 
					              label={item.replaceAll("_", " ")}
 | 
				
			||||||
              isChecked={currentLabels?.includes(item) ?? false}
 | 
					              isChecked={currentLabels?.includes(item) ?? false}
 | 
				
			||||||
              onCheckedChange={(isChecked) => {
 | 
					              onCheckedChange={(isChecked) => {
 | 
				
			||||||
@ -397,6 +467,7 @@ export function GeneralFilterContent({
 | 
				
			|||||||
            <div className="my-2.5 flex flex-col gap-2.5">
 | 
					            <div className="my-2.5 flex flex-col gap-2.5">
 | 
				
			||||||
              {allZones.map((item) => (
 | 
					              {allZones.map((item) => (
 | 
				
			||||||
                <FilterSwitch
 | 
					                <FilterSwitch
 | 
				
			||||||
 | 
					                  key={item}
 | 
				
			||||||
                  label={item.replaceAll("_", " ")}
 | 
					                  label={item.replaceAll("_", " ")}
 | 
				
			||||||
                  isChecked={currentZones?.includes(item) ?? false}
 | 
					                  isChecked={currentZones?.includes(item) ?? false}
 | 
				
			||||||
                  onCheckedChange={(isChecked) => {
 | 
					                  onCheckedChange={(isChecked) => {
 | 
				
			||||||
@ -438,6 +509,10 @@ export function GeneralFilterContent({
 | 
				
			|||||||
              updateZoneFilter(currentZones);
 | 
					              updateZoneFilter(currentZones);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (selectedSearchSources != currentSearchSources) {
 | 
				
			||||||
 | 
					              updateSearchSourceFilter(currentSearchSources);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            onClose();
 | 
					            onClose();
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
 | 
				
			|||||||
@ -75,7 +75,7 @@ export default function Search() {
 | 
				
			|||||||
          before: searchSearchParams["before"],
 | 
					          before: searchSearchParams["before"],
 | 
				
			||||||
          after: searchSearchParams["after"],
 | 
					          after: searchSearchParams["after"],
 | 
				
			||||||
          include_thumbnails: 0,
 | 
					          include_thumbnails: 0,
 | 
				
			||||||
          search_type: "thumbnail",
 | 
					          search_type: "similarity",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      ];
 | 
					      ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -89,6 +89,7 @@ export default function Search() {
 | 
				
			|||||||
        zones: searchSearchParams["zones"],
 | 
					        zones: searchSearchParams["zones"],
 | 
				
			||||||
        before: searchSearchParams["before"],
 | 
					        before: searchSearchParams["before"],
 | 
				
			||||||
        after: searchSearchParams["after"],
 | 
					        after: searchSearchParams["after"],
 | 
				
			||||||
 | 
					        search_type: searchSearchParams["search_type"],
 | 
				
			||||||
        include_thumbnails: 0,
 | 
					        include_thumbnails: 0,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
@ -192,6 +193,7 @@ export default function Search() {
 | 
				
			|||||||
        allPreviews={allPreviews}
 | 
					        allPreviews={allPreviews}
 | 
				
			||||||
        isLoading={isLoading}
 | 
					        isLoading={isLoading}
 | 
				
			||||||
        setSearch={setSearch}
 | 
					        setSearch={setSearch}
 | 
				
			||||||
 | 
					        similaritySearch={similaritySearch}
 | 
				
			||||||
        setSimilaritySearch={setSimilaritySearch}
 | 
					        setSimilaritySearch={setSimilaritySearch}
 | 
				
			||||||
        onUpdateFilter={onUpdateFilter}
 | 
					        onUpdateFilter={onUpdateFilter}
 | 
				
			||||||
        onOpenSearch={onOpenSearch}
 | 
					        onOpenSearch={onOpenSearch}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
type SearchSource = "thumbnail" | "description";
 | 
					export type SearchSource = "similarity" | "thumbnail" | "description";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SearchResult = {
 | 
					export type SearchResult = {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
@ -21,4 +21,5 @@ export type SearchFilter = {
 | 
				
			|||||||
  zones?: string[];
 | 
					  zones?: string[];
 | 
				
			||||||
  before?: number;
 | 
					  before?: number;
 | 
				
			||||||
  after?: number;
 | 
					  after?: number;
 | 
				
			||||||
 | 
					  search_type?: SearchSource[];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@ type SearchViewProps = {
 | 
				
			|||||||
  searchResults?: SearchResult[];
 | 
					  searchResults?: SearchResult[];
 | 
				
			||||||
  allPreviews?: Preview[];
 | 
					  allPreviews?: Preview[];
 | 
				
			||||||
  isLoading: boolean;
 | 
					  isLoading: boolean;
 | 
				
			||||||
 | 
					  similaritySearch?: SearchResult;
 | 
				
			||||||
  setSearch: (search: string) => void;
 | 
					  setSearch: (search: string) => void;
 | 
				
			||||||
  setSimilaritySearch: (search: SearchResult) => void;
 | 
					  setSimilaritySearch: (search: SearchResult) => void;
 | 
				
			||||||
  onUpdateFilter: (filter: SearchFilter) => void;
 | 
					  onUpdateFilter: (filter: SearchFilter) => void;
 | 
				
			||||||
@ -44,6 +45,7 @@ export default function SearchView({
 | 
				
			|||||||
  searchResults,
 | 
					  searchResults,
 | 
				
			||||||
  allPreviews,
 | 
					  allPreviews,
 | 
				
			||||||
  isLoading,
 | 
					  isLoading,
 | 
				
			||||||
 | 
					  similaritySearch,
 | 
				
			||||||
  setSearch,
 | 
					  setSearch,
 | 
				
			||||||
  setSimilaritySearch,
 | 
					  setSimilaritySearch,
 | 
				
			||||||
  onUpdateFilter,
 | 
					  onUpdateFilter,
 | 
				
			||||||
@ -112,7 +114,7 @@ export default function SearchView({
 | 
				
			|||||||
            placeholder={
 | 
					            placeholder={
 | 
				
			||||||
              isMobileOnly ? "Search" : "Search for a detected object..."
 | 
					              isMobileOnly ? "Search" : "Search for a detected object..."
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            value={search}
 | 
					            value={similaritySearch ? "" : search}
 | 
				
			||||||
            onChange={(e) => setSearch(e.target.value)}
 | 
					            onChange={(e) => setSearch(e.target.value)}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
          {search && (
 | 
					          {search && (
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user