Allow view searching and sorting. Refactor grid logic to fix multiple issuies
This commit is contained in:
parent
b5546f8d9b
commit
0566644508
|
@ -4615,14 +4615,15 @@
|
||||||
"type": "field/sortable",
|
"type": "field/sortable",
|
||||||
"label": "Sort by",
|
"label": "Sort by",
|
||||||
"key": "sortColumn",
|
"key": "sortColumn",
|
||||||
"placeholder": "None"
|
"placeholder": "Default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"label": "Sort order",
|
"label": "Sort order",
|
||||||
"key": "sortOrder",
|
"key": "sortOrder",
|
||||||
"options": ["Ascending", "Descending"],
|
"options": ["Ascending", "Descending"],
|
||||||
"defaultValue": "Ascending"
|
"defaultValue": "Ascending",
|
||||||
|
"dependsOn": "sortColumn"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
|
|
@ -31,6 +31,7 @@ export const buildViewV2Endpoints = API => ({
|
||||||
/**
|
/**
|
||||||
* Fetches all rows in a view
|
* Fetches all rows in a view
|
||||||
* @param viewId the id of the view
|
* @param viewId the id of the view
|
||||||
|
* @param query the search query
|
||||||
* @param paginate whether to paginate or not
|
* @param paginate whether to paginate or not
|
||||||
* @param limit page size
|
* @param limit page size
|
||||||
* @param bookmark pagination cursor
|
* @param bookmark pagination cursor
|
||||||
|
@ -40,6 +41,7 @@ export const buildViewV2Endpoints = API => ({
|
||||||
*/
|
*/
|
||||||
fetch: async ({
|
fetch: async ({
|
||||||
viewId,
|
viewId,
|
||||||
|
query,
|
||||||
paginate,
|
paginate,
|
||||||
limit,
|
limit,
|
||||||
bookmark,
|
bookmark,
|
||||||
|
@ -50,6 +52,7 @@ export const buildViewV2Endpoints = API => ({
|
||||||
return await API.post({
|
return await API.post({
|
||||||
url: `/api/v2/views/${viewId}/search`,
|
url: `/api/v2/views/${viewId}/search`,
|
||||||
body: {
|
body: {
|
||||||
|
query,
|
||||||
paginate,
|
paginate,
|
||||||
limit,
|
limit,
|
||||||
bookmark,
|
bookmark,
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
let anchor
|
let anchor
|
||||||
|
|
||||||
$: columnOptions = getColumnOptions($stickyColumn, $columns)
|
$: columnOptions = getColumnOptions($stickyColumn, $columns)
|
||||||
$: checkValidSortColumn($sort.column, $stickyColumn, $columns)
|
|
||||||
$: orderOptions = getOrderOptions($sort.column, columnOptions)
|
$: orderOptions = getOrderOptions($sort.column, columnOptions)
|
||||||
|
|
||||||
const getColumnOptions = (stickyColumn, columns) => {
|
const getColumnOptions = (stickyColumn, columns) => {
|
||||||
|
@ -46,8 +45,8 @@
|
||||||
|
|
||||||
const updateSortColumn = e => {
|
const updateSortColumn = e => {
|
||||||
sort.update(state => ({
|
sort.update(state => ({
|
||||||
...state,
|
|
||||||
column: e.detail,
|
column: e.detail,
|
||||||
|
order: e.detail ? state.order : "ascending",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,29 +56,6 @@
|
||||||
order: e.detail,
|
order: e.detail,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we never have a sort column selected that is not visible
|
|
||||||
const checkValidSortColumn = (sortColumn, stickyColumn, columns) => {
|
|
||||||
if (!sortColumn) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
sortColumn !== stickyColumn?.name &&
|
|
||||||
!columns.some(col => col.name === sortColumn)
|
|
||||||
) {
|
|
||||||
if (stickyColumn) {
|
|
||||||
sort.update(state => ({
|
|
||||||
...state,
|
|
||||||
column: stickyColumn.name,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
sort.update(state => ({
|
|
||||||
...state,
|
|
||||||
column: columns[0]?.name,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
<div bind:this={anchor}>
|
||||||
|
@ -98,21 +74,23 @@
|
||||||
<Popover bind:open {anchor} align="left">
|
<Popover bind:open {anchor} align="left">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Select
|
<Select
|
||||||
placeholder={null}
|
placeholder="Default"
|
||||||
value={$sort.column}
|
value={$sort.column}
|
||||||
options={columnOptions}
|
options={columnOptions}
|
||||||
autoWidth
|
autoWidth
|
||||||
on:change={updateSortColumn}
|
on:change={updateSortColumn}
|
||||||
label="Column"
|
label="Column"
|
||||||
/>
|
/>
|
||||||
|
{#if $sort.column}
|
||||||
<Select
|
<Select
|
||||||
placeholder={null}
|
placeholder={null}
|
||||||
value={$sort.order}
|
value={$sort.order || "ascending"}
|
||||||
options={orderOptions}
|
options={orderOptions}
|
||||||
autoWidth
|
autoWidth
|
||||||
on:change={updateSortOrder}
|
on:change={updateSortOrder}
|
||||||
label="Order"
|
label="Order"
|
||||||
/>
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { derived } from "svelte/store"
|
import { derived, get } from "svelte/store"
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = context => {
|
||||||
const { scrolledRowCount, rows, visualRowCapacity } = context
|
const { scrolledRowCount, rows, visualRowCapacity } = context
|
||||||
|
@ -11,13 +11,12 @@ export const initialise = context => {
|
||||||
[scrolledRowCount, rowCount, visualRowCapacity],
|
[scrolledRowCount, rowCount, visualRowCapacity],
|
||||||
([$scrolledRowCount, $rowCount, $visualRowCapacity]) => {
|
([$scrolledRowCount, $rowCount, $visualRowCapacity]) => {
|
||||||
return Math.max(0, $rowCount - $scrolledRowCount - $visualRowCapacity)
|
return Math.max(0, $rowCount - $scrolledRowCount - $visualRowCapacity)
|
||||||
},
|
}
|
||||||
100
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fetch next page when fewer than 25 remaining rows to scroll
|
// Fetch next page when fewer than 25 remaining rows to scroll
|
||||||
remainingRows.subscribe(remaining => {
|
remainingRows.subscribe(remaining => {
|
||||||
if (remaining < 25) {
|
if (remaining < 25 && get(rowCount)) {
|
||||||
rows.actions.loadNextPage()
|
rows.actions.loadNextPage()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { get } from "svelte/store"
|
import { derived, get } from "svelte/store"
|
||||||
import { memo } from "../../../utils"
|
import { memo } from "../../../utils"
|
||||||
|
|
||||||
export const createStores = context => {
|
export const createStores = context => {
|
||||||
|
@ -17,13 +17,34 @@ export const createStores = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = context => {
|
||||||
const { sort, initialSortColumn, initialSortOrder } = context
|
const { sort, initialSortColumn, initialSortOrder, definition } = context
|
||||||
|
|
||||||
// Reset sort when initial sort props change
|
// Reset sort when initial sort props change
|
||||||
initialSortColumn.subscribe(newSortColumn => {
|
initialSortColumn.subscribe(newSortColumn => {
|
||||||
sort.update(state => ({ ...state, column: newSortColumn }))
|
sort.update(state => ({ ...state, column: newSortColumn }))
|
||||||
})
|
})
|
||||||
initialSortOrder.subscribe(newSortOrder => {
|
initialSortOrder.subscribe(newSortOrder => {
|
||||||
sort.update(state => ({ ...state, order: newSortOrder }))
|
sort.update(state => ({ ...state, order: newSortOrder || "ascending" }))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Derive if the current sort column exists in the schema
|
||||||
|
const sortColumnExists = derived(
|
||||||
|
[sort, definition],
|
||||||
|
([$sort, $definition]) => {
|
||||||
|
if (!$sort?.column) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return $definition?.schema?.[$sort.column] != null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clear sort state if our sort column does not exist
|
||||||
|
sortColumnExists.subscribe(exists => {
|
||||||
|
if (!exists) {
|
||||||
|
sort.set({
|
||||||
|
column: null,
|
||||||
|
order: "ascending",
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,51 +59,55 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = context => {
|
||||||
const { datasource, fetch, filter, sort, definition } = context
|
const { datasource, fetch, filter, sort, table } = context
|
||||||
|
|
||||||
// Wipe filter whenever table ID changes to avoid using stale filters
|
// Keep a list of subscriptions so that we can clear them when the datasource
|
||||||
|
// config changes
|
||||||
|
let unsubscribers = []
|
||||||
|
|
||||||
|
// Observe datasource changes and apply logic for table datasources
|
||||||
datasource.subscribe($datasource => {
|
datasource.subscribe($datasource => {
|
||||||
if ($datasource?.type !== "table") {
|
// Clear previous subscriptions
|
||||||
|
unsubscribers?.forEach(unsubscribe => unsubscribe())
|
||||||
|
unsubscribers = []
|
||||||
|
if (!table.actions.isDatasourceValid($datasource)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wipe state
|
||||||
filter.set([])
|
filter.set([])
|
||||||
|
sort.set({
|
||||||
|
column: null,
|
||||||
|
order: "ascending",
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update fetch when filter changes
|
// Update fetch when filter changes
|
||||||
|
unsubscribers.push(
|
||||||
filter.subscribe($filter => {
|
filter.subscribe($filter => {
|
||||||
if (get(datasource)?.type !== "table") {
|
// Ensure we're updating the correct fetch
|
||||||
|
const $fetch = get(fetch)
|
||||||
|
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
get(fetch)?.update({
|
$fetch.update({
|
||||||
filter: $filter,
|
filter: $filter,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// Update fetch when sorting changes
|
// Update fetch when sorting changes
|
||||||
|
unsubscribers.push(
|
||||||
sort.subscribe($sort => {
|
sort.subscribe($sort => {
|
||||||
if (get(datasource)?.type !== "table") {
|
// Ensure we're updating the correct fetch
|
||||||
|
const $fetch = get(fetch)
|
||||||
|
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
get(fetch)?.update({
|
$fetch.update({
|
||||||
sortOrder: $sort.order,
|
sortOrder: $sort.order || "ascending",
|
||||||
sortColumn: $sort.column,
|
sortColumn: $sort.column,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
)
|
||||||
// Ensure sorting UI reflects the fetch state whenever we reset the fetch,
|
|
||||||
// which triggers a new definition
|
|
||||||
definition.subscribe(() => {
|
|
||||||
if (get(datasource)?.type !== "table") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const $fetch = get(fetch)
|
|
||||||
if (!$fetch) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const { sortColumn, sortOrder } = get($fetch)
|
|
||||||
sort.set({
|
|
||||||
column: sortColumn,
|
|
||||||
order: sortOrder,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,24 +69,49 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = context => {
|
||||||
const { definition, datasource, sort, rows, filter, subscribe } = context
|
const { definition, datasource, sort, rows, filter, subscribe, viewV2 } =
|
||||||
|
context
|
||||||
|
|
||||||
|
// Keep a list of subscriptions so that we can clear them when the datasource
|
||||||
|
// config changes
|
||||||
|
let unsubscribers = []
|
||||||
|
|
||||||
|
// Observe datasource changes and apply logic for view V2 datasources
|
||||||
|
datasource.subscribe($datasource => {
|
||||||
|
// Clear previous subscriptions
|
||||||
|
unsubscribers?.forEach(unsubscribe => unsubscribe())
|
||||||
|
unsubscribers = []
|
||||||
|
if (!viewV2.actions.isDatasourceValid($datasource)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset state for new view
|
||||||
|
filter.set([])
|
||||||
|
sort.set({
|
||||||
|
column: null,
|
||||||
|
order: "ascending",
|
||||||
|
})
|
||||||
|
|
||||||
// Keep sort and filter state in line with the view definition
|
// Keep sort and filter state in line with the view definition
|
||||||
|
unsubscribers.push(
|
||||||
definition.subscribe($definition => {
|
definition.subscribe($definition => {
|
||||||
if (!$definition || get(datasource)?.type !== "viewV2") {
|
if ($definition?.id !== $datasource.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sort.set({
|
sort.set({
|
||||||
column: $definition.sort?.field,
|
column: $definition.sort?.field,
|
||||||
order: $definition.sort?.order,
|
order: $definition.sort?.order || "ascending",
|
||||||
})
|
})
|
||||||
filter.set($definition.query || [])
|
filter.set($definition.query || [])
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// When sorting changes, ensure view definition is kept up to date
|
// When sorting changes, ensure view definition is kept up to date
|
||||||
|
unsubscribers.push(
|
||||||
sort.subscribe(async $sort => {
|
sort.subscribe(async $sort => {
|
||||||
|
// Ensure we're updating the correct view
|
||||||
const $view = get(definition)
|
const $view = get(definition)
|
||||||
if (!$view || get(datasource)?.type !== "viewV2") {
|
if ($view?.id !== $datasource.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
@ -97,17 +122,20 @@ export const initialise = context => {
|
||||||
...$view,
|
...$view,
|
||||||
sort: {
|
sort: {
|
||||||
field: $sort.column,
|
field: $sort.column,
|
||||||
order: $sort.order,
|
order: $sort.order || "ascending",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await rows.actions.refreshData()
|
await rows.actions.refreshData()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// When filters change, ensure view definition is kept up to date
|
// When filters change, ensure view definition is kept up to date
|
||||||
|
unsubscribers?.push(
|
||||||
filter.subscribe(async $filter => {
|
filter.subscribe(async $filter => {
|
||||||
|
// Ensure we're updating the correct view
|
||||||
const $view = get(definition)
|
const $view = get(definition)
|
||||||
if (!$view || get(datasource)?.type !== "viewV2") {
|
if ($view?.id !== $datasource.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (JSON.stringify($filter) !== JSON.stringify($view.query)) {
|
if (JSON.stringify($filter) !== JSON.stringify($view.query)) {
|
||||||
|
@ -118,13 +146,14 @@ export const initialise = context => {
|
||||||
await rows.actions.refreshData()
|
await rows.actions.refreshData()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// When hidden we show columns, we need to refresh data in order to fetch
|
// When hidden we show columns, we need to refresh data in order to fetch
|
||||||
// values for those columns
|
// values for those columns
|
||||||
|
unsubscribers.push(
|
||||||
subscribe("show-column", async () => {
|
subscribe("show-column", async () => {
|
||||||
if (get(datasource)?.type !== "viewV2") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await rows.actions.refreshData()
|
await rows.actions.refreshData()
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,17 @@ export default class DataFetch {
|
||||||
return this.derivedStore.subscribe
|
return this.derivedStore.subscribe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the default sort column for this datasource
|
||||||
|
*/
|
||||||
|
getDefaultSortColumn(definition, schema) {
|
||||||
|
if (definition?.primaryDisplay && schema[definition.primaryDisplay]) {
|
||||||
|
return definition.primaryDisplay
|
||||||
|
} else {
|
||||||
|
return Object.keys(schema)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a fresh set of data from the server, resetting pagination
|
* Fetches a fresh set of data from the server, resetting pagination
|
||||||
*/
|
*/
|
||||||
|
@ -118,12 +129,6 @@ export default class DataFetch {
|
||||||
|
|
||||||
// Fetch datasource definition and extract sort properties if configured
|
// Fetch datasource definition and extract sort properties if configured
|
||||||
const definition = await this.getDefinition(datasource)
|
const definition = await this.getDefinition(datasource)
|
||||||
if (definition?.sort?.field) {
|
|
||||||
this.options.sortColumn = definition.sort.field
|
|
||||||
}
|
|
||||||
if (definition?.sort?.order) {
|
|
||||||
this.options.sortOrder = definition.sort.order
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine feature flags
|
// Determine feature flags
|
||||||
const features = this.determineFeatureFlags(definition)
|
const features = this.determineFeatureFlags(definition)
|
||||||
|
@ -140,32 +145,32 @@ export default class DataFetch {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no sort order, default to descending
|
// If an invalid sort column is specified, delete it
|
||||||
|
if (this.options.sortColumn && !schema[this.options.sortColumn]) {
|
||||||
|
this.options.sortColumn = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no sort column, get the default column for this datasource
|
||||||
|
if (!this.options.sortColumn) {
|
||||||
|
this.options.sortColumn = this.getDefaultSortColumn(definition, schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a sort column specified then just ensure we don't set
|
||||||
|
// any sorting params
|
||||||
|
if (!this.options.sortColumn) {
|
||||||
|
this.options.sortOrder = "ascending"
|
||||||
|
this.options.sortType = null
|
||||||
|
} else {
|
||||||
|
// Otherwise determine what sort type to use base on sort column
|
||||||
|
const type = schema?.[this.options.sortColumn]?.type
|
||||||
|
this.options.sortType =
|
||||||
|
type === "number" || type === "bigint" ? "number" : "string"
|
||||||
|
|
||||||
|
// If no sort order, default to ascending
|
||||||
if (!this.options.sortOrder) {
|
if (!this.options.sortOrder) {
|
||||||
this.options.sortOrder = "ascending"
|
this.options.sortOrder = "ascending"
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no sort column, or an invalid sort column is provided, use the primary
|
|
||||||
// display and fallback to first column
|
|
||||||
const sortValid = this.options.sortColumn && schema[this.options.sortColumn]
|
|
||||||
if (!sortValid) {
|
|
||||||
let newSortColumn
|
|
||||||
if (definition?.primaryDisplay && schema[definition.primaryDisplay]) {
|
|
||||||
newSortColumn = definition.primaryDisplay
|
|
||||||
} else {
|
|
||||||
newSortColumn = Object.keys(schema)[0]
|
|
||||||
}
|
}
|
||||||
this.options.sortColumn = newSortColumn
|
|
||||||
}
|
|
||||||
const { sortOrder, sortColumn } = this.options
|
|
||||||
|
|
||||||
// Determine what sort type to use
|
|
||||||
let sortType = "string"
|
|
||||||
if (sortColumn) {
|
|
||||||
const type = schema?.[sortColumn]?.type
|
|
||||||
sortType = type === "number" || type === "bigint" ? "number" : "string"
|
|
||||||
}
|
|
||||||
this.options.sortType = sortType
|
|
||||||
|
|
||||||
// Build the lucene query
|
// Build the lucene query
|
||||||
let query = this.options.query
|
let query = this.options.query
|
||||||
|
@ -182,8 +187,6 @@ export default class DataFetch {
|
||||||
loading: true,
|
loading: true,
|
||||||
cursors: [],
|
cursors: [],
|
||||||
cursor: null,
|
cursor: null,
|
||||||
sortOrder,
|
|
||||||
sortColumn,
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Actually fetch data
|
// Actually fetch data
|
||||||
|
@ -351,6 +354,14 @@ export default class DataFetch {
|
||||||
const entries = Object.entries(newOptions || {})
|
const entries = Object.entries(newOptions || {})
|
||||||
for (let [key, value] of entries) {
|
for (let [key, value] of entries) {
|
||||||
if (JSON.stringify(value) !== JSON.stringify(this.options[key])) {
|
if (JSON.stringify(value) !== JSON.stringify(this.options[key])) {
|
||||||
|
console.log(
|
||||||
|
key,
|
||||||
|
"is different",
|
||||||
|
"new",
|
||||||
|
value,
|
||||||
|
"vs old",
|
||||||
|
this.options[key]
|
||||||
|
)
|
||||||
refresh = true
|
refresh = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,10 @@ export default class QueryFetch extends DataFetch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaultSortColumn() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
const { datasource, limit, paginate } = this.options
|
const { datasource, limit, paginate } = this.options
|
||||||
const { supportsPagination } = this.features
|
const { supportsPagination } = this.features
|
||||||
|
|
|
@ -4,9 +4,6 @@ import { get } from "svelte/store"
|
||||||
export default class ViewV2Fetch extends DataFetch {
|
export default class ViewV2Fetch extends DataFetch {
|
||||||
determineFeatureFlags() {
|
determineFeatureFlags() {
|
||||||
return {
|
return {
|
||||||
// The API does not actually support dynamic filtering, but since views
|
|
||||||
// have filters built in we don't want to perform client side filtering
|
|
||||||
// which would happen if we marked this as false
|
|
||||||
supportsSearch: true,
|
supportsSearch: true,
|
||||||
supportsSort: true,
|
supportsSort: true,
|
||||||
supportsPagination: true,
|
supportsPagination: true,
|
||||||
|
@ -33,18 +30,23 @@ export default class ViewV2Fetch extends DataFetch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaultSortColumn() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
|
const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
|
||||||
this.options
|
this.options
|
||||||
const { cursor } = get(this.store)
|
const { cursor, query } = get(this.store)
|
||||||
try {
|
try {
|
||||||
const res = await this.API.viewV2.fetch({
|
const res = await this.API.viewV2.fetch({
|
||||||
viewId: datasource.id,
|
viewId: datasource.id,
|
||||||
|
query,
|
||||||
paginate,
|
paginate,
|
||||||
limit,
|
limit,
|
||||||
bookmark: cursor,
|
bookmark: cursor,
|
||||||
sort: sortColumn,
|
sort: sortColumn,
|
||||||
sortOrder,
|
sortOrder: sortOrder?.toLowerCase(),
|
||||||
sortType,
|
sortType,
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -6,9 +6,11 @@ import {
|
||||||
SearchViewRowRequest,
|
SearchViewRowRequest,
|
||||||
RequiredKeys,
|
RequiredKeys,
|
||||||
SearchParams,
|
SearchParams,
|
||||||
|
SearchFilters,
|
||||||
} 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"
|
||||||
|
import { db } from "@budibase/backend-core"
|
||||||
|
|
||||||
export async function searchView(
|
export async function searchView(
|
||||||
ctx: UserCtx<SearchViewRowRequest, SearchRowResponse>
|
ctx: UserCtx<SearchViewRowRequest, SearchRowResponse>
|
||||||
|
@ -19,15 +21,37 @@ export async function searchView(
|
||||||
if (!view) {
|
if (!view) {
|
||||||
ctx.throw(404, `View ${viewId} not found`)
|
ctx.throw(404, `View ${viewId} not found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view.version !== 2) {
|
if (view.version !== 2) {
|
||||||
ctx.throw(400, `This method only supports viewsV2`)
|
ctx.throw(400, `This method only supports viewsV2`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewFields = Object.keys(view.schema || {})
|
const viewFields = Object.keys(view.schema || {})
|
||||||
|
|
||||||
const { body } = ctx.request
|
const { body } = ctx.request
|
||||||
const query = dataFilters.buildLuceneQuery(view.query || [])
|
|
||||||
|
// Enrich saved query with ephemeral query params.
|
||||||
|
// We prevent searching on any fields that are saved as part of the query, as
|
||||||
|
// that could let users find rows they should not be allowed to access.
|
||||||
|
let query = dataFilters.buildLuceneQuery(view.query || [])
|
||||||
|
if (body.query) {
|
||||||
|
// Extract existing fields
|
||||||
|
const existingFields =
|
||||||
|
view.query
|
||||||
|
?.filter(filter => filter.field)
|
||||||
|
.map(filter => db.removeKeyNumbering(filter.field)) || []
|
||||||
|
|
||||||
|
// Prevent using an "OR" search
|
||||||
|
delete body.query.allOr
|
||||||
|
|
||||||
|
// Carry over filters for unused fields
|
||||||
|
Object.keys(body.query).forEach(key => {
|
||||||
|
const operator = key as keyof Omit<SearchFilters, "allOr">
|
||||||
|
Object.keys(body.query[operator] || {}).forEach(field => {
|
||||||
|
if (!existingFields.includes(db.removeKeyNumbering(field))) {
|
||||||
|
query[operator]![field] = body.query[operator]![field]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const searchOptions: RequiredKeys<SearchViewRowRequest> &
|
const searchOptions: RequiredKeys<SearchViewRowRequest> &
|
||||||
RequiredKeys<Pick<SearchParams, "tableId" | "query" | "fields">> = {
|
RequiredKeys<Pick<SearchParams, "tableId" | "query" | "fields">> = {
|
||||||
|
|
|
@ -16,7 +16,13 @@ export interface SearchRowRequest extends Omit<SearchParams, "tableId"> {}
|
||||||
export interface SearchViewRowRequest
|
export interface SearchViewRowRequest
|
||||||
extends Pick<
|
extends Pick<
|
||||||
SearchRowRequest,
|
SearchRowRequest,
|
||||||
"sort" | "sortOrder" | "sortType" | "limit" | "bookmark" | "paginate"
|
| "sort"
|
||||||
|
| "sortOrder"
|
||||||
|
| "sortType"
|
||||||
|
| "limit"
|
||||||
|
| "bookmark"
|
||||||
|
| "paginate"
|
||||||
|
| "query"
|
||||||
> {}
|
> {}
|
||||||
|
|
||||||
export interface SearchRowResponse {
|
export interface SearchRowResponse {
|
||||||
|
|
Loading…
Reference in New Issue