Skip to content

Commit

Permalink
Run the correct histogram query
Browse files Browse the repository at this point in the history
  • Loading branch information
jameskerr committed Aug 3, 2022
1 parent 561c5ba commit 17b9b1b
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 27 deletions.
41 changes: 41 additions & 0 deletions src/app/core/models/zed-ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as zealot from "@brimdata/zealot"

export class ZedAst {
public tree: any
constructor(public script: string) {
this.tree = zealot.parseAst(script)
}

get from() {
return this.ops.find((o) => o.kind === "From")
}

get pools() {
const trunks = this.from?.trunks || []
return trunks.filter((t) => t.source.kind === "Pool").map((t) => t.source)
}

private _ops: any[]
get ops() {
if (this._ops) return this._ops
if (!this.tree || this.tree.error) return []
const list = []

function collectOps(op, list) {
list.push(op)
if (COMPOUND_PROCS.includes(op.kind)) {
for (const p of op.ops) collectOps(p, list)
} else if (op.kind === OP_EXPR_PROC) {
collectOps(op.expr, list)
}
}

collectOps(this.tree, list)
return (this._ops = list)
}
}

export const OP_EXPR_PROC = "OpExpr"
export const PARALLEL_PROC = "Parallel"
export const SEQUENTIAL_PROC = "Sequential"
export const COMPOUND_PROCS = [PARALLEL_PROC, SEQUENTIAL_PROC]
30 changes: 30 additions & 0 deletions src/app/core/models/zed-script.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {ZedScript} from "./zed-script"

test("time range", () => {
const script = new ZedScript(
"from sample.pcap range 2022-01-01T00:00:00Z to 2022-02-01T00:00:00Z"
)

expect(script.range).toEqual([
new Date(Date.UTC(2022, 0, 1, 0, 0, 0)),
new Date(Date.UTC(2022, 1, 1, 0, 0, 0)),
])
})

test("number range range", () => {
const script = new ZedScript("from sample.pcap range 0 to 100")

expect(script.range).toEqual([0, 100])
})

test("no range", () => {
const script = new ZedScript("from sample.pcap")

expect(script.range).toEqual(null)
})

test("no pool", () => {
const script = new ZedScript("hello world")

expect(script.range).toEqual(null)
})
28 changes: 28 additions & 0 deletions src/app/core/models/zed-script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {ZedAst} from "./zed-ast"

export class ZedScript {
constructor(public script: string) {}

private _ast: ZedAst
get ast() {
return this._ast || (this._ast = new ZedAst(this.script))
}

get range() {
const pool = this.ast.pools[0]
if (!pool) return null
const range = pool.range
if (!range) return null

return [parseRangeItem(range.lower), parseRangeItem(range.upper)]
}
}

function parseRangeItem({type, text}) {
switch (type) {
case "int64":
return parseInt(text)
case "time":
return new Date(text)
}
}
36 changes: 24 additions & 12 deletions src/app/query-home/histogram/MainHistogram/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import React from "react"
import {useSelector} from "react-redux"
import {ZedScript} from "src/app/core/models/zed-script"
import Dimens from "src/js/components/Dimens"
import Results from "src/js/state/Results"
import styled from "styled-components"

import ChartSVG from "../ChartSVG"
import {HISTOGRAM_RESULTS} from "../run-histogram-query"
import useMainHistogram from "./useMainHistogram"

const BG = styled.div`
height: 80px;
margin: 24px 16px 16px 16px;
`

export default function MainHistogramChart() {
const query = useSelector(Results.getQuery(HISTOGRAM_RESULTS))
const range = new ZedScript(query).range
if (!range) return null
return (
<Dimens
className="chart main-search-histogram"
data-testid="histogram"
render={(rect) => (
<MainHistogramSvg width={rect.width} height={rect.height} />
)}
/>
<BG>
<Dimens
className="chart main-search-histogram"
data-testid="histogram"
render={(rect) => (
<MainHistogramSvg width={rect.width} height={rect.height} />
)}
/>
</BG>
)
}

type Props = {height: number; width: number}

