metrics for view joins and grouped filters

This commit is contained in:
Martin McKeaveney 2024-12-05 17:32:07 +00:00
parent 9ef6cbd566
commit 3332f2fa22
16 changed files with 100 additions and 67 deletions

View File

@ -13,8 +13,8 @@ const EXCLUDED_EVENTS: Event[] = [
Event.ROLE_UPDATED, Event.ROLE_UPDATED,
Event.DATASOURCE_UPDATED, Event.DATASOURCE_UPDATED,
Event.QUERY_UPDATED, Event.QUERY_UPDATED,
Event.TABLE_UPDATED, // Event.TABLE_UPDATED,
Event.VIEW_UPDATED, // Event.VIEW_UPDATED,
Event.VIEW_FILTER_UPDATED, Event.VIEW_FILTER_UPDATED,
Event.VIEW_CALCULATION_UPDATED, Event.VIEW_CALCULATION_UPDATED,
Event.AUTOMATION_TRIGGER_UPDATED, Event.AUTOMATION_TRIGGER_UPDATED,

View File

@ -1,13 +1,14 @@
import { publishEvent } from "../events" import { publishEvent } from "../events"
import { import {
Event, Event,
TableExportFormat, FieldType,
Table, Table,
TableCreatedEvent, TableCreatedEvent,
TableUpdatedEvent,
TableDeletedEvent, TableDeletedEvent,
TableExportedEvent, TableExportedEvent,
TableExportFormat,
TableImportedEvent, TableImportedEvent,
TableUpdatedEvent,
} from "@budibase/types" } from "@budibase/types"
async function created(table: Table, timestamp?: string | number) { async function created(table: Table, timestamp?: string | number) {
@ -20,15 +21,35 @@ async function created(table: Table, timestamp?: string | number) {
await publishEvent(Event.TABLE_CREATED, properties, timestamp) await publishEvent(Event.TABLE_CREATED, properties, timestamp)
} }
async function updated(table: Table) { async function updated(oldTable: Table, newTable: Table) {
// only publish the event if it has fields we are interested in
let defaultValues, aiColumn
// check that new fields have been added
for (const key in newTable.schema) {
if (!oldTable.schema[key]) {
const newColumn = newTable.schema[key]
if ("default" in newColumn) {
defaultValues = true
}
if (newColumn.type === FieldType.AI) {
aiColumn = newColumn.operation
}
}
}
const properties: TableUpdatedEvent = { const properties: TableUpdatedEvent = {
tableId: table._id as string, tableId: newTable._id as string,
defaultValues,
aiColumn,
audited: { audited: {
name: table.name, name: newTable.name,
}, },
} }
if (defaultValues || aiColumn) {
await publishEvent(Event.TABLE_UPDATED, properties) await publishEvent(Event.TABLE_UPDATED, properties)
} }
}
async function deleted(table: Table) { async function deleted(table: Table) {
const properties: TableDeletedEvent = { const properties: TableDeletedEvent = {

View File

@ -11,6 +11,7 @@ import {
ViewFilterDeletedEvent, ViewFilterDeletedEvent,
ViewFilterUpdatedEvent, ViewFilterUpdatedEvent,
ViewUpdatedEvent, ViewUpdatedEvent,
View,
ViewV2, ViewV2,
ViewCalculation, ViewCalculation,
Table, Table,
@ -19,17 +20,27 @@ import {
/* eslint-disable */ /* eslint-disable */
async function created(view: Partial<ViewV2>, timestamp?: string | number) { async function created(view: ViewV2, timestamp?: string | number) {
const properties: ViewCreatedEvent = { const properties: ViewCreatedEvent = {
name: view.name, name: view.name,
type: view.type, type: view.type,
tableId: view.tableId,
} }
await publishEvent(Event.VIEW_CREATED, properties, timestamp) await publishEvent(Event.VIEW_CREATED, properties, timestamp)
} }
async function updated(view: View) { async function updated(newView: ViewV2) {
// // check whether any of the fields are different
let viewJoins = 0
for (const key in newView.schema) {
if (newView.schema[key]?.columns) {
viewJoins += Object.keys(newView.schema[key]?.columns).length
}
}
const properties: ViewUpdatedEvent = { const properties: ViewUpdatedEvent = {
tableId: view.tableId, tableId: newView.tableId,
groupedFilters: newView.queryUI?.groups?.length || 0,
viewJoins,
} }
await publishEvent(Event.VIEW_UPDATED, properties) await publishEvent(Event.VIEW_UPDATED, properties)
} }

View File

@ -45,13 +45,13 @@ export async function updateTable(
inputs.created = true inputs.created = true
} }
try { try {
const { datasource, table } = await sdk.tables.external.save( const { datasource, oldTable, table } = await sdk.tables.external.save(
datasourceId!, datasourceId!,
inputs, inputs,
{ tableId, renaming } { tableId, renaming }
) )
builderSocket?.emitDatasourceUpdate(ctx, datasource) builderSocket?.emitDatasourceUpdate(ctx, datasource)
return table return { table, oldTable }
} catch (err: any) { } catch (err: any) {
if (err instanceof Error) { if (err instanceof Error) {
ctx.throw(400, err.message) ctx.throw(400, err.message)

View File

@ -119,8 +119,15 @@ export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) {
await events.table.created(savedTable) await events.table.created(savedTable)
} else { } else {
const api = pickApi({ table }) const api = pickApi({ table })
savedTable = await api.updateTable(ctx, renaming) const { table: updatedTable, oldTable } = await api.updateTable(
await events.table.updated(savedTable) ctx,
renaming
)
savedTable = updatedTable
if (oldTable) {
await events.table.updated(oldTable, savedTable)
}
} }
if (renaming) { if (renaming) {
await sdk.views.renameLinkedViews(savedTable, renaming) await sdk.views.renameLinkedViews(savedTable, renaming)

View File

@ -30,14 +30,14 @@ export async function updateTable(
} }
try { try {
const { table } = await sdk.tables.internal.save(tableToSave, { const { table, oldTable } = await sdk.tables.internal.save(tableToSave, {
userId: ctx.user._id, userId: ctx.user._id,
rowsToImport: rows, rowsToImport: rows,
tableId: ctx.request.body._id, tableId: ctx.request.body._id,
renaming, renaming,
}) })
return table return { table, oldTable }
} catch (err: any) { } catch (err: any) {
if (err instanceof Error) { if (err instanceof Error) {
ctx.throw(400, err.message) ctx.throw(400, err.message)

View File

@ -60,35 +60,31 @@ export async function save(ctx: Ctx) {
existingTable.views[viewName] = existingTable.views[originalName] existingTable.views[viewName] = existingTable.views[originalName]
} }
await db.put(table) await db.put(table)
await handleViewEvents(
existingTable.views[viewName] as View,
table.views[viewName]
)
ctx.body = table.views[viewName] ctx.body = table.views[viewName]
builderSocket?.emitTableUpdate(ctx, table) builderSocket?.emitTableUpdate(ctx, table)
} }
export async function calculationEvents(existingView: View, newView: View) { // export async function calculationEvents(existingView: View, newView: View) {
const existingCalculation = existingView && existingView.calculation // const existingCalculation = existingView && existingView.calculation
const newCalculation = newView && newView.calculation // const newCalculation = newView && newView.calculation
//
if (existingCalculation && !newCalculation) { // if (existingCalculation && !newCalculation) {
await events.view.calculationDeleted(existingView) // await events.view.calculationDeleted(existingView)
} // }
//
if (!existingCalculation && newCalculation) { // if (!existingCalculation && newCalculation) {
await events.view.calculationCreated(newView) // await events.view.calculationCreated(newView)
} // }
//
if ( // if (
existingCalculation && // existingCalculation &&
newCalculation && // newCalculation &&
existingCalculation !== newCalculation // existingCalculation !== newCalculation
) { // ) {
await events.view.calculationUpdated(newView) // await events.view.calculationUpdated(newView)
} // }
} // }
export async function filterEvents(existingView: View, newView: View) { export async function filterEvents(existingView: View, newView: View) {
const hasExistingFilters = !!( const hasExistingFilters = !!(
@ -115,16 +111,6 @@ export async function filterEvents(existingView: View, newView: View) {
} }
} }
async function handleViewEvents(existingView: View, newView: View) {
if (!existingView) {
await events.view.created(newView)
} else {
await events.view.updated(newView)
}
await calculationEvents(existingView, newView)
await filterEvents(existingView, newView)
}
export async function destroy(ctx: Ctx) { export async function destroy(ctx: Ctx) {
const db = context.getAppDB() const db = context.getAppDB()
const viewName = decodeURIComponent(ctx.params.viewName) const viewName = decodeURIComponent(ctx.params.viewName)

View File

@ -151,7 +151,7 @@ export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
} }
const result = await sdk.views.create(tableId, parsedView) const result = await sdk.views.create(tableId, parsedView)
await events.view.created(view) await events.view.created(result)
ctx.status = 201 ctx.status = 201
ctx.body = { ctx.body = {
@ -190,10 +190,11 @@ export async function update(ctx: Ctx<UpdateViewRequest, ViewResponse>) {
primaryDisplay: view.primaryDisplay, primaryDisplay: view.primaryDisplay,
} }
const result = await sdk.views.update(tableId, parsedView) const { view: result } = await sdk.views.update(tableId, parsedView)
ctx.body = {
data: result, await events.view.updated(result)
}
ctx.body = { data: result }
const table = await sdk.tables.getTable(tableId) const table = await sdk.tables.getTable(tableId)
builderSocket?.emitTableUpdate(ctx, table) builderSocket?.emitTableUpdate(ctx, table)

View File

@ -14,7 +14,7 @@ export const backfill = async (appDb: Database, timestamp: string | number) => {
continue continue
} }
await events.view.created(view, timestamp) // await events.view.created(view, timestamp)
if (view.calculation) { if (view.calculation) {
await events.view.calculationCreated(view, timestamp) await events.view.calculationCreated(view, timestamp)

View File

@ -282,7 +282,7 @@ export async function save(
tableToSave.sql = true tableToSave.sql = true
} }
return { datasource: updatedDatasource, table: tableToSave } return { datasource: updatedDatasource, table: tableToSave, oldTable }
} }
export async function destroy(datasourceId: string, table: Table) { export async function destroy(datasourceId: string, table: Table) {

View File

@ -171,7 +171,7 @@ export async function save(
} }
// has to run after, make sure it has _id // has to run after, make sure it has _id
await runStaticFormulaChecks(table, { oldTable, deletion: false }) await runStaticFormulaChecks(table, { oldTable, deletion: false })
return { table } return { table, oldTable }
} }
export async function destroy(table: Table) { export async function destroy(table: Table) {

View File

@ -63,7 +63,7 @@ export async function create(
export async function update( export async function update(
tableId: string, tableId: string,
view: Readonly<ViewV2> view: Readonly<ViewV2>
): Promise<ViewV2> { ): Promise<{ view: ViewV2; existingView: ViewV2 }> {
const db = context.getAppDB() const db = context.getAppDB()
const { datasourceId, tableName } = breakExternalTableId(tableId) const { datasourceId, tableName } = breakExternalTableId(tableId)
@ -87,7 +87,7 @@ export async function update(
delete views[existingView.name] delete views[existingView.name]
views[view.name] = view views[view.name] = view
await db.put(ds) await db.put(ds)
return view return { view, existingView }
} }
export async function remove(viewId: string): Promise<ViewV2> { export async function remove(viewId: string): Promise<ViewV2> {

View File

@ -315,7 +315,10 @@ export async function create(
return view return view
} }
export async function update(tableId: string, view: ViewV2): Promise<ViewV2> { export async function update(
tableId: string,
view: ViewV2
): Promise<{ view: ViewV2; existingView: ViewV2 }> {
await guardViewSchema(tableId, view) await guardViewSchema(tableId, view)
return pickApi(tableId).update(tableId, view) return pickApi(tableId).update(tableId, view)

View File

@ -54,7 +54,7 @@ export async function create(
export async function update( export async function update(
tableId: string, tableId: string,
view: Readonly<ViewV2> view: Readonly<ViewV2>
): Promise<ViewV2> { ): Promise<{ view: ViewV2; existingView: ViewV2 }> {
const db = context.getAppDB() const db = context.getAppDB()
const table = await sdk.tables.getTable(tableId) const table = await sdk.tables.getTable(tableId)
table.views ??= {} table.views ??= {}
@ -76,7 +76,7 @@ export async function update(
delete table.views[existingView.name] delete table.views[existingView.name]
table.views[view.name] = view table.views[view.name] = view
await db.put(table) await db.put(table)
return view return { view, existingView }
} }
export async function remove(viewId: string): Promise<ViewV2> { export async function remove(viewId: string): Promise<ViewV2> {

View File

@ -1,4 +1,5 @@
import { BaseEvent, TableExportFormat } from "./event" import { BaseEvent, TableExportFormat } from "./event"
import { AIOperationEnum } from "../ai"
export interface TableCreatedEvent extends BaseEvent { export interface TableCreatedEvent extends BaseEvent {
tableId: string tableId: string
@ -9,6 +10,8 @@ export interface TableCreatedEvent extends BaseEvent {
export interface TableUpdatedEvent extends BaseEvent { export interface TableUpdatedEvent extends BaseEvent {
tableId: string tableId: string
defaultValues: boolean | undefined
aiColumn: AIOperationEnum | undefined
audited: { audited: {
name: string name: string
} }

View File

@ -1,15 +1,16 @@
import { ViewCalculation, ViewV2Schema, ViewV2Type } from "../../documents" import { ViewCalculation, ViewV2Type } from "../../documents"
import { BaseEvent, TableExportFormat } from "./event" import { BaseEvent, TableExportFormat } from "./event"
import { LegacyFilter, SortOrder, SortType, UISearchFilter } from "../../api"
import { SearchFilters } from "../search"
export interface ViewCreatedEvent extends BaseEvent { export interface ViewCreatedEvent extends BaseEvent {
name: string name: string
type?: ViewV2Type type?: ViewV2Type
tableId: string
} }
export interface ViewUpdatedEvent extends BaseEvent { export interface ViewUpdatedEvent extends BaseEvent {
tableId: string tableId: string
groupedFilters: number
viewJoins: number
} }
export interface ViewDeletedEvent extends BaseEvent { export interface ViewDeletedEvent extends BaseEvent {