Skip to content

Commit

Permalink
Key val search (#13)
Browse files Browse the repository at this point in the history
* add key value search support

* allow n-logs filter to run on each key down
  • Loading branch information
vish9812 authored Sep 13, 2024
1 parent a307a79 commit 17f0624
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 20 deletions.
Binary file modified cmd/bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions cmd/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,7 @@ Example:
Caution: Processing multiple files will need at least twice the space as the logs files size.
For example, if you are analyzing 4GB of logs make sure you have 8GB of *free* RAM left for smoother processing.
A few utility commands can also be found here - https://github.com/vish9812/analog?tab=readme-ov-file#utility-commands.
`);
}
41 changes: 29 additions & 12 deletions ui/src/components/filters/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Alert,
Button,
Divider,
FormControlLabel,
Expand All @@ -15,19 +16,21 @@ import useViewModel, {
import { AgGridSolidRef } from "ag-grid-solid";
import { GridOptions } from "ag-grid-community";
import { Accessor, For, Show } from "solid-js";
import { Select } from "@thisbeyond/solid-select";
import { Select, createOptions } from "@thisbeyond/solid-select";
import { IconButton } from "@suid/material";
import AddIcon from "@suid/icons-material/Add";
import RemoveIcon from "@suid/icons-material/Remove";
import comparer from "@al/services/comparer";
import { GroupedMsg } from "@al/models/logData";
import GroupedMsgGrid from "../groupedMsgGrid";
import timesUtils from "@al/utils/times";

const texts = {
and: "AND",
or: "OR",
contains: "Contains",
notContains: "Not Contains",
allFields: "All Fields",
};

interface GridsOptions {
Expand Down Expand Up @@ -114,16 +117,14 @@ function Filters(props: FiltersProps) {
},
};

function handleEnterKey(e: KeyboardEvent) {
function handleFiltersEnterKey(e: KeyboardEvent) {
if (e.key === "Enter") {
handleFiltersChange();
}
}

function handleNLogsEnter(e: KeyboardEvent) {
if (e.key === "Enter") {
handleLogsSelectionChanged(gridsRefs);
}
function handleNLogsKeyDown(e: KeyboardEvent) {
timesUtils.debounce(handleLogsSelectionChanged, 600)(gridsRefs);
}

function getSimpleSearchHTML(term: SearchTerm, i: Accessor<number>) {
Expand All @@ -143,13 +144,26 @@ function Filters(props: FiltersProps) {
setFilters("terms", i(), "contains", val === texts.contains)
}
/>
<Select
class="app-select"
initialValue={texts.allFields}
{...createOptions([texts.allFields, ...comparer.last().keys])}
onChange={(val) =>
setFilters(
"terms",
i(),
"field",
val === texts.allFields ? "" : val
)
}
/>
<TextField
label="Search"
value={term.value}
onChange={(_, val) =>
setFilters("terms", i(), "value", val.toLowerCase())
}
onKeyDown={handleEnterKey}
onKeyDown={handleFiltersEnterKey}
/>
</>
);
Expand All @@ -163,19 +177,19 @@ function Filters(props: FiltersProps) {
label="Start Time(Inclusive)"
value={filters.startTime}
onChange={(_, val) => setFilters("startTime", val)}
onKeyDown={handleEnterKey}
onKeyDown={handleFiltersEnterKey}
/>
<TextField
label="End Time(Exclusive)"
value={filters.endTime}
onChange={(_, val) => setFilters("endTime", val)}
onKeyDown={handleEnterKey}
onKeyDown={handleFiltersEnterKey}
/>
<TextField
label="Regex Search"
value={filters.regex}
onChange={(_, val) => setFilters("regex", val)}
onKeyDown={handleEnterKey}
onKeyDown={handleFiltersEnterKey}
/>
<For each={filters.terms}>{getSimpleSearchHTML}</For>
<IconButton color="primary" onClick={() => handleNewSearchTerm(true)}>
Expand Down Expand Up @@ -220,16 +234,19 @@ function Filters(props: FiltersProps) {
onChange={(_, val) =>
setFilters("firstN", isNaN(+val) || +val < 0 ? 0 : +val)
}
onKeyDown={handleNLogsEnter}
onKeyDown={handleNLogsKeyDown}
/>
<TextField
label="Last N Logs"
value={filters.lastN}
onChange={(_, val) =>
setFilters("lastN", isNaN(+val) || +val < 0 ? 0 : +val)
}
onKeyDown={handleNLogsEnter}
onKeyDown={handleNLogsKeyDown}
/>
<Alert severity="info">
N-Logs works only with the below "selection" filters.
</Alert>
</Stack>
</Grid>
<Grid item xs={12} container spacing={2}>
Expand Down
15 changes: 12 additions & 3 deletions ui/src/components/filters/useViewModel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,18 @@ describe("useViewModel", () => {
} as any,
added: {
api: {
getSelectedRows: () => [{ logs: [{ id: 1 }, { id: 7 }] }],
getSelectedRows: () => [
{ logs: [{ id: 1 }, { id: 7 }, { id: 10 }, { id: 20 }] },
{ logs: [{ id: 3 }, { id: 30 }] },
],
},
} as any,
removed: undefined as any,
};

const vm = useViewModel(props);
vm.setFilters("firstN", 1);
vm.setFilters("lastN", 1);
vm.handleLogsSelectionChanged(gridsRefs);

const logs = [
Expand All @@ -251,11 +256,14 @@ describe("useViewModel", () => {
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 },
// { id: 7 }, // both skipped due to firstN=1 and lastN=1
// { id: 10 },
{ id: 20 },
{ id: 30 },
];
expect(vm.filters.logs, "filters.logs").toEqual(logs);
expect(props.onFiltersChange, "onFiltersChange").toBeCalledWith({
...defaultFilters(),
...vm.filters,
logs,
});

Expand Down Expand Up @@ -326,6 +334,7 @@ describe("useViewModel", () => {
{
and: true,
contains: true,
field: "",
value: "",
},
]);
Expand Down
3 changes: 3 additions & 0 deletions ui/src/components/filters/useViewModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface GridsRefs {
interface SearchTerm {
and: boolean;
contains: boolean;
field: string;
value: string;
}

Expand All @@ -43,6 +44,7 @@ function defaultFilters(): FiltersData {
{
and: true,
contains: true,
field: "",
value: "",
},
],
Expand Down Expand Up @@ -148,6 +150,7 @@ function useViewModel(props: FiltersProps) {
setFilters("terms", filters.terms.length, {
and: true,
contains: true,
field: "",
value: "",
});
return;
Expand Down
25 changes: 23 additions & 2 deletions ui/src/pages/analyze/useViewModel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,19 @@ describe("useViewModel", () => {
[LogData.logKeys.timestamp]: "2023-10-20T10:00:00.000Z",
[LogData.logKeys.fullData]: "test one two contains check four",
[LogData.logKeys.msg]: "test one two contains check four",
random_key_1: "Random Value",
},
{
[LogData.logKeys.id]: "24",
[LogData.logKeys.timestamp]: "2023-10-20T10:00:00.000Z",
[LogData.logKeys.fullData]: "test one two ands check four",
[LogData.logKeys.msg]: "test one two ands check four",
[LogData.logKeys.fullData]: "test one two ands four",
[LogData.logKeys.msg]: "test one two ands four",
},
{
[LogData.logKeys.id]: "25",
[LogData.logKeys.timestamp]: "2023-10-20T10:00:00.000Z",
[LogData.logKeys.fullData]: "test one two ands four",
[LogData.logKeys.msg]: "test one two ands four",
},
{
[LogData.logKeys.id]: "30",
Expand All @@ -88,6 +95,7 @@ describe("useViewModel", () => {
.mockReturnValueOnce(true)
.mockReturnValueOnce(true)
.mockReturnValueOnce(true)
.mockReturnValueOnce(true)
.mockReturnValueOnce(false);

vi.spyOn(timesUtils, "diffMinutes").mockReturnValue(100);
Expand All @@ -105,6 +113,7 @@ describe("useViewModel", () => {
endTime: "2023-10-20T11:00:00.000Z",
errorsOnly: true,
logs: [
comparer.last().logs[0],
comparer.last().logs[1],
comparer.last().logs[2],
comparer.last().logs[3],
Expand All @@ -115,19 +124,30 @@ describe("useViewModel", () => {
{
and: true,
contains: true,
field: "",
value: "ands",
},
{
and: true,
contains: false,
field: "",
value: "msg",
},
{
and: false,
contains: true,
field: "",
value: "check",
},
{
and: false,
contains: true,
field: "random_key_2",
value: "random value",
},
],
firstN: 0,
lastN: 0,
};

const vm = useViewModel();
Expand All @@ -136,6 +156,7 @@ describe("useViewModel", () => {
expect(vm.rows(), "rows").toEqual([
comparer.last().logs[2],
comparer.last().logs[3],
comparer.last().logs[4],
]);

expect(mockUseJumper.reset).toHaveBeenCalledOnce();
Expand Down
23 changes: 20 additions & 3 deletions ui/src/pages/analyze/useViewModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,30 @@ function useViewModel() {
}
if (keep && filtersData.terms) {
const ands = filtersData.terms.filter((t) => t.and);

let currCondition = true;
const updateCurrCondition = (term: SearchTerm) => {
const field = term.field;
const val = term.value.trim();
if (val) {
if (field && !val) {
currCondition = term.contains
? fullData.includes(val)
: !fullData.includes(val);
? log[field] !== undefined
: log[field] === undefined;

return;
}

if (val) {
if (field) {
const fieldVal = (log[field] && log[field].toString().toLowerCase()) || "";
currCondition = term.contains
? fieldVal.includes(val)
: !fieldVal.includes(val);
} else {
currCondition = term.contains
? fullData.includes(val)
: !fullData.includes(val);
}
}
};

Expand Down
27 changes: 27 additions & 0 deletions ui/src/utils/times.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import timesUtils from "./times";
import { Mock } from "vitest";

test.each`
date1 | date2 | expected
Expand All @@ -11,3 +12,29 @@ test.each`
expect(timesUtils.diffMinutes(date1, date2)).toBe(expected);
}
);

describe("debounce", () => {
let mockFn: Mock;
let debouncedFn: Function;

beforeEach(() => {
vi.useFakeTimers();
mockFn = vi.fn();
debouncedFn = timesUtils.debounce(mockFn, 20);
});

it("should not call function until delay is over", () => {
debouncedFn();
expect(mockFn).toHaveBeenCalledTimes(0);

vi.advanceTimersByTime(15); // Fast forward time to within the debounce duration
expect(mockFn).toHaveBeenCalledTimes(0);
});

it("should call function after delay has passed", () => {
debouncedFn();

vi.advanceTimersByTime(21); // Fast forward time beyond the debounce duration
expect(mockFn).toHaveBeenCalledTimes(1);
});
});
13 changes: 13 additions & 0 deletions ui/src/utils/times.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ function diffMinutes(date1: Date, date2: Date) {
return Math.abs(Math.round(diff));
}

function debounce(fn: Function, delayMS: number): Function {
let timer: ReturnType<typeof setTimeout>;

return function (...args: any[]) {
clearTimeout(timer);

timer = setTimeout(() => {
fn(...args);
}, delayMS);
};
}

const timesUtils = {
diffMinutes,
debounce,
};
export default timesUtils;

0 comments on commit 17f0624

Please sign in to comment.