const MainHistogramSvg = React.memo(function MainHistogramSvg({
width,
height,
}: Props) {
function MainHistogramSvg({width, height}: Props) {
const chart = useMainHistogram(width, height)
return <ChartSVG chart={chart} />
})
}
57 changes: 50 additions & 7 deletions src/app/query-home/histogram/MainHistogram/useMainHistogram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {DateTuple} from "src/js/lib/TimeWindow"
import {Pen, HistogramChart} from "../types"
import {innerHeight, innerWidth} from "../dimens"

import Chart from "src/js/state/Chart"
import EmptyMessage from "src/js/components/EmptyMessage"
import HistogramTooltip from "src/js/components/HistogramTooltip"
import LoadingMessage from "src/js/components/LoadingMessage"
Expand All @@ -22,15 +21,30 @@ import xAxisTime from "../pens/xAxisTime"
import xPositionTooltip from "../pens/xPositionTooltip"
import yAxisSingleTick from "../pens/yAxisSingleTick"
import submitSearch from "../../flows/submit-search"
import Results from "src/js/state/Results"
import {HISTOGRAM_RESULTS} from "../run-histogram-query"
import {ChartData} from "src/js/state/Chart/types"
import {zed} from "packages/zealot/src"
import UniqArray from "src/js/models/UniqArray"
import MergeHash from "src/js/models/MergeHash"
import {ZedScript} from "src/app/core/models/zed-script"

const id = HISTOGRAM_RESULTS

// get pool
// make a new brim query with the values,
// get the pool name
// get the pool
// get the full pool range

export default function useMainHistogram(
width: number,
height: number
): HistogramChart {
const chartData = useSelector(Chart.getData)
const status = useSelector(Chart.getStatus)
const span = [new Date(0), new Date()] //useSelector(tab.getSpanAsDates)

const chartData = useSelector(Results.getValues(id)) as zed.Record[]
const status = useSelector(Results.getStatus(id))
const query = useSelector(Results.getQuery(id))
const range = new ZedScript(query).range
const dispatch = useDispatch()
const pens = useConst<Pen[]>([], () => {
function onDragEnd(span: DateTuple) {
Expand Down Expand Up @@ -63,7 +77,7 @@ export default function useMainHistogram(
})

return useMemo<HistogramChart>(() => {
const data = format(chartData, span)
const data = format(histogramFormat(chartData), range)
const maxY = d3.max(data.points, (d: {count: number}) => d.count) || 0
const oneCharWidth = 5.5366666667
const chars = d3.format(",")(maxY).length
Expand Down Expand Up @@ -96,5 +110,34 @@ export default function useMainHistogram(
.domain(data.span),
pens,
}
}, [chartData, status, span, width, height])
}, [chartData, status, range, width, height])
}

function histogramFormat(records: zed.Record[]): ChartData {
const paths = new UniqArray()
const table = new MergeHash()

records.forEach((r) => {
const [ts, path, count] = r.fields.map((f) => f.data) as [
zed.Time,
zed.String,
zed.Uint64
]

try {
const pathName = path.toString()
const key = ts.toDate().getTime()
const val = {[path.toString()]: count.toInt()}

table.merge(key, val)
paths.push(pathName)
} catch (e) {
console.log("Error rendering histogram: " + e.toString())
}
})

return {
table: table.toJSON(),
keys: paths.toArray(),
}
}
64 changes: 64 additions & 0 deletions src/app/query-home/histogram/build-histogram-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {ZedScript} from "src/app/core/models/zed-script"
import {Pool} from "src/app/core/pools/pool"
import {syncPool} from "src/app/core/pools/sync-pool"
import span from "src/js/brim/span"
import histogramInterval, {timeUnits} from "src/js/lib/histogramInterval"
import {DateTuple} from "src/js/lib/TimeWindow"
import Current from "src/js/state/Current"
import Editor from "src/js/state/Editor"
import Pools from "src/js/state/Pools"
import {Thunk} from "src/js/state/types"
import zql from "src/js/zql"
import {BrimQuery} from "../utils/brim-query"

export const buildHistogramQuery =
(): Thunk<Promise<string | null>> =>
async (dispatch, getState, {api}) => {
const poolName = api.current.poolName
const range = await dispatch(getRange(poolName))
console.log(range)
return histogramZed(poolName, range)
}

export const getRange =
(name: string): Thunk<Promise<DateTuple> | DateTuple> =>
(dispatch) => {
const queryRange = dispatch(getRangeFromQuery())
if (queryRange) return queryRange
else return dispatch(getRangeFromPool(name))
}

function histogramZed(pool: string, range: DateTuple | null) {
if (!range) return null
const {number, unit} = histogramInterval(range)
const interval = `${number}${timeUnits[unit]}`
return `from "${pool}" range ${zql`${range[0]}`} to ${zql`${range[1]}`} | count() by every(${interval}), _path`
}

const getRangeFromPool =
(poolName: string): Thunk<Promise<DateTuple>> =>
async (dispatch) => {
if (!poolName) return null
const pool = await dispatch(ensurePoolLoaded(poolName))
if (!pool) return
return span(pool.everythingSpan()).toDateTuple()
}

const getRangeFromQuery = (): Thunk<DateTuple> => (_, getState) => {
const snapshot = Editor.getSnapshot(getState())
const inputs = new ZedScript(BrimQuery.versionToZed(snapshot))
return inputs.range as DateTuple
}

const ensurePoolLoaded =
(name: string): Thunk<Promise<Pool>> =>
(dispatch, getState) => {
const lakeId = Current.getLakeId(getState())
const pool = Pools.getByName(lakeId, name)(getState())
if (!pool) return Promise.resolve(null)
if (pool.hasStats()) {
return Promise.resolve(pool)
} else {
return dispatch(syncPool(pool.id, lakeId))
}
}
39 changes: 39 additions & 0 deletions src/app/query-home/histogram/run-histogram-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {Collector} from "@brimdata/zealot"
import Results from "src/js/state/Results"
import {Thunk} from "src/js/state/types"
import {buildHistogramQuery} from "./build-histogram-query"

export const HISTOGRAM_RESULTS = "zui/histogram"
const id = HISTOGRAM_RESULTS

// This is looking very similar to Results/flows.ts fetchResults()
// Maybe this can be part of the api. It automatically saves it to
// the results reducer, and it paginates the query for you?
export const runHistogramQuery =
(): Thunk =>
async (dispatch, getState, {api}) => {
const tabId = api.current.tabId
const key = api.current.location.key
const query = await dispatch(buildHistogramQuery())
if (!query) return
console.log(query)
dispatch(Results.init({id, tabId, query, key}))
const collect: Collector = ({rows, shapesMap}) => {
dispatch(Results.setValues({id, tabId, values: rows}))
dispatch(Results.setShapes({id, tabId, shapes: shapesMap}))
}

try {
const res = await api.query(query, {tabId, id, collect})
await res.promise
dispatch(Results.success({id, tabId, count: res.rows.length}))
} catch (error) {
if (
error instanceof DOMException &&
error.message.match(/user aborted/)
) {
return
}
dispatch(Results.error({id, error, tabId}))
}
}
12 changes: 6 additions & 6 deletions src/app/query-home/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import Notice from "src/js/state/Notice"
import Tabs from "src/js/state/Tabs"
import {Thunk} from "src/js/state/types"
import {Location} from "history"
import {runHistogramQuery} from "./histogram/run-histogram-query"

export function loadRoute(location: Location): Thunk {
return (dispatch) => {
dispatch(Tabs.loaded(location.key))
dispatch(Notice.dismiss())
dispatch(Results.error({id: MAIN_RESULTS, error: null, tabId: ""}))
dispatch(syncEditor)
dispatch(fetchData(location))
dispatch(fetchData())
}
}

Expand All @@ -34,16 +35,15 @@ function syncEditor(dispatch, getState) {
})
}

function fetchData(location) {
function fetchData() {
return (dispatch, getState) => {
const key = Results.getKey(MAIN_RESULTS)(getState())
const version = Current.getVersion(getState())

if (key === location.key) return

startTransition(() => {
version &&
if (version) {
dispatch(Results.fetchFirstPage(BrimQuery.versionToZed(version)))
dispatch(runHistogramQuery())
}
})
}
}
2 changes: 1 addition & 1 deletion src/app/query-home/search-area/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const InputBackdrop = styled.div<{height: number}>`

const Submit = styled(SubmitButton)`
position: absolute;
right: 20px;
right: 16px;
bottom: 10px;
`

Expand Down
Loading

0 comments on commit 17b9b1b

Please sign in to comment.