Improving typing around search, there was duplicates of SearchParams and SearchResponse - which were a little different, bring all of this together under the types library.
This commit is contained in:
parent
12c168e6eb
commit
4c755b3af3
|
@ -1,28 +1,16 @@
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
import { getCouchInfo } from "./couch"
|
import { getCouchInfo } from "./couch"
|
||||||
import { SearchFilters, Row, EmptyFilterOption } from "@budibase/types"
|
import {
|
||||||
|
SearchFilters,
|
||||||
|
Row,
|
||||||
|
EmptyFilterOption,
|
||||||
|
SearchResponse,
|
||||||
|
SearchParams,
|
||||||
|
WithRequired,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
const QUERY_START_REGEX = /\d[0-9]*:/g
|
const QUERY_START_REGEX = /\d[0-9]*:/g
|
||||||
|
|
||||||
interface SearchResponse<T> {
|
|
||||||
rows: T[] | any[]
|
|
||||||
bookmark?: string
|
|
||||||
totalRows: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SearchParams<T> = {
|
|
||||||
tableId?: string
|
|
||||||
sort?: string
|
|
||||||
sortOrder?: string
|
|
||||||
sortType?: string
|
|
||||||
limit?: number
|
|
||||||
bookmark?: string
|
|
||||||
version?: string
|
|
||||||
indexer?: () => Promise<any>
|
|
||||||
disableEscaping?: boolean
|
|
||||||
rows?: T | Row[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeKeyNumbering(key: any): string {
|
export function removeKeyNumbering(key: any): string {
|
||||||
if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
|
if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
|
||||||
const parts = key.split(":")
|
const parts = key.split(":")
|
||||||
|
@ -44,7 +32,7 @@ export class QueryBuilder<T> {
|
||||||
#query: SearchFilters
|
#query: SearchFilters
|
||||||
#limit: number
|
#limit: number
|
||||||
#sort?: string
|
#sort?: string
|
||||||
#bookmark?: string
|
#bookmark?: string | number
|
||||||
#sortOrder: string
|
#sortOrder: string
|
||||||
#sortType: string
|
#sortType: string
|
||||||
#includeDocs: boolean
|
#includeDocs: boolean
|
||||||
|
@ -130,7 +118,7 @@ export class QueryBuilder<T> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setBookmark(bookmark?: string) {
|
setBookmark(bookmark?: string | number) {
|
||||||
if (bookmark != null) {
|
if (bookmark != null) {
|
||||||
this.#bookmark = bookmark
|
this.#bookmark = bookmark
|
||||||
}
|
}
|
||||||
|
@ -226,14 +214,20 @@ export class QueryBuilder<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
preprocess(
|
||||||
* Preprocesses a value before going into a lucene search.
|
value: any,
|
||||||
* Transforms strings to lowercase and wraps strings and bools in quotes.
|
{
|
||||||
* @param value The value to process
|
escape,
|
||||||
* @param options The preprocess options
|
lowercase,
|
||||||
* @returns {string|*}
|
wrap,
|
||||||
*/
|
type,
|
||||||
preprocess(value: any, { escape, lowercase, wrap, type }: any = {}) {
|
}: {
|
||||||
|
escape?: boolean
|
||||||
|
lowercase?: boolean
|
||||||
|
wrap?: boolean
|
||||||
|
type?: string
|
||||||
|
} = {}
|
||||||
|
): string | any {
|
||||||
const hasVersion = !!this.#version
|
const hasVersion = !!this.#version
|
||||||
// Determine if type needs wrapped
|
// Determine if type needs wrapped
|
||||||
const originalType = typeof value
|
const originalType = typeof value
|
||||||
|
@ -561,7 +555,7 @@ async function runQuery<T>(
|
||||||
url: string,
|
url: string,
|
||||||
body: any,
|
body: any,
|
||||||
cookie: string
|
cookie: string
|
||||||
): Promise<SearchResponse<T>> {
|
): Promise<WithRequired<SearchResponse<T>, "totalRows">> {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -575,7 +569,7 @@ async function runQuery<T>(
|
||||||
}
|
}
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
|
||||||
let output: SearchResponse<T> = {
|
let output: WithRequired<SearchResponse<T>, "totalRows"> = {
|
||||||
rows: [],
|
rows: [],
|
||||||
totalRows: 0,
|
totalRows: 0,
|
||||||
}
|
}
|
||||||
|
@ -613,63 +607,51 @@ async function recursiveSearch<T>(
|
||||||
dbName: string,
|
dbName: string,
|
||||||
index: string,
|
index: string,
|
||||||
query: any,
|
query: any,
|
||||||
params: any
|
params: SearchParams
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const bookmark = params.bookmark
|
const bookmark = params.bookmark
|
||||||
const rows = params.rows || []
|
const rows = params.rows || []
|
||||||
if (rows.length >= params.limit) {
|
if (params.limit && rows.length >= params.limit) {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
let pageSize = QueryBuilder.maxLimit
|
let pageSize = QueryBuilder.maxLimit
|
||||||
if (rows.length > params.limit - QueryBuilder.maxLimit) {
|
if (params.limit && rows.length > params.limit - QueryBuilder.maxLimit) {
|
||||||
pageSize = params.limit - rows.length
|
pageSize = params.limit - rows.length
|
||||||
}
|
}
|
||||||
const page = await new QueryBuilder<T>(dbName, index, query)
|
const queryBuilder = new QueryBuilder<T>(dbName, index, query)
|
||||||
|
queryBuilder
|
||||||
.setVersion(params.version)
|
.setVersion(params.version)
|
||||||
.setTable(params.tableId)
|
|
||||||
.setBookmark(bookmark)
|
.setBookmark(bookmark)
|
||||||
.setLimit(pageSize)
|
.setLimit(pageSize)
|
||||||
.setSort(params.sort)
|
.setSort(params.sort)
|
||||||
.setSortOrder(params.sortOrder)
|
.setSortOrder(params.sortOrder)
|
||||||
.setSortType(params.sortType)
|
.setSortType(params.sortType)
|
||||||
.run()
|
|
||||||
|
if (params.tableId) {
|
||||||
|
queryBuilder.setTable(params.tableId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = await queryBuilder.run()
|
||||||
if (!page.rows.length) {
|
if (!page.rows.length) {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
if (page.rows.length < QueryBuilder.maxLimit) {
|
if (page.rows.length < QueryBuilder.maxLimit) {
|
||||||
return [...rows, ...page.rows]
|
return [...rows, ...page.rows]
|
||||||
}
|
}
|
||||||
const newParams = {
|
const newParams: SearchParams = {
|
||||||
...params,
|
...params,
|
||||||
bookmark: page.bookmark,
|
bookmark: page.bookmark,
|
||||||
rows: [...rows, ...page.rows],
|
rows: [...rows, ...page.rows] as Row[],
|
||||||
}
|
}
|
||||||
return await recursiveSearch(dbName, index, query, newParams)
|
return await recursiveSearch(dbName, index, query, newParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a paginated search. A bookmark will be returned to allow the next
|
|
||||||
* page to be fetched. There is a max limit off 200 results per page in a
|
|
||||||
* paginated search.
|
|
||||||
* @param dbName Which database to run a lucene query on
|
|
||||||
* @param index Which search index to utilise
|
|
||||||
* @param query The JSON query structure
|
|
||||||
* @param params The search params including:
|
|
||||||
* tableId {string} The table ID to search
|
|
||||||
* sort {string} The sort column
|
|
||||||
* sortOrder {string} The sort order ("ascending" or "descending")
|
|
||||||
* sortType {string} Whether to treat sortable values as strings or
|
|
||||||
* numbers. ("string" or "number")
|
|
||||||
* limit {number} The desired page size
|
|
||||||
* bookmark {string} The bookmark to resume from
|
|
||||||
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
|
|
||||||
*/
|
|
||||||
export async function paginatedSearch<T>(
|
export async function paginatedSearch<T>(
|
||||||
dbName: string,
|
dbName: string,
|
||||||
index: string,
|
index: string,
|
||||||
query: SearchFilters,
|
query: SearchFilters,
|
||||||
params: SearchParams<T>
|
params: SearchParams
|
||||||
) {
|
): Promise<SearchResponse<T>> {
|
||||||
let limit = params.limit
|
let limit = params.limit
|
||||||
if (limit == null || isNaN(limit) || limit < 0) {
|
if (limit == null || isNaN(limit) || limit < 0) {
|
||||||
limit = 50
|
limit = 50
|
||||||
|
@ -713,29 +695,12 @@ export async function paginatedSearch<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a full search, fetching multiple pages if required to return the
|
|
||||||
* desired amount of results. There is a limit of 1000 results to avoid
|
|
||||||
* heavy performance hits, and to avoid client components breaking from
|
|
||||||
* handling too much data.
|
|
||||||
* @param dbName Which database to run a lucene query on
|
|
||||||
* @param index Which search index to utilise
|
|
||||||
* @param query The JSON query structure
|
|
||||||
* @param params The search params including:
|
|
||||||
* tableId {string} The table ID to search
|
|
||||||
* sort {string} The sort column
|
|
||||||
* sortOrder {string} The sort order ("ascending" or "descending")
|
|
||||||
* sortType {string} Whether to treat sortable values as strings or
|
|
||||||
* numbers. ("string" or "number")
|
|
||||||
* limit {number} The desired number of results
|
|
||||||
* @returns {Promise<{rows: *}>}
|
|
||||||
*/
|
|
||||||
export async function fullSearch<T>(
|
export async function fullSearch<T>(
|
||||||
dbName: string,
|
dbName: string,
|
||||||
index: string,
|
index: string,
|
||||||
query: SearchFilters,
|
query: SearchFilters,
|
||||||
params: SearchParams<T>
|
params: SearchParams
|
||||||
) {
|
): Promise<{ rows: Row[] }> {
|
||||||
let limit = params.limit
|
let limit = params.limit
|
||||||
if (limit == null || isNaN(limit) || limit < 0) {
|
if (limit == null || isNaN(limit) || limit < 0) {
|
||||||
limit = 1000
|
limit = 1000
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import { newid } from "../../docIds/newid"
|
import { newid } from "../../docIds/newid"
|
||||||
import { getDB } from "../db"
|
import { getDB } from "../db"
|
||||||
import { Database, EmptyFilterOption } from "@budibase/types"
|
import {
|
||||||
import { QueryBuilder, paginatedSearch, fullSearch } from "../lucene"
|
Database,
|
||||||
|
EmptyFilterOption,
|
||||||
|
SortOrder,
|
||||||
|
SortType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { fullSearch, paginatedSearch, QueryBuilder } from "../lucene"
|
||||||
|
|
||||||
const INDEX_NAME = "main"
|
const INDEX_NAME = "main"
|
||||||
|
const TABLE_ID = ""
|
||||||
|
|
||||||
const index = `function(doc) {
|
const index = `function(doc) {
|
||||||
let props = ["property", "number", "array"]
|
let props = ["property", "number", "array"]
|
||||||
|
@ -25,8 +31,16 @@ describe("lucene", () => {
|
||||||
dbName = `db-${newid()}`
|
dbName = `db-${newid()}`
|
||||||
// create the DB for testing
|
// create the DB for testing
|
||||||
db = getDB(dbName)
|
db = getDB(dbName)
|
||||||
await db.put({ _id: newid(), property: "word", array: ["1", "4"] })
|
await db.put({
|
||||||
await db.put({ _id: newid(), property: "word2", array: ["3", "1"] })
|
_id: newid(),
|
||||||
|
property: "word",
|
||||||
|
array: ["1", "4"],
|
||||||
|
})
|
||||||
|
await db.put({
|
||||||
|
_id: newid(),
|
||||||
|
property: "word2",
|
||||||
|
array: ["3", "1"],
|
||||||
|
})
|
||||||
await db.put({
|
await db.put({
|
||||||
_id: newid(),
|
_id: newid(),
|
||||||
property: "word3",
|
property: "word3",
|
||||||
|
@ -338,10 +352,11 @@ describe("lucene", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
tableId: TABLE_ID,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
sort: "property",
|
sort: "property",
|
||||||
sortType: "string",
|
sortType: SortType.STRING,
|
||||||
sortOrder: "desc",
|
sortOrder: SortOrder.DESCENDING,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
expect(page.rows.length).toBe(1)
|
expect(page.rows.length).toBe(1)
|
||||||
|
@ -360,7 +375,10 @@ describe("lucene", () => {
|
||||||
property: "wo",
|
property: "wo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{}
|
{
|
||||||
|
tableId: TABLE_ID,
|
||||||
|
query: {},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
expect(page.rows.length).toBe(3)
|
expect(page.rows.length).toBe(3)
|
||||||
})
|
})
|
||||||
|
|
|
@ -32,7 +32,6 @@ export { default as env } from "./environment"
|
||||||
export * as blacklist from "./blacklist"
|
export * as blacklist from "./blacklist"
|
||||||
export * as docUpdates from "./docUpdates"
|
export * as docUpdates from "./docUpdates"
|
||||||
export * from "./utils/Duration"
|
export * from "./utils/Duration"
|
||||||
export { SearchParams } from "./db"
|
|
||||||
export * as docIds from "./docIds"
|
export * as docIds from "./docIds"
|
||||||
export * as security from "./security"
|
export * as security from "./security"
|
||||||
// Add context to tenancy for backwards compatibility
|
// Add context to tenancy for backwards compatibility
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
PatchRowRequest,
|
PatchRowRequest,
|
||||||
PatchRowResponse,
|
PatchRowResponse,
|
||||||
Row,
|
Row,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
SearchRowRequest,
|
SearchRowRequest,
|
||||||
SearchRowResponse,
|
SearchRowResponse,
|
||||||
UserCtx,
|
UserCtx,
|
||||||
|
@ -192,7 +192,7 @@ export async function destroy(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
|
export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const tableId = utils.getTableId(ctx)
|
||||||
|
|
||||||
const searchParams: SearchParams = {
|
const searchParams: RowSearchParams = {
|
||||||
...ctx.request.body,
|
...ctx.request.body,
|
||||||
tableId,
|
tableId,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import {
|
||||||
SearchRowResponse,
|
SearchRowResponse,
|
||||||
SearchViewRowRequest,
|
SearchViewRowRequest,
|
||||||
RequiredKeys,
|
RequiredKeys,
|
||||||
SearchParams,
|
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
|
RowSearchParams,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { dataFilters } from "@budibase/shared-core"
|
import { dataFilters } from "@budibase/shared-core"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
@ -57,7 +57,7 @@ export async function searchView(
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchOptions: RequiredKeys<SearchViewRowRequest> &
|
const searchOptions: RequiredKeys<SearchViewRowRequest> &
|
||||||
RequiredKeys<Pick<SearchParams, "tableId" | "query" | "fields">> = {
|
RequiredKeys<Pick<RowSearchParams, "tableId" | "query" | "fields">> = {
|
||||||
tableId: view.tableId,
|
tableId: view.tableId,
|
||||||
query,
|
query,
|
||||||
fields: viewFields,
|
fields: viewFields,
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
const nodeFetch = require("node-fetch")
|
const nodeFetch = require("node-fetch")
|
||||||
|
|
||||||
nodeFetch.mockSearch()
|
nodeFetch.mockSearch()
|
||||||
import { SearchParams } from "@budibase/backend-core"
|
import * as search from "../../../sdk/app/rows/search/utils"
|
||||||
import * as search from "../../../sdk/app/rows/search/internalSearch"
|
import { RowSearchParams, SortOrder, SortType } from "@budibase/types"
|
||||||
import { Row } from "@budibase/types"
|
|
||||||
|
|
||||||
// this will be mocked out for _search endpoint
|
// this will be mocked out for _search endpoint
|
||||||
const PARAMS: SearchParams<Row> = {
|
const PARAMS: RowSearchParams = {
|
||||||
|
query: {},
|
||||||
tableId: "ta_12345679abcdef",
|
tableId: "ta_12345679abcdef",
|
||||||
version: "1",
|
version: "1",
|
||||||
bookmark: undefined,
|
bookmark: undefined,
|
||||||
sort: undefined,
|
sort: undefined,
|
||||||
sortOrder: "ascending",
|
sortOrder: SortOrder.ASCENDING,
|
||||||
sortType: "string",
|
sortType: SortType.STRING,
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkLucene(resp: any, expected: any, params = PARAMS) {
|
function checkLucene(resp: any, expected: any, params = PARAMS) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Row,
|
Row,
|
||||||
|
RowSearchParams,
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
SearchParams,
|
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { isExternalTableID } from "../../../integrations/utils"
|
import { isExternalTableID } from "../../../integrations/utils"
|
||||||
|
@ -56,7 +56,9 @@ export function removeEmptyFilters(filters: SearchFilters) {
|
||||||
return filters
|
return filters
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function search(options: SearchParams): Promise<SearchResponse> {
|
export async function search(
|
||||||
|
options: RowSearchParams
|
||||||
|
): Promise<SearchResponse<Row>> {
|
||||||
const isExternalTable = isExternalTableID(options.tableId)
|
const isExternalTable = isExternalTableID(options.tableId)
|
||||||
if (isExternalTable) {
|
if (isExternalTable) {
|
||||||
return external.search(options)
|
return external.search(options)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
IncludeRelationship,
|
IncludeRelationship,
|
||||||
Row,
|
Row,
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import * as exporters from "../../../../api/controllers/view/exporters"
|
import * as exporters from "../../../../api/controllers/view/exporters"
|
||||||
|
@ -23,11 +23,14 @@ import pick from "lodash/pick"
|
||||||
import { outputProcessing } from "../../../../utilities/rowProcessor"
|
import { outputProcessing } from "../../../../utilities/rowProcessor"
|
||||||
import sdk from "../../../"
|
import sdk from "../../../"
|
||||||
|
|
||||||
export async function search(options: SearchParams): Promise<SearchResponse> {
|
export async function search(
|
||||||
|
options: RowSearchParams
|
||||||
|
): Promise<SearchResponse<Row>> {
|
||||||
const { tableId } = options
|
const { tableId } = options
|
||||||
const { paginate, query, ...params } = options
|
const { paginate, query, ...params } = options
|
||||||
const { limit } = params
|
const { limit } = params
|
||||||
let bookmark = (params.bookmark && parseInt(params.bookmark)) || undefined
|
let bookmark =
|
||||||
|
(params.bookmark && parseInt(params.bookmark as string)) || undefined
|
||||||
if (paginate && !bookmark) {
|
if (paginate && !bookmark) {
|
||||||
bookmark = 1
|
bookmark = 1
|
||||||
}
|
}
|
||||||
|
@ -92,7 +95,7 @@ export async function search(options: SearchParams): Promise<SearchResponse> {
|
||||||
rows = rows.map((r: any) => pick(r, fields))
|
rows = rows.map((r: any) => pick(r, fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
rows = await outputProcessing(table, rows, {
|
rows = await outputProcessing<Row[]>(table, rows, {
|
||||||
preserveLinks: true,
|
preserveLinks: true,
|
||||||
squash: true,
|
squash: true,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
import {
|
import { context, db, HTTPError } from "@budibase/backend-core"
|
||||||
context,
|
|
||||||
db,
|
|
||||||
HTTPError,
|
|
||||||
SearchParams as InternalSearchParams,
|
|
||||||
} from "@budibase/backend-core"
|
|
||||||
import env from "../../../../environment"
|
import env from "../../../../environment"
|
||||||
import { fullSearch, paginatedSearch } from "./internalSearch"
|
import { fullSearch, paginatedSearch, searchInputMapping } from "./utils"
|
||||||
import { getRowParams, InternalTables } from "../../../../db/utils"
|
import { getRowParams, InternalTables } from "../../../../db/utils"
|
||||||
import {
|
import {
|
||||||
Database,
|
Database,
|
||||||
Row,
|
|
||||||
Table,
|
|
||||||
SearchParams,
|
|
||||||
SearchResponse,
|
|
||||||
DocumentType,
|
DocumentType,
|
||||||
|
Row,
|
||||||
|
RowSearchParams,
|
||||||
|
SearchResponse,
|
||||||
|
SortType,
|
||||||
|
Table,
|
||||||
|
User,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { getGlobalUsersFromMetadata } from "../../../../utilities/global"
|
import { getGlobalUsersFromMetadata } from "../../../../utilities/global"
|
||||||
import { outputProcessing } from "../../../../utilities/rowProcessor"
|
import { outputProcessing } from "../../../../utilities/rowProcessor"
|
||||||
|
@ -32,16 +29,17 @@ import {
|
||||||
} from "../../../../api/controllers/view/utils"
|
} from "../../../../api/controllers/view/utils"
|
||||||
import sdk from "../../../../sdk"
|
import sdk from "../../../../sdk"
|
||||||
import { ExportRowsParams, ExportRowsResult } from "./types"
|
import { ExportRowsParams, ExportRowsResult } from "./types"
|
||||||
import { searchInputMapping } from "./utils"
|
|
||||||
import pick from "lodash/pick"
|
import pick from "lodash/pick"
|
||||||
import { breakRowIdField } from "../../../../integrations/utils"
|
import { breakRowIdField } from "../../../../integrations/utils"
|
||||||
|
|
||||||
export async function search(options: SearchParams): Promise<SearchResponse> {
|
export async function search(
|
||||||
|
options: RowSearchParams
|
||||||
|
): Promise<SearchResponse<Row>> {
|
||||||
const { tableId } = options
|
const { tableId } = options
|
||||||
|
|
||||||
const { paginate, query } = options
|
const { paginate, query } = options
|
||||||
|
|
||||||
const params: InternalSearchParams<any> = {
|
const params: RowSearchParams = {
|
||||||
tableId: options.tableId,
|
tableId: options.tableId,
|
||||||
sort: options.sort,
|
sort: options.sort,
|
||||||
sortOrder: options.sortOrder,
|
sortOrder: options.sortOrder,
|
||||||
|
@ -50,6 +48,7 @@ export async function search(options: SearchParams): Promise<SearchResponse> {
|
||||||
bookmark: options.bookmark,
|
bookmark: options.bookmark,
|
||||||
version: options.version,
|
version: options.version,
|
||||||
disableEscaping: options.disableEscaping,
|
disableEscaping: options.disableEscaping,
|
||||||
|
query: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
let table = await sdk.tables.getTable(tableId)
|
let table = await sdk.tables.getTable(tableId)
|
||||||
|
@ -57,7 +56,8 @@ export async function search(options: SearchParams): Promise<SearchResponse> {
|
||||||
if (params.sort && !params.sortType) {
|
if (params.sort && !params.sortType) {
|
||||||
const schema = table.schema
|
const schema = table.schema
|
||||||
const sortField = schema[params.sort]
|
const sortField = schema[params.sort]
|
||||||
params.sortType = sortField.type === "number" ? "number" : "string"
|
params.sortType =
|
||||||
|
sortField.type === "number" ? SortType.NUMBER : SortType.STRING
|
||||||
}
|
}
|
||||||
|
|
||||||
let response
|
let response
|
||||||
|
@ -71,7 +71,7 @@ export async function search(options: SearchParams): Promise<SearchResponse> {
|
||||||
if (response.rows && response.rows.length) {
|
if (response.rows && response.rows.length) {
|
||||||
// enrich with global users if from users table
|
// enrich with global users if from users table
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (tableId === InternalTables.USER_METADATA) {
|
||||||
response.rows = await getGlobalUsersFromMetadata(response.rows)
|
response.rows = await getGlobalUsersFromMetadata(response.rows as User[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.fields) {
|
if (options.fields) {
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { db as dbCore, context, SearchParams } from "@budibase/backend-core"
|
|
||||||
import { SearchFilters, Row, SearchIndex } from "@budibase/types"
|
|
||||||
|
|
||||||
export async function paginatedSearch(
|
|
||||||
query: SearchFilters,
|
|
||||||
params: SearchParams<Row>
|
|
||||||
) {
|
|
||||||
const appId = context.getAppId()
|
|
||||||
return dbCore.paginatedSearch(appId!, SearchIndex.ROWS, query, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fullSearch(
|
|
||||||
query: SearchFilters,
|
|
||||||
params: SearchParams<Row>
|
|
||||||
) {
|
|
||||||
const appId = context.getAppId()
|
|
||||||
return dbCore.fullSearch(appId!, SearchIndex.ROWS, query, params)
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
RelationshipFieldMetadata,
|
RelationshipFieldMetadata,
|
||||||
Row,
|
Row,
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
SortDirection,
|
SortDirection,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
|
@ -93,7 +93,9 @@ function buildTableMap(tables: Table[]) {
|
||||||
return tableMap
|
return tableMap
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function search(options: SearchParams): Promise<SearchResponse> {
|
export async function search(
|
||||||
|
options: RowSearchParams
|
||||||
|
): Promise<SearchResponse<Row>> {
|
||||||
const { tableId, paginate, query, ...params } = options
|
const { tableId, paginate, query, ...params } = options
|
||||||
|
|
||||||
const builder = new SqlQueryBuilder(SqlClient.SQL_LITE)
|
const builder = new SqlQueryBuilder(SqlClient.SQL_LITE)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
Row,
|
Row,
|
||||||
SourceName,
|
SourceName,
|
||||||
Table,
|
Table,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ describe("external search", () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const tableId = config.table!._id!
|
const tableId = config.table!._id!
|
||||||
|
|
||||||
const searchParams: SearchParams = {
|
const searchParams: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {},
|
query: {},
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ describe("external search", () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const tableId = config.table!._id!
|
const tableId = config.table!._id!
|
||||||
|
|
||||||
const searchParams: SearchParams = {
|
const searchParams: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {},
|
query: {},
|
||||||
fields: ["name", "age"],
|
fields: ["name", "age"],
|
||||||
|
@ -151,7 +151,7 @@ describe("external search", () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const tableId = config.table!._id!
|
const tableId = config.table!._id!
|
||||||
|
|
||||||
const searchParams: SearchParams = {
|
const searchParams: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {
|
query: {
|
||||||
oneOf: {
|
oneOf: {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
FieldType,
|
FieldType,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
@ -77,7 +77,7 @@ describe("internal", () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const tableId = config.table!._id!
|
const tableId = config.table!._id!
|
||||||
|
|
||||||
const searchParams: SearchParams = {
|
const searchParams: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {},
|
query: {},
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ describe("internal", () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const tableId = config.table!._id!
|
const tableId = config.table!._id!
|
||||||
|
|
||||||
const searchParams: SearchParams = {
|
const searchParams: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {},
|
query: {},
|
||||||
fields: ["name", "age"],
|
fields: ["name", "age"],
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
FieldType,
|
FieldType,
|
||||||
FieldTypeSubtypes,
|
FieldTypeSubtypes,
|
||||||
INTERNAL_TABLE_SOURCE_ID,
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
@ -47,7 +47,7 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
const userMedataId = dbCore.generateUserMetadataID(globalUserId)
|
const userMedataId = dbCore.generateUserMetadataID(globalUserId)
|
||||||
|
|
||||||
it("should be able to map ro_ to global user IDs", () => {
|
it("should be able to map ro_ to global user IDs", () => {
|
||||||
const params: SearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {
|
query: {
|
||||||
equal: {
|
equal: {
|
||||||
|
@ -60,7 +60,7 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle array of user IDs", () => {
|
it("should handle array of user IDs", () => {
|
||||||
const params: SearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {
|
query: {
|
||||||
oneOf: {
|
oneOf: {
|
||||||
|
@ -77,7 +77,7 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
|
|
||||||
it("shouldn't change any other input", () => {
|
it("shouldn't change any other input", () => {
|
||||||
const email = "test@example.com"
|
const email = "test@example.com"
|
||||||
const params: SearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId,
|
tableId,
|
||||||
query: {
|
query: {
|
||||||
equal: {
|
equal: {
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
import {
|
import {
|
||||||
FieldType,
|
FieldType,
|
||||||
SearchParams,
|
|
||||||
Table,
|
Table,
|
||||||
DocumentType,
|
DocumentType,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
FieldSubtype,
|
FieldSubtype,
|
||||||
|
SearchFilters,
|
||||||
|
SearchIndex,
|
||||||
|
SearchResponse,
|
||||||
|
Row,
|
||||||
|
RowSearchParams,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore, context } from "@budibase/backend-core"
|
||||||
import { utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
|
export async function paginatedSearch(
|
||||||
|
query: SearchFilters,
|
||||||
|
params: RowSearchParams
|
||||||
|
): Promise<SearchResponse<Row>> {
|
||||||
|
const appId = context.getAppId()
|
||||||
|
return dbCore.paginatedSearch(appId!, SearchIndex.ROWS, query, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fullSearch(
|
||||||
|
query: SearchFilters,
|
||||||
|
params: RowSearchParams
|
||||||
|
): Promise<{ rows: Row[] }> {
|
||||||
|
const appId = context.getAppId()
|
||||||
|
return dbCore.fullSearch(appId!, SearchIndex.ROWS, query, params)
|
||||||
|
}
|
||||||
|
|
||||||
function findColumnInQueries(
|
function findColumnInQueries(
|
||||||
column: string,
|
column: string,
|
||||||
options: SearchParams,
|
options: RowSearchParams,
|
||||||
callback: (filter: any) => any
|
callback: (filter: any) => any
|
||||||
) {
|
) {
|
||||||
if (!options.query) {
|
if (!options.query) {
|
||||||
|
@ -29,7 +49,7 @@ function findColumnInQueries(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function userColumnMapping(column: string, options: SearchParams) {
|
function userColumnMapping(column: string, options: RowSearchParams) {
|
||||||
findColumnInQueries(column, options, (filterValue: any): any => {
|
findColumnInQueries(column, options, (filterValue: any): any => {
|
||||||
const isArray = Array.isArray(filterValue),
|
const isArray = Array.isArray(filterValue),
|
||||||
isString = typeof filterValue === "string"
|
isString = typeof filterValue === "string"
|
||||||
|
@ -60,7 +80,7 @@ function userColumnMapping(column: string, options: SearchParams) {
|
||||||
|
|
||||||
// maps through the search parameters to check if any of the inputs are invalid
|
// maps through the search parameters to check if any of the inputs are invalid
|
||||||
// based on the table schema, converts them to something that is valid.
|
// based on the table schema, converts them to something that is valid.
|
||||||
export function searchInputMapping(table: Table, options: SearchParams) {
|
export function searchInputMapping(table: Table, options: RowSearchParams) {
|
||||||
if (!table?.schema) {
|
if (!table?.schema) {
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ import {
|
||||||
RelationshipType,
|
RelationshipType,
|
||||||
Row,
|
Row,
|
||||||
Screen,
|
Screen,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
SourceName,
|
SourceName,
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
|
@ -733,7 +733,7 @@ export default class TestConfiguration {
|
||||||
return this.api.row.fetch(tableId)
|
return this.api.row.fetch(tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchRows(tableId: string, searchParams?: SearchParams) {
|
async searchRows(tableId: string, searchParams?: RowSearchParams) {
|
||||||
if (!tableId && this.table) {
|
if (!tableId && this.table) {
|
||||||
tableId = this.table._id!
|
tableId = this.table._id!
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
BulkImportRequest,
|
BulkImportRequest,
|
||||||
BulkImportResponse,
|
BulkImportResponse,
|
||||||
SearchRowResponse,
|
SearchRowResponse,
|
||||||
SearchParams,
|
RowSearchParams,
|
||||||
DeleteRows,
|
DeleteRows,
|
||||||
DeleteRow,
|
DeleteRow,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
@ -135,7 +135,7 @@ export class RowAPI extends TestAPI {
|
||||||
|
|
||||||
search = async (
|
search = async (
|
||||||
sourceId: string,
|
sourceId: string,
|
||||||
params?: SearchParams,
|
params?: RowSearchParams,
|
||||||
expectations?: Expectations
|
expectations?: Expectations
|
||||||
): Promise<SearchRowResponse> => {
|
): Promise<SearchRowResponse> => {
|
||||||
return await this._post<SearchRowResponse>(`/api/${sourceId}/search`, {
|
return await this._post<SearchRowResponse>(`/api/${sourceId}/search`, {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { SearchFilters, SearchParams } from "../../../sdk"
|
import { SearchFilters, RowSearchParams } from "../../../sdk"
|
||||||
import { Row } from "../../../documents"
|
import { Row } from "../../../documents"
|
||||||
import { PaginationResponse, SortOrder } from "../../../api"
|
import { PaginationResponse, SortOrder } from "../../../api"
|
||||||
import { ReadStream } from "fs"
|
import { ReadStream } from "fs"
|
||||||
|
@ -13,7 +13,7 @@ export interface PatchRowRequest extends Row {
|
||||||
|
|
||||||
export interface PatchRowResponse extends Row {}
|
export interface PatchRowResponse extends Row {}
|
||||||
|
|
||||||
export interface SearchRowRequest extends Omit<SearchParams, "tableId"> {}
|
export interface SearchRowRequest extends Omit<RowSearchParams, "tableId"> {}
|
||||||
|
|
||||||
export interface SearchViewRowRequest
|
export interface SearchViewRowRequest
|
||||||
extends Pick<
|
extends Pick<
|
||||||
|
|
|
@ -22,6 +22,6 @@ export interface PaginationRequest extends BasicPaginationRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginationResponse {
|
export interface PaginationResponse {
|
||||||
bookmark: string | undefined
|
bookmark: string | number | undefined
|
||||||
hasNextPage: boolean
|
hasNextPage?: boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { SortOrder, SortType } from "../api"
|
import { SortOrder, SortType } from "../api"
|
||||||
import { SearchFilters } from "./search"
|
import { SearchFilters } from "./search"
|
||||||
import { Row } from "../documents"
|
import { Row } from "../documents"
|
||||||
|
import { WithRequired } from "../shared"
|
||||||
|
|
||||||
export interface SearchParams {
|
export interface SearchParams {
|
||||||
tableId: string
|
tableId?: string
|
||||||
|
query?: SearchFilters
|
||||||
paginate?: boolean
|
paginate?: boolean
|
||||||
query: SearchFilters
|
bookmark?: string | number
|
||||||
bookmark?: string
|
|
||||||
limit?: number
|
limit?: number
|
||||||
sort?: string
|
sort?: string
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
|
@ -14,10 +15,17 @@ export interface SearchParams {
|
||||||
version?: string
|
version?: string
|
||||||
disableEscaping?: boolean
|
disableEscaping?: boolean
|
||||||
fields?: string[]
|
fields?: string[]
|
||||||
|
indexer?: () => Promise<any>
|
||||||
|
rows?: Row[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchResponse {
|
// when searching for rows we want a more extensive search type that requires certain properties
|
||||||
rows: Row[]
|
export interface RowSearchParams
|
||||||
|
extends WithRequired<SearchParams, "tableId" | "query"> {}
|
||||||
|
|
||||||
|
export interface SearchResponse<T> {
|
||||||
|
rows: T[]
|
||||||
hasNextPage?: boolean
|
hasNextPage?: boolean
|
||||||
bookmark?: string | number
|
bookmark?: string | number
|
||||||
|
totalRows?: number
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue