Further enhancement, client library sends up the column it wants enriched and then we can ignore everything else, makes a big difference for enriching users (with a lot of relationships).

This commit is contained in:
mike12345567 2023-10-05 18:23:18 +01:00
parent 069fd33964
commit 3e2f9dfc4e
6 changed files with 51 additions and 14 deletions

View File

@ -9,7 +9,9 @@ export const buildRelationshipEndpoints = API => ({
if (!tableId || !rowId) {
return []
}
const response = await API.get({ url: `/api/${tableId}/${rowId}/enrich` })
const response = await API.get({
url: `/api/${tableId}/${rowId}/enrich?field=${fieldName}`,
})
if (!fieldName) {
return response || []
} else {

View File

@ -224,14 +224,15 @@ export async function bulkDestroy(ctx: UserCtx) {
}
export async function fetchEnrichedRow(ctx: UserCtx) {
const fieldName = ctx.request.query.field as string | undefined
const db = context.getAppDB()
const tableId = utils.getTableId(ctx)
const rowId = ctx.params.rowId
const rowId = ctx.params.rowId as string
// need table to work out where links go in row, as well as the link docs
let response = await Promise.all([
sdk.tables.getTable(tableId),
utils.findRow(ctx, tableId, rowId),
linkRows.getLinkDocuments({ tableId, rowId }),
linkRows.getLinkDocuments({ tableId, rowId, fieldName }),
])
const table = response[0] as Table
const row = response[1] as Row
@ -248,8 +249,13 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
let final = []
for (let linkTable of linkTables) {
const relatedRows = linkedRows.filter(row => row.tableId === linkTable._id)
// include the row being enriched for performance reasons, don't need to fetch it to include
final = final.concat(
outputProcessing(linkTable, relatedRows, { preserveLinks: true })
outputProcessing(linkTable, relatedRows, {
// have to clone to avoid JSON cycle
fromRow: cloneDeep(row),
squash: true,
})
)
}
// finalise the promises

View File

@ -146,9 +146,14 @@ export async function updateLinks(args: {
* This is required for formula fields, this may only be utilised internally (for now).
* @param {object} table The table from which the rows originated.
* @param {array<object>} rows The rows which are to be enriched.
* @param {object} opts optional - options like passing in a base row to use for enrichment.
* @return {Promise<*>} returns the rows with all of the enriched relationships on it.
*/
export async function attachFullLinkedDocs(table: Table, rows: Row[]) {
export async function attachFullLinkedDocs(
table: Table,
rows: Row[],
opts?: { fromRow?: Row }
) {
const linkedTableIds = getLinkedTableIDs(table)
if (linkedTableIds.length === 0) {
return rows
@ -158,20 +163,34 @@ export async function attachFullLinkedDocs(table: Table, rows: Row[]) {
getLinksForRows(rows),
sdk.tables.getTables(linkedTableIds),
])
// find the links that pertain to one of the rows that is being enriched
const links = (response[0] as LinkDocumentValue[]).filter(link =>
rows.some(row => row._id === link.thisId)
)
// if fromRow has been passed in, then we don't need to fetch it (optimisation)
let linksWithoutFromRow = links
if (opts?.fromRow) {
linksWithoutFromRow = links.filter(link => link.id !== opts?.fromRow?._id)
}
const linkedTables = response[1] as Table[]
// clear any existing links that could be dupe'd
rows = clearRelationshipFields(table, rows)
// now get the docs and combine into the rows
let linked = await getFullLinkedDocs(links)
let linked = []
if (linksWithoutFromRow.length > 0) {
linked = await getFullLinkedDocs(linksWithoutFromRow)
}
for (let row of rows) {
for (let link of links.filter(link => link.thisId === row._id)) {
if (row[link.fieldName] == null) {
row[link.fieldName] = []
}
const linkedRow = linked.find(row => row._id === link.id)
let linkedRow: Row
if (opts?.fromRow && opts?.fromRow?._id === link.id) {
linkedRow = opts.fromRow!
} else {
linkedRow = linked.find(row => row._id === link.id)
}
if (linkedRow) {
const linkedTableId =
linkedRow.tableId || getRelatedTableForField(table, link.fieldName)

View File

@ -35,19 +35,22 @@ export const IncludeDocs = {
export async function getLinkDocuments(args: {
tableId?: string
rowId?: string
includeDocs?: any
fieldName?: string
includeDocs?: boolean
}): Promise<LinkDocumentValue[] | LinkDocument[]> {
const { tableId, rowId, includeDocs } = args
const { tableId, rowId, fieldName, includeDocs } = args
const db = context.getAppDB()
let params: any
if (rowId != null) {
if (rowId) {
params = { key: [tableId, rowId] }
}
// only table is known
else {
params = { startKey: [tableId], endKey: [tableId, {}] }
}
params.include_docs = !!includeDocs
if (includeDocs) {
params.include_docs = true
}
try {
let linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows
// filter to get unique entries
@ -67,6 +70,11 @@ export async function getLinkDocuments(args: {
return unique
})
// filter down to just the required field name
if (fieldName) {
linkRows = linkRows.filter(link => link.value.fieldName === fieldName)
}
// return docs if docs requested, otherwise just the value information
if (includeDocs) {
return linkRows.map(row => row.doc) as LinkDocument[]
} else {

View File

@ -104,7 +104,7 @@ async function getTable(tableId: string): Promise<Table> {
const table = await getExternalTable(datasourceId, tableName)
return { ...table, sql: isSQL(datasource) }
} else {
return db.get(tableId)
return db.get<Table>(tableId)
}
}

View File

@ -201,7 +201,7 @@ export async function inputProcessing(
export async function outputProcessing<T extends Row[] | Row>(
table: Table,
rows: T,
opts: { squash?: boolean; preserveLinks?: boolean } = {
opts: { squash?: boolean; preserveLinks?: boolean; fromRow?: Row } = {
squash: true,
preserveLinks: false,
}
@ -216,7 +216,9 @@ export async function outputProcessing<T extends Row[] | Row>(
}
// attach any linked row information
let enriched = !opts.preserveLinks
? await linkRows.attachFullLinkedDocs(table, safeRows)
? await linkRows.attachFullLinkedDocs(table, safeRows, {
fromRow: opts?.fromRow,
})
: safeRows
// process formulas