Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
useEffect,
useMemo,
} from "react";

import VirtualTable from "../../../../../components/VirtualTable";
import useSearchStore from "../../../SearchState/index";
import {
getPrestoSearchResultsTableColumns,
PrestoSearchResult,
} from "./typings";
import {usePrestoSearchResults} from "./usePrestoSearchResults";


interface PrestoResultsVirtualTableProps {
tableHeight: number;
}

/**
* Renders Presto search results in a virtual table.
*
* @param props
* @param props.tableHeight
* @return
*/
const PrestoResultsVirtualTable = ({tableHeight}: PrestoResultsVirtualTableProps) => {
const {updateNumSearchResultsTable} = useSearchStore();
const prestoSearchResults = usePrestoSearchResults();

const columns = useMemo(
() => getPrestoSearchResultsTableColumns(prestoSearchResults || []),
[prestoSearchResults]
);

useEffect(() => {
const num = prestoSearchResults ?
prestoSearchResults.length :
0;

updateNumSearchResultsTable(num);
}, [
prestoSearchResults,
updateNumSearchResultsTable,
]);

return (
<VirtualTable<PrestoSearchResult>
columns={columns}
dataSource={prestoSearchResults || []}
pagination={false}
rowKey={(record) => record._id}
scroll={{y: tableHeight}}/>
);
};

export default PrestoResultsVirtualTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {TableProps} from "antd";


/**
* Structure of dynamic Presto search results data.
*/
interface PrestoSearchResult {
_id: string;
row: Record<string, unknown>;
}

/**
* Generates dynamic columns configuration for Presto query engine.
*
* @param data Array of Presto search results
* @return
*/
const getPrestoSearchResultsTableColumns = (
data: PrestoSearchResult[]
): NonNullable<TableProps<PrestoSearchResult>["columns"]> => {
if (0 === data.length || "undefined" === typeof data[0] || "undefined" === typeof data[0].row) {
return [];
}

return Object.keys(data[0].row)
.map((key) => ({
dataIndex: ["row",
key],
key: key,
title: key,
}));
};

export type {PrestoSearchResult};
export {getPrestoSearchResultsTableColumns};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import MongoSocketCollection from "../../../../../api/socket/MongoSocketCollection";
import {useCursor} from "../../../../../api/socket/useCursor";
import useSearchStore, {SEARCH_STATE_DEFAULT} from "../../../SearchState/index";
import {SEARCH_MAX_NUM_RESULTS} from "../typings";
import {PrestoSearchResult} from "./typings";


/**
* Custom hook to get Presto search results for the current searchJobId.
*
* @return
*/
const usePrestoSearchResults = () => {
const searchJobId = useSearchStore((state) => state.searchJobId);

const searchResultsCursor = useCursor<PrestoSearchResult>(
() => {
// If there is no active search job, there are no results to fetch. The cursor will
// return null.
if (searchJobId === SEARCH_STATE_DEFAULT.searchJobId) {
return null;
}

console.log(
`Subscribing to updates to Presto search results with job ID: ${searchJobId}`
);

// Retrieve 1k most recent results.
const options = {
sort: [
[
"_id",
"desc",
],
],
limit: SEARCH_MAX_NUM_RESULTS,
};

const collection = new MongoSocketCollection(searchJobId.toString());
return collection.find({}, options);
},
[searchJobId]
);

return searchResultsCursor;
};

export {usePrestoSearchResults};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {useEffect} from "react";

import VirtualTable from "../../../../components/VirtualTable";
import useSearchStore from "../../SearchState/index";
import {
SearchResult,
searchResultsTableColumns,
} from "./typings";
import {useSearchResults} from "./useSearchResults";


interface SearchResultsVirtualTableProps {
tableHeight: number;
}

/**
* Renders search results in a virtual table.
*
* @param props
* @param props.tableHeight
* @return
*/
const SearchResultsVirtualTable = ({tableHeight}: SearchResultsVirtualTableProps) => {
const {updateNumSearchResultsTable} = useSearchStore();
const searchResults = useSearchResults();

useEffect(() => {
const num = searchResults ?
searchResults.length :
0;

updateNumSearchResultsTable(num);
}, [
searchResults,
updateNumSearchResultsTable,
]);

return (
<VirtualTable<SearchResult>
columns={searchResultsTableColumns}
dataSource={searchResults || []}
pagination={false}
rowKey={(record) => record._id.toString()}
scroll={{y: tableHeight}}/>
);
};

export default SearchResultsVirtualTable;
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import {
useState,
} from "react";

import VirtualTable from "../../../../components/VirtualTable";
import useSearchStore from "../../SearchState/index";
import {
SearchResult,
searchResultsTableColumns,
TABLE_BOTTOM_PADDING,
} from "./typings";
import {useSearchResults} from "./useSearchResults";
CLP_QUERY_ENGINES,
SETTINGS_QUERY_ENGINE,
} from "../../../../config";
import PrestoResultsVirtualTable from "./Presto/PrestoResultsVirtualTable";
import SearchResultsVirtualTable from "./SearchResultsVirtualTable";
import {TABLE_BOTTOM_PADDING} from "./typings";


/**
Expand All @@ -20,22 +19,9 @@ import {useSearchResults} from "./useSearchResults";
* @return
*/
const SearchResultsTable = () => {
const {updateNumSearchResultsTable} = useSearchStore();
const searchResults = useSearchResults();
const [tableHeight, setTableHeight] = useState<number>(0);
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const num = searchResults ?
searchResults.length :
0;

updateNumSearchResultsTable(num);
}, [
searchResults,
updateNumSearchResultsTable,
]);

// Antd table requires a fixed height for virtual scrolling. The effect sets a fixed height
// based on the window height, container top, and fixed padding.
useEffect(() => {
Expand All @@ -60,12 +46,13 @@ const SearchResultsTable = () => {
ref={containerRef}
style={{outline: "none"}}
>
<VirtualTable<SearchResult>
columns={searchResultsTableColumns}
dataSource={searchResults || []}
pagination={false}
rowKey={(record) => record._id.toString()}
scroll={{y: tableHeight}}/>
{CLP_QUERY_ENGINES.PRESTO === SETTINGS_QUERY_ENGINE ?
(
<PrestoResultsVirtualTable tableHeight={tableHeight}/>
) :
(
<SearchResultsVirtualTable tableHeight={tableHeight}/>
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface SearchResult {
}

/**
* Columns configuration for the seach results table.
* Columns configuration for the search results table.
*/
const searchResultsTableColumns: NonNullable<TableProps<SearchResult>["columns"]> = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
throw error;
}

await mongoDb.createCollection(searchJobId);
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this cannot happen in presto-client-node, but in case we change to a more async client, how about adding this comment:

// In the very unlikely situation where the first data arrives before we create the collection,
// MongoDB automatically creates a collection on the first insert if it doesn't exist.
// In this case, `createCollection` does nothing instead of throwing an error, which is what happens in pymongo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think with the current code the situation you describe is not possible. see line here. Like if the data comes first, then it will just exit early and print a log, like it wont create a collection. I dont think the comment is neccesary since if it is actually possible which i doubt, then it will print error log

Copy link
Contributor

@hoophalab hoophalab Aug 12, 2025

Choose a reason for hiding this comment

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

We should do better than only runtime correctness, but I'm fine if we don't add a comment here as it doesn't make the code better and we might forget to update comments in the future.


reply.code(StatusCodes.CREATED);

return {searchJobId};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const prestoRowToObject = (
obj[col.name] = row[idx];
});

return obj;
// Object is wrapped in a `row` property to prevent conflicts with MongoDB's `_id` field.
return {row: obj};
};

/**
Expand Down