From 04c1b5c84484f2734b1cbcbeafcb08d4be26b2d0 Mon Sep 17 00:00:00 2001 From: David <75678655+David-Werth@users.noreply.github.com> Date: Sun, 25 Jan 2026 13:55:59 +0100 Subject: [PATCH 1/4] Add manual sorting and filtering options to `useTabular` export --- .../src/components/Tabular/useTabular.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/graph-explorer/src/components/Tabular/useTabular.ts b/packages/graph-explorer/src/components/Tabular/useTabular.ts index 9fee5a1dc..90d9cf1ed 100644 --- a/packages/graph-explorer/src/components/Tabular/useTabular.ts +++ b/packages/graph-explorer/src/components/Tabular/useTabular.ts @@ -195,6 +195,11 @@ export interface TabularOptions { */ disableSortRemove?: boolean; + /** + * Enables sorting detection functionality, but does not automatically perform row sorting. + */ + manualSort?: boolean; + /** * Must be memoized. An array of filters. */ @@ -220,6 +225,11 @@ export interface TabularOptions { */ autoResetFilters?: boolean; + /** + * Enables filter detection functionality, but does not automatically perform row filtering. + */ + manualFilters?: boolean; + /** * Disables the pagination. */ @@ -309,6 +319,8 @@ export const useTabular = (options: TabularOptions) => { toggleAllRowsSelected, initialColumnOrder, initialHiddenColumns, + manualFilters, + manualSort, ...restOptions } = options; @@ -394,6 +406,8 @@ export const useTabular = (options: TabularOptions) => { defaultColumn, disableSortBy: disableSorting, disableMultiSort: disableMultiSorting, + manualSortBy: manualSort, + manualFilters, columns: useDeepMemo( () => columns.map(column => columnDefinitionToColumn(column)), [columns], From daca77f3ce8fefef2d52028a73447006e44496db Mon Sep 17 00:00:00 2001 From: David <75678655+David-Werth@users.noreply.github.com> Date: Sun, 25 Jan 2026 13:58:38 +0100 Subject: [PATCH 2/4] Fix manual sorting name --- .../graph-explorer/src/components/Tabular/useTabular.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/graph-explorer/src/components/Tabular/useTabular.ts b/packages/graph-explorer/src/components/Tabular/useTabular.ts index 90d9cf1ed..363498ede 100644 --- a/packages/graph-explorer/src/components/Tabular/useTabular.ts +++ b/packages/graph-explorer/src/components/Tabular/useTabular.ts @@ -198,7 +198,7 @@ export interface TabularOptions { /** * Enables sorting detection functionality, but does not automatically perform row sorting. */ - manualSort?: boolean; + manualSorting?: boolean; /** * Must be memoized. An array of filters. @@ -320,7 +320,7 @@ export const useTabular = (options: TabularOptions) => { initialColumnOrder, initialHiddenColumns, manualFilters, - manualSort, + manualSorting, ...restOptions } = options; @@ -406,7 +406,7 @@ export const useTabular = (options: TabularOptions) => { defaultColumn, disableSortBy: disableSorting, disableMultiSort: disableMultiSorting, - manualSortBy: manualSort, + manualSortBy: manualSorting, manualFilters, columns: useDeepMemo( () => columns.map(column => columnDefinitionToColumn(column)), From 36063eb98d485e470eca41cb3357fb15690c0bd9 Mon Sep 17 00:00:00 2001 From: David <75678655+David-Werth@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:08:07 +0100 Subject: [PATCH 3/4] Move table instance from ref to state, enable server side filtering and sorting for data explorer --- .../src/routes/DataExplorer/DataExplorer.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx index 223e633df..b74e02f19 100644 --- a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx +++ b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { useRef } from "react"; +import { useEffect, useState } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router"; import { @@ -52,6 +52,7 @@ import { useVertexStyling } from "@/core/StateProvider/userPreferences"; import { useAddVertexToGraph, useHasVertexBeenAddedToGraph } from "@/hooks"; import useTranslations from "@/hooks/useTranslations"; import useUpdateVertexTypeCounts from "@/hooks/useUpdateVertexTypeCounts"; +import { logger } from "@/utils"; import { LABELS, RESERVED_ID_PROPERTY, @@ -91,7 +92,8 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { const { pageIndex, pageSize, onPageIndexChange, onPageSizeChange } = usePagingOptions(); - const tableRef = useRef | null>(null); + const [tableInstance, setTableInstance] = + useState | null>(); const columns = useColumnDefinitions(vertexType); const query = useDataExplorerQuery(vertexType, pageSize, pageIndex); @@ -114,6 +116,14 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { }); }; + useEffect(() => { + logger.log(tableInstance?.filters); + }, [tableInstance?.filters]); + + useEffect(() => { + logger.log(tableInstance?.sorts); + }, [tableInstance?.sorts]); + return ( @@ -144,7 +154,7 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { setTableInstance(instance)} defaultColumn={DEFAULT_COLUMN} data={displayVertices} columns={columns} @@ -152,8 +162,8 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { pageIndex={pageIndex} pageSize={pageSize} disablePagination={true} - disableFilters={true} - disableSorting={true} + manualFilters={true} + manualSorting={true} > {query.isPending ? ( From 61474a8be3755beb22f262610b53b161807d11fc Mon Sep 17 00:00:00 2001 From: David <75678655+David-Werth@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:12:06 +0100 Subject: [PATCH 4/4] Wire up filter and sort for gremlin --- .../src/connector/emptyExplorer.ts | 1 + .../filterAndSort/filterAndSortTemplate.ts | 109 +++++++++++++++ .../connector/gremlin/filterAndSort/index.ts | 47 +++++++ .../src/connector/gremlin/gremlinExplorer.ts | 11 ++ .../connector/queries/filterAndSortQuery.ts | 41 ++++++ .../src/connector/queries/index.ts | 1 + .../src/connector/useGEFetchTypes.ts | 46 ++++++ .../core/StateProvider/displayTypeConfigs.ts | 12 ++ .../src/routes/DataExplorer/DataExplorer.tsx | 131 +++++++++++++++--- 9 files changed, 383 insertions(+), 16 deletions(-) create mode 100644 packages/graph-explorer/src/connector/gremlin/filterAndSort/filterAndSortTemplate.ts create mode 100644 packages/graph-explorer/src/connector/gremlin/filterAndSort/index.ts create mode 100644 packages/graph-explorer/src/connector/queries/filterAndSortQuery.ts diff --git a/packages/graph-explorer/src/connector/emptyExplorer.ts b/packages/graph-explorer/src/connector/emptyExplorer.ts index 423b975f5..29033eec9 100644 --- a/packages/graph-explorer/src/connector/emptyExplorer.ts +++ b/packages/graph-explorer/src/connector/emptyExplorer.ts @@ -28,6 +28,7 @@ export const emptyExplorer: Explorer = { fetchNeighbors: async () => ({ vertices: [], edges: [] }), neighborCounts: async () => ({ counts: [] }), keywordSearch: async () => ({ vertices: [] }), + filterAndSortSearch: async () => ({ vertices: [] }), vertexDetails: async () => ({ vertices: [] }), edgeDetails: async () => ({ edges: [] }), rawQuery: async () => ({ results: [], rawResponse: null }), diff --git a/packages/graph-explorer/src/connector/gremlin/filterAndSort/filterAndSortTemplate.ts b/packages/graph-explorer/src/connector/gremlin/filterAndSort/filterAndSortTemplate.ts new file mode 100644 index 000000000..61fe52a93 --- /dev/null +++ b/packages/graph-explorer/src/connector/gremlin/filterAndSort/filterAndSortTemplate.ts @@ -0,0 +1,109 @@ +import type { + Criterion, + FilterAndSortRequest, +} from "@/connector/useGEFetchTypes"; + +import { escapeString } from "@/utils"; + +function escapeRegexLiteral(s: string): string { + return s.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&"); +} + +function criterionNumberTemplate({ name, value }: Criterion): string { + const num = Number(value); + if (!Number.isFinite(num)) { + return `has("${name}",eq(0))`; + } + return `has("${name}",eq(${num}))`; +} + +function criterionStringTemplate( + { name, value }: Criterion, + exactMatch: boolean, +): string { + const escaped = escapeString(String(value)); + const str = String(value); + const numericValue = Number(value); + const isNumeric = + str.trim() !== "" && + Number.isFinite(numericValue) && + String(numericValue) === str.trim(); + + if (isNumeric) { + return `has("${name}",eq(${numericValue}))`; + } + if (exactMatch) { + return `has("${name}","${escaped}")`; + } + const regexEscaped = escapeRegexLiteral(String(value)); + const pattern = `(?i).*${regexEscaped}.*`; + return `has("${name}",regex("${escapeString(pattern)}"))`; +} + +function criterionDateTemplate({ name, value }: Criterion): string { + return `has("${name}",eq(datetime(${value})))`; +} + +function criterionTemplate(criterion: Criterion, exactMatch: boolean): string { + switch (criterion.dataType) { + case "Number": + return criterionNumberTemplate(criterion); + case "Date": + return criterionDateTemplate(criterion); + case "String": + case undefined: + default: + return criterionStringTemplate(criterion, exactMatch); + } +} + +/** + * Builds a Gremlin traversal for g.V() with optional vertexTypes, filterCriteria, + * sortingCriteria, and range. + * + * @example + * vertexTypes = ["airport"] + * filterCriteria = [{ name: "country", value: "US" }] + * sortingCriteria = [{ name: "code", direction: "asc" }] + * limit = 20, offset = 0 + * + * g.V().hasLabel("airport").and(has("country",containing("US"))).order().by("code",asc).range(0,20) + */ +export default function filterAndSortTemplate({ + vertexTypes = [], + filterCriteria = [], + sortingCriteria = [], + limit, + offset = 0, + exactMatch = false, +}: FilterAndSortRequest): string { + let template = "g.V()"; + + if (vertexTypes.length > 0) { + const hasLabelContent = vertexTypes + .flatMap(type => type.split("::")) + .map(type => `"${type}"`) + .join(","); + template += `.hasLabel(${hasLabelContent})`; + } + + if (filterCriteria.length > 0) { + const andContent = filterCriteria + .map(c => criterionTemplate(c, exactMatch)) + .join(", "); + template += `.and(${andContent})`; + } + + if (sortingCriteria.length > 0) { + const byClauses = sortingCriteria + .map(s => `.by("${s.name}",${s.direction === "desc" ? "desc" : "asc"})`) + .join(""); + template += `.order()${byClauses}`; + } + + if (limit != null && limit > 0) { + template += `.range(${offset},${offset + limit})`; + } + + return template; +} diff --git a/packages/graph-explorer/src/connector/gremlin/filterAndSort/index.ts b/packages/graph-explorer/src/connector/gremlin/filterAndSort/index.ts new file mode 100644 index 000000000..81123a8bf --- /dev/null +++ b/packages/graph-explorer/src/connector/gremlin/filterAndSort/index.ts @@ -0,0 +1,47 @@ +import type { + ErrorResponse, + FilterAndSortRequest, + FilterAndSortResponse, +} from "@/connector/useGEFetchTypes"; + +import isErrorResponse from "@/connector/utils/isErrorResponse"; +import { createVertex } from "@/core"; + +import type { GVertexList } from "../types"; +import type { GremlinFetch } from "../types"; + +import mapApiVertex from "../mappers/mapApiVertex"; +import filterAndSortTemplate from "./filterAndSortTemplate"; + +type RawFilterAndSortResponse = { + requestId: string; + status: { + message: string; + code: number; + }; + result: { + data: GVertexList; + }; +}; + +async function filterAndSortSearch( + gremlinFetch: GremlinFetch, + req: FilterAndSortRequest, +): Promise { + const gremlinTemplate = filterAndSortTemplate(req); + const data = await gremlinFetch( + gremlinTemplate, + ); + + if (isErrorResponse(data)) { + throw new Error(data.detailedMessage); + } + + const vertices = data.result.data["@value"] + .map(value => mapApiVertex(value)) + .map(createVertex); + + return { vertices }; +} + +export default filterAndSortSearch; diff --git a/packages/graph-explorer/src/connector/gremlin/gremlinExplorer.ts b/packages/graph-explorer/src/connector/gremlin/gremlinExplorer.ts index ef244a554..afbb55b72 100644 --- a/packages/graph-explorer/src/connector/gremlin/gremlinExplorer.ts +++ b/packages/graph-explorer/src/connector/gremlin/gremlinExplorer.ts @@ -14,6 +14,7 @@ import fetchEdgeConnections from "./fetchEdgeConnections"; import fetchNeighbors from "./fetchNeighbors"; import fetchSchema from "./fetchSchema"; import fetchVertexTypeCounts from "./fetchVertexTypeCounts"; +import filterAndSortSearch from "./filterAndSort"; import keywordSearch from "./keywordSearch"; import { neighborCounts } from "./neighborCounts"; import { rawQuery } from "./rawQuery"; @@ -120,6 +121,16 @@ export function createGremlinExplorer( req, ); }, + async filterAndSortSearch(req, options) { + options ??= {}; + options.queryId = v4(); + + remoteLogger.info("[Gremlin Explorer] Fetching filter and sort..."); + return filterAndSortSearch( + _gremlinFetch(connection, featureFlags, options), + req, + ); + }, async vertexDetails(req, options) { options ??= {}; options.queryId = v4(); diff --git a/packages/graph-explorer/src/connector/queries/filterAndSortQuery.ts b/packages/graph-explorer/src/connector/queries/filterAndSortQuery.ts new file mode 100644 index 000000000..058d12bb5 --- /dev/null +++ b/packages/graph-explorer/src/connector/queries/filterAndSortQuery.ts @@ -0,0 +1,41 @@ +import { queryOptions } from "@tanstack/react-query"; + +import type { UpdateSchemaHandler } from "@/core/StateProvider/schema"; + +import type { FilterAndSortRequest } from "../useGEFetchTypes"; + +import { + getExplorer, + getStore, + setVertexDetailsQueryCache, + updateVertexGraphCanvasState, +} from "./helpers"; + +/** + * Performs a filter-and-sort query with the provided parameters. + * @param request The filter/sort parameters to use for the query. + * @param updateSchema Handler to update schema from results. + * @returns Query options for vertices matching the filter/sort criteria. + */ +export function filterAndSortQuery( + request: FilterAndSortRequest, + updateSchema: UpdateSchemaHandler, +) { + return queryOptions({ + queryKey: ["filter-and-sort", request], + queryFn: async ({ signal, meta, client }) => { + const explorer = getExplorer(meta); + const store = getStore(meta); + + const results = await explorer.filterAndSortSearch(request, { signal }); + + results.vertices.forEach(vertex => { + setVertexDetailsQueryCache(client, vertex); + }); + updateVertexGraphCanvasState(store, results.vertices); + updateSchema(results); + + return results; + }, + }); +} diff --git a/packages/graph-explorer/src/connector/queries/index.ts b/packages/graph-explorer/src/connector/queries/index.ts index 2b1a53a2e..4495352f0 100644 --- a/packages/graph-explorer/src/connector/queries/index.ts +++ b/packages/graph-explorer/src/connector/queries/index.ts @@ -1,3 +1,4 @@ +export * from "./filterAndSortQuery"; export * from "./schemaSyncQuery"; export * from "./searchQuery"; export * from "./bulkNeighborCountsQuery"; diff --git a/packages/graph-explorer/src/connector/useGEFetchTypes.ts b/packages/graph-explorer/src/connector/useGEFetchTypes.ts index 7e2874ffe..0ed479022 100644 --- a/packages/graph-explorer/src/connector/useGEFetchTypes.ts +++ b/packages/graph-explorer/src/connector/useGEFetchTypes.ts @@ -81,6 +81,18 @@ export type Criterion = { dataType?: "String" | "Number" | "Date"; }; +export type SortingCriterion = { + /** + * Attribute name. + */ + name: string; + /** + * Sorting direction. + * By default, "asc". + */ + direction: "asc" | "desc"; +}; + /** * A request for the neighbors and relationships for the given vertex, filtered * by the provided paramters. @@ -171,8 +183,38 @@ export type KeywordSearchRequest = { exactMatch?: boolean; }; +export type FilterAndSortRequest = { + /** + * Filter by vertex types. + */ + vertexTypes?: Array; + /** + * Filter criteria to apply to the request. + */ + filterCriteria?: Array; + /** + * Sorting criteria to apply to the request. + */ + sortingCriteria?: Array; + /** + * Limit the number of results. + * 0 = No limit. + */ + limit?: number; + /** + * Skip the given number of results. + */ + offset?: number; + /** + * Only return exact attribute value matches. + */ + exactMatch?: boolean; +}; + export type KeywordSearchResponse = { vertices: Vertex[] }; +export type FilterAndSortResponse = { vertices: Vertex[] }; + export type ErrorResponse = { code: string; detailedMessage: string; @@ -249,6 +291,10 @@ export type Explorer = { req: KeywordSearchRequest, options?: ExplorerRequestOptions, ) => Promise; + filterAndSortSearch: ( + req: FilterAndSortRequest, + options?: ExplorerRequestOptions, + ) => Promise; vertexDetails: ( req: VertexDetailsRequest, options?: ExplorerRequestOptions, diff --git a/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts b/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts index 26927b86a..187e76056 100644 --- a/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts +++ b/packages/graph-explorer/src/core/StateProvider/displayTypeConfigs.ts @@ -38,6 +38,7 @@ export type DisplayConfigAttribute = { name: string; displayLabel: string; isSearchable: boolean; + dataType?: "String" | "Number" | "Date"; }; /** Gets the matching vertex type config or a generated default value. */ @@ -166,6 +167,7 @@ export function mapToDisplayVertexTypeConfig( name: attr.name, displayLabel: textTransform(attr.name), isSearchable: isAttributeSearchable(attr), + dataType: toDisplayDataType(attr.dataType), })) .toSorted(sortAttributeByName); @@ -193,6 +195,7 @@ export function mapToDisplayEdgeTypeConfig( name: attr.name, displayLabel: textTransform(attr.name), isSearchable: isAttributeSearchable(attr), + dataType: toDisplayDataType(attr.dataType), })) .toSorted(sortAttributeByName); @@ -207,3 +210,12 @@ export function mapToDisplayEdgeTypeConfig( function isAttributeSearchable(attribute: AttributeConfig) { return attribute.dataType === "String"; } + +function toDisplayDataType( + dataType: string | undefined, +): DisplayConfigAttribute["dataType"] { + if (dataType === "Number" || dataType === "Date" || dataType === "String") { + return dataType; + } + return undefined; +} diff --git a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx index 94505b1ff..306e0407e 100644 --- a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx +++ b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx @@ -1,7 +1,9 @@ import { useQuery } from "@tanstack/react-query"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router"; +import type { DisplayConfigAttribute } from "@/core/StateProvider/displayTypeConfigs"; + import { Button, CheckIcon, @@ -37,9 +39,11 @@ import { import { ExternalExportControl } from "@/components/Tabular/controls/ExportControl/ExternalExportControl"; import Tabular from "@/components/Tabular/Tabular"; import { - type KeywordSearchRequest, + type Criterion, + filterAndSortQuery, + type FilterAndSortRequest, nodeCountByNodeTypeQuery, - searchQuery, + type SortingCriterion, } from "@/connector"; import { createVertexType, @@ -54,9 +58,12 @@ import { } from "@/core"; import { useVertexTypeConfig } from "@/core/ConfigurationProvider/useConfiguration"; import { useVertexStyling } from "@/core/StateProvider/userPreferences"; -import { useAddVertexToGraph, useHasVertexBeenAddedToGraph } from "@/hooks"; +import { + useAddVertexToGraph, + useDebounceValue, + useHasVertexBeenAddedToGraph, +} from "@/hooks"; import useTranslations from "@/hooks/useTranslations"; -import { logger } from "@/utils"; import { LABELS, RESERVED_ID_PROPERTY, @@ -72,6 +79,58 @@ const DEFAULT_COLUMN = { width: 150, }; +function tabularFiltersToCriteria( + filters: Array<{ id: string; value: unknown }>, + attributes: DisplayConfigAttribute[], +): Criterion[] { + const attrByName = new Map(attributes.map(a => [a.name, a])); + + return filters + .filter( + f => + f.value != null && + f.value !== "" && + (typeof f.value !== "string" || f.value.trim() !== ""), + ) + .map(f => { + const attr = attrByName.get(f.id); + const dataType = attr?.dataType; + + if (dataType === "Number") { + const num = Number(f.value); + return { + name: f.id, + operator: "eq" as const, + value: Number.isFinite(num) ? num : f.value, + dataType: "Number" as const, + }; + } + if (dataType === "Date") { + return { + name: f.id, + operator: "eq" as const, + value: f.value, + dataType: "Date" as const, + }; + } + return { + name: f.id, + operator: "like" as const, + value: f.value, + dataType: "String" as const, + }; + }); +} + +function tabularSortsToCriteria( + sorts: Array<{ id: string; desc?: boolean }>, +): SortingCriterion[] { + return sorts.map(s => ({ + name: s.id, + direction: s.desc ? ("desc" as const) : ("asc" as const), + })); +} + export default function DataExplorer() { const { vertexType } = useParams(); @@ -98,9 +157,51 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { const [tableInstance, setTableInstance] = useState | null>(null); + const [filterInput, setFilterInput] = useState([]); + const filterCriteria = useDebounceValue(filterInput, 300); + const [sortingCriteria, setSortingCriteria] = useState( + [], + ); + const initialFilterSync = useRef(true); + const initialSortSync = useRef(true); + const columns = useColumnDefinitions(vertexType); - const query = useDataExplorerQuery(vertexType, pageSize, pageIndex); + const onDataFilteredChange = ( + _: unknown, + filters: Array<{ id: string; value: unknown }>, + ) => { + setFilterInput( + tabularFiltersToCriteria(filters, displayTypeConfig.attributes), + ); + }; + + useEffect(() => { + if (initialFilterSync.current) { + initialFilterSync.current = false; + } else { + onPageIndexChange(0); + } + }, [filterCriteria, onPageIndexChange]); + + const onColumnSortedChange = ( + sorts: Array<{ id: string; desc?: boolean }>, + ) => { + setSortingCriteria(tabularSortsToCriteria(sorts)); + if (initialSortSync.current) { + initialSortSync.current = false; + } else { + onPageIndexChange(0); + } + }; + + const query = useDataExplorerQuery( + vertexType, + pageSize, + pageIndex, + filterCriteria, + sortingCriteria, + ); const displayVertices = useDisplayVerticesFromVertices( query.data?.vertices ?? [], ) @@ -120,14 +221,6 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { }); }; - useEffect(() => { - logger.log(tableInstance?.filters); - }, [tableInstance?.filters]); - - useEffect(() => { - logger.log(tableInstance?.sorts); - }, [tableInstance?.sorts]); - return ( @@ -179,6 +272,8 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { disablePagination={true} manualFilters={true} manualSorting={true} + onDataFilteredChange={onDataFilteredChange} + onColumnSortedChange={onColumnSortedChange} > {query.isPending ? ( @@ -371,14 +466,18 @@ function useDataExplorerQuery( vertexType: VertexType, pageSize: number, pageIndex: number, + filterCriteria?: Array, + sortingCriteria?: Array, ) { const updateSchema = useUpdateSchemaFromEntities(); - const searchRequest: KeywordSearchRequest = { + const searchRequest: FilterAndSortRequest = { vertexTypes: [vertexType], limit: pageSize, offset: pageIndex * pageSize, + filterCriteria, + sortingCriteria, }; - return useQuery(searchQuery(searchRequest, updateSchema)); + return useQuery(filterAndSortQuery(searchRequest, updateSchema)); }