дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
feat(database): search and pagination on result query (#1432)
Adds a search bar to query result grids (match case / words / regex, previous/next occurrence, Filter rows) and a pager for console results (first/previous/next/last, page size). Later pages re-run the read-only query with a skip, which every driver reads past without decoding, so only one page crosses IPC; Last counts rows with the statement wrapped so a trailing line comment can't swallow the closing parenthesis. Closes #1431 Co-authored-by: kidp2h <[email protected]>
Цей коміт міститься в:
1 джерело
7856fabbfe
коміт
75c4ca0be8
22 files changed
+973
-97
No files matched your search
@@ -12,6 +12,10 @@ import {
|
||||
import { dropSql } from "./objects";
|
||||
import {
|
||||
aggregates,
|
||||
cellMatcher,
|
||||
findCells,
|
||||
pageFetch,
|
||||
pageWindow,
|
||||
chartSeries,
|
||||
compareCells,
|
||||
distinctValues,
|
||||
@@ -219,3 +223,81 @@ describe("dropping schema objects", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("grid search", () => {
|
||||
const opts = { caseSensitive: false, wholeWord: false, regex: false };
|
||||
|
||||
it("matches cell text with Match Case, Words and Regex", () => {
|
||||
const m = cellMatcher("shang", opts);
|
||||
expect(typeof m).toBe("function");
|
||||
const test = m as (v: string) => boolean;
|
||||
expect(test("Shanghai")).toBe(true);
|
||||
expect(test("Beijing")).toBe(false);
|
||||
expect((cellMatcher("shang", { ...opts, caseSensitive: true }) as (v: string) => boolean)("Shanghai")).toBe(false);
|
||||
const word = cellMatcher("23", { ...opts, wholeWord: true }) as (v: string) => boolean;
|
||||
expect(word("23")).toBe(true);
|
||||
expect(word("123")).toBe(false);
|
||||
expect(word("a 23 b")).toBe(true);
|
||||
const re = cellMatcher(String.raw`^\d\.\d$`, { ...opts, regex: true }) as (v: string) => boolean;
|
||||
expect(re("2.5")).toBe(true);
|
||||
expect(re("10")).toBe(false);
|
||||
// A dot is literal without Regex.
|
||||
expect((cellMatcher("2.5", opts) as (v: string) => boolean)("215")).toBe(false);
|
||||
});
|
||||
|
||||
it("reports no query and a bad regex", () => {
|
||||
expect(cellMatcher("", opts)).toBeNull();
|
||||
expect(cellMatcher("(", { ...opts, regex: true })).toBe("invalid");
|
||||
});
|
||||
|
||||
it("steps over astral characters on empty matches in Words mode", () => {
|
||||
for (const query of ["x*", "|", "^", String.raw`\d*`, String.raw`\b`]) {
|
||||
const m = cellMatcher(query, { ...opts, regex: true, wholeWord: true }) as (v: string) => boolean;
|
||||
expect(() => m("a😀b")).not.toThrow();
|
||||
expect(typeof m("😀")).toBe("boolean");
|
||||
}
|
||||
});
|
||||
|
||||
it("finds the matching cells row by row, skipping NULL", () => {
|
||||
const m = cellMatcher("i", opts) as (v: string) => boolean;
|
||||
expect(findCells([0, 1, 2, 3], 3, value, m)).toEqual([
|
||||
{ row: 0, col: 1 },
|
||||
{ row: 1, col: 1 },
|
||||
{ row: 2, col: 1 },
|
||||
]);
|
||||
// Display rows are positions in `rows`.
|
||||
expect(findCells([3, 1], 3, value, m)).toEqual([{ row: 1, col: 1 }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("result pages", () => {
|
||||
it("describes the rows of a page, IDEA style", () => {
|
||||
expect(pageWindow({ page: 0, pageSize: 500, rows: 500, hasMore: true, total: null })).toEqual({
|
||||
first: 1,
|
||||
last: 500,
|
||||
total: null,
|
||||
lastPage: null,
|
||||
});
|
||||
expect(pageWindow({ page: 1, pageSize: 500, rows: 1, hasMore: false, total: null })).toEqual({
|
||||
first: 501,
|
||||
last: 501,
|
||||
total: 501,
|
||||
lastPage: 1,
|
||||
});
|
||||
expect(pageWindow({ page: 0, pageSize: 100, rows: 100, hasMore: true, total: 1234 })).toMatchObject({
|
||||
total: 1234,
|
||||
lastPage: 12,
|
||||
});
|
||||
expect(pageWindow({ page: 0, pageSize: 100, rows: 0, hasMore: false, total: null })).toEqual({
|
||||
first: 0,
|
||||
last: 0,
|
||||
total: 0,
|
||||
lastPage: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("slices the rows of a page from a fetch of every row up to it", () => {
|
||||
expect(pageFetch(0, 500)).toEqual({ maxRows: 500, skip: 0 });
|
||||
expect(pageFetch(2, 100)).toEqual({ maxRows: 100, skip: 200 });
|
||||
});
|
||||
});
|
||||
Reference in new issue
Заблокувати користувача