primary and secondary index fields
This commit is contained in:
parent
41bc7dbaf7
commit
e599854b14
|
@ -232,7 +232,7 @@ export const getBackendUiStore = () => {
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
saveField: ({ originalName, field, primaryDisplay = false }) => {
|
saveField: ({ originalName, field, primaryDisplay = false, indexes }) => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
// delete the original if renaming
|
// delete the original if renaming
|
||||||
// need to handle if the column had no name, empty string
|
// need to handle if the column had no name, empty string
|
||||||
|
@ -249,6 +249,10 @@ export const getBackendUiStore = () => {
|
||||||
state.draftTable.primaryDisplay = field.name
|
state.draftTable.primaryDisplay = field.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (indexes) {
|
||||||
|
state.draftTable.indexes = indexes
|
||||||
|
}
|
||||||
|
|
||||||
state.draftTable.schema[field.name] = cloneDeep(field)
|
state.draftTable.schema[field.name] = cloneDeep(field)
|
||||||
store.actions.tables.save(state.draftTable)
|
store.actions.tables.save(state.draftTable)
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input, Button, TextButton, Select, Toggle } from "@budibase/bbui"
|
import {
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
Label,
|
||||||
|
TextButton,
|
||||||
|
Select,
|
||||||
|
Toggle,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
||||||
|
@ -24,6 +31,7 @@
|
||||||
let primaryDisplay =
|
let primaryDisplay =
|
||||||
$backendUiStore.selectedTable.primaryDisplay == null ||
|
$backendUiStore.selectedTable.primaryDisplay == null ||
|
||||||
$backendUiStore.selectedTable.primaryDisplay === field.name
|
$backendUiStore.selectedTable.primaryDisplay === field.name
|
||||||
|
let indexes = [...($backendUiStore.selectedTable.indexes || [])]
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
let deletion
|
let deletion
|
||||||
|
|
||||||
|
@ -41,6 +49,7 @@
|
||||||
originalName,
|
originalName,
|
||||||
field,
|
field,
|
||||||
primaryDisplay,
|
primaryDisplay,
|
||||||
|
indexes,
|
||||||
})
|
})
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
@ -79,8 +88,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeSearchable() {
|
function onChangePrimaryIndex(e) {
|
||||||
|
const enabled = e.target.checked
|
||||||
|
if (enabled) {
|
||||||
|
indexes[0] = field.name
|
||||||
|
} else {
|
||||||
|
indexes.shift()
|
||||||
|
indexes = indexes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeSecondaryIndex(e) {
|
||||||
|
const enabled = e.target.checked
|
||||||
|
if (enabled) {
|
||||||
|
indexes[1] = field.name
|
||||||
|
} else {
|
||||||
|
indexes.pop()
|
||||||
|
indexes = indexes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete() {
|
function confirmDelete() {
|
||||||
|
@ -124,10 +149,20 @@
|
||||||
on:change={onChangePrimaryDisplay}
|
on:change={onChangePrimaryDisplay}
|
||||||
thin
|
thin
|
||||||
text="Use as table display column" />
|
text="Use as table display column" />
|
||||||
|
|
||||||
|
<Label gray small>Search Indexes</Label>
|
||||||
<Toggle
|
<Toggle
|
||||||
bind:checked={field.searchable}
|
checked={indexes[0] === field.name}
|
||||||
|
disabled={indexes[1] === field.name}
|
||||||
|
on:change={onChangePrimaryIndex}
|
||||||
thin
|
thin
|
||||||
text="Index for Search" />
|
text="Primary" />
|
||||||
|
<Toggle
|
||||||
|
checked={indexes[1] === field.name}
|
||||||
|
disabled={!indexes[0] || indexes[0] === field.name}
|
||||||
|
on:change={onChangeSecondaryIndex}
|
||||||
|
thin
|
||||||
|
text="Secondary" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if field.type === 'string'}
|
{#if field.type === 'string'}
|
||||||
|
|
|
@ -229,27 +229,8 @@ exports.fetchView = async function(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createIndex = async function(ctx) {
|
|
||||||
const appId = "app_1987903cf3604d459969c80cf17651a0"
|
|
||||||
const db = new CouchDB(appId)
|
|
||||||
|
|
||||||
const indexes = await db.getIndexes()
|
|
||||||
|
|
||||||
// ctx.body = await db.get("_design/search_ddoc")
|
|
||||||
ctx.body = await db.createIndex({
|
|
||||||
index: {
|
|
||||||
fields: ctx.request.body.fields,
|
|
||||||
name: "other_search_index",
|
|
||||||
ddoc: "search_ddoc",
|
|
||||||
type: "json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// ctx.body = await db.getIndexes()
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.search = async function(ctx) {
|
exports.search = async function(ctx) {
|
||||||
// const appId = ctx.user.appId
|
const appId = ctx.user.appId
|
||||||
const appId = "app_1987903cf3604d459969c80cf17651a0"
|
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
|
|
||||||
|
@ -260,20 +241,10 @@ exports.search = async function(ctx) {
|
||||||
|
|
||||||
query.tableId = ctx.params.tableId
|
query.tableId = ctx.params.tableId
|
||||||
|
|
||||||
// Paginating
|
|
||||||
// if (cursor) {
|
|
||||||
// if (backwards) {
|
|
||||||
// query._id = { $lte: cursor }
|
|
||||||
// } else {
|
|
||||||
// query._id = { $gte: cursor }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const response = await db.find({
|
const response = await db.find({
|
||||||
selector: query,
|
selector: query,
|
||||||
limit: pageSize,
|
limit: pageSize,
|
||||||
skip: pageSize * page,
|
skip: pageSize * page,
|
||||||
// sort: ["_id"],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const rows = response.docs
|
const rows = response.docs
|
||||||
|
@ -285,7 +256,6 @@ exports.search = async function(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctx.body = response
|
|
||||||
ctx.body = await linkRows.attachLinkInfo(appId, rows)
|
ctx.body = await linkRows.attachLinkInfo(appId, rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ const {
|
||||||
generateTableID,
|
generateTableID,
|
||||||
generateRowID,
|
generateRowID,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
|
const { isEqual } = require("lodash/fp")
|
||||||
|
|
||||||
async function checkForColumnUpdates(db, oldTable, updatedTable) {
|
async function checkForColumnUpdates(db, oldTable, updatedTable) {
|
||||||
let updatedRows
|
let updatedRows
|
||||||
|
@ -38,18 +39,6 @@ async function checkForColumnUpdates(db, oldTable, updatedTable) {
|
||||||
return updatedRows
|
return updatedRows
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateSearchIndex(fields) {
|
|
||||||
console.log("Updating stuff")
|
|
||||||
const resp = await db.createIndex({
|
|
||||||
index: {
|
|
||||||
fields,
|
|
||||||
name: "search_index",
|
|
||||||
ddoc: "search_ddoc",
|
|
||||||
type: "json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetch = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const body = await db.allDocs(
|
const body = await db.allDocs(
|
||||||
|
@ -140,6 +129,46 @@ exports.save = async function(ctx) {
|
||||||
const result = await db.post(tableToSave)
|
const result = await db.post(tableToSave)
|
||||||
tableToSave._rev = result.rev
|
tableToSave._rev = result.rev
|
||||||
|
|
||||||
|
// create relevant search indexes
|
||||||
|
if (tableToSave.indexes && tableToSave.indexes.length > 0) {
|
||||||
|
const currentIndexes = await db.getIndexes()
|
||||||
|
const indexName = `search:${result.id}`
|
||||||
|
|
||||||
|
const existingIndex = currentIndexes.indexes.find(
|
||||||
|
existing => existing.name === indexName
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingIndex) {
|
||||||
|
const currentFields = existingIndex.def.fields.map(
|
||||||
|
field => Object.keys(field)[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
// if index fields have changed, delete the original index
|
||||||
|
if (!isEqual(currentFields, tableToSave.indexes)) {
|
||||||
|
await db.deleteIndex(existingIndex)
|
||||||
|
// create/recreate the index with fields
|
||||||
|
await db.createIndex({
|
||||||
|
index: {
|
||||||
|
fields: tableToSave.indexes,
|
||||||
|
name: indexName,
|
||||||
|
ddoc: "search_ddoc",
|
||||||
|
type: "json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// create/recreate the index with fields
|
||||||
|
await db.createIndex({
|
||||||
|
index: {
|
||||||
|
fields: tableToSave.indexes,
|
||||||
|
name: indexName,
|
||||||
|
ddoc: "search_ddoc",
|
||||||
|
type: "json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.eventEmitter &&
|
ctx.eventEmitter &&
|
||||||
ctx.eventEmitter.emitTable(`table:save`, appId, tableToSave)
|
ctx.eventEmitter.emitTable(`table:save`, appId, tableToSave)
|
||||||
|
|
||||||
|
|
|
@ -31,14 +31,9 @@ router
|
||||||
usage,
|
usage,
|
||||||
rowController.save
|
rowController.save
|
||||||
)
|
)
|
||||||
.post(
|
|
||||||
"/api/createindex",
|
|
||||||
// authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
|
||||||
rowController.createIndex
|
|
||||||
)
|
|
||||||
.post(
|
.post(
|
||||||
"/api/:tableId/rows/search",
|
"/api/:tableId/rows/search",
|
||||||
// authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
rowController.search
|
rowController.search
|
||||||
)
|
)
|
||||||
.patch(
|
.patch(
|
||||||
|
|
|
@ -14,7 +14,15 @@ const selfhost = require("./selfhost")
|
||||||
const app = new Koa()
|
const app = new Koa()
|
||||||
|
|
||||||
// set up top level koa middleware
|
// set up top level koa middleware
|
||||||
app.use(koaBody({ multipart: true }))
|
app.use(
|
||||||
|
koaBody({
|
||||||
|
multipart: true,
|
||||||
|
formLimit: "10mb",
|
||||||
|
jsonLimit: "10mb",
|
||||||
|
textLimit: "10mb",
|
||||||
|
enableTypes: ["json", "form", "text"],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
logger({
|
logger({
|
||||||
|
|
|
@ -41,9 +41,7 @@
|
||||||
<div class="root" use:styleable={$component.styles}>
|
<div class="root" use:styleable={$component.styles}>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{#if logo}
|
{#if logo}
|
||||||
<div class="logo-container">
|
<div class="logo-container"><img src={logo} alt="logo" /></div>
|
||||||
<img src={logo} alt="logo" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if title}
|
{#if title}
|
||||||
|
|
|
@ -28,8 +28,13 @@
|
||||||
let page = 0
|
let page = 0
|
||||||
|
|
||||||
$: fetchData(table, page)
|
$: fetchData(table, page)
|
||||||
|
$: searchable = [...(table.indexes || []), ...columns]
|
||||||
// omit empty strings
|
// omit empty strings
|
||||||
$: parsedSearch = Object.keys(search).reduce((acc, next) => search[next] === "" ? acc : { ...acc, [next]: search[next] }, {})
|
$: parsedSearch = Object.keys(search).reduce(
|
||||||
|
(acc, next) =>
|
||||||
|
search[next] === "" ? acc : { ...acc, [next]: search[next] },
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
async function fetchData(table, page) {
|
async function fetchData(table, page) {
|
||||||
if (!isEmpty(table)) {
|
if (!isEmpty(table)) {
|
||||||
|
@ -40,8 +45,8 @@
|
||||||
search: parsedSearch,
|
search: parsedSearch,
|
||||||
pagination: {
|
pagination: {
|
||||||
pageSize,
|
pageSize,
|
||||||
page
|
page,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
loaded = true
|
loaded = true
|
||||||
|
|
Loading…
Reference in New Issue