internal search working
This commit is contained in:
parent
eea86fca83
commit
27c7f5697b
|
@ -79,6 +79,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
function onChangeSearchable() {
|
||||
|
||||
}
|
||||
|
||||
function confirmDelete() {
|
||||
confirmDeleteDialog.show()
|
||||
deletion = true
|
||||
|
@ -120,6 +124,10 @@
|
|||
on:change={onChangePrimaryDisplay}
|
||||
thin
|
||||
text="Use as table display column" />
|
||||
<Toggle
|
||||
bind:checked={field.searchable}
|
||||
thin
|
||||
text="Searchable" />
|
||||
{/if}
|
||||
|
||||
{#if field.type === 'string'}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
.icon {
|
||||
right: 2px;
|
||||
top: 26px;
|
||||
top: 5px;
|
||||
bottom: 2px;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"datagrid",
|
||||
"list",
|
||||
"button",
|
||||
"search",
|
||||
{
|
||||
"name": "Form",
|
||||
"icon": "ri-file-edit-line",
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
const dispatch = createEventDispatcher()
|
||||
let anchorRight, dropdownRight
|
||||
let drawer
|
||||
let tableDrawer
|
||||
|
||||
export let value = {}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { cloneDeep } from "lodash/fp"
|
||||
import { fetchTableData } from "./tables"
|
||||
import { fetchTableData, searchTableData } from "./tables"
|
||||
import { fetchViewData } from "./views"
|
||||
import { fetchRelationshipData } from "./relationships"
|
||||
import { executeQuery } from "./queries"
|
||||
|
@ -14,10 +14,15 @@ export const fetchDatasource = async datasource => {
|
|||
}
|
||||
|
||||
// Fetch all rows in data source
|
||||
const { type, tableId, fieldName } = datasource
|
||||
const { type, tableId, fieldName, search } = datasource
|
||||
let rows = []
|
||||
if (type === "table") {
|
||||
rows = await fetchTableData(tableId)
|
||||
// TODO refactor
|
||||
if (search) {
|
||||
rows = await searchTableData(tableId, search)
|
||||
} else {
|
||||
rows = await fetchTableData(tableId)
|
||||
}
|
||||
} else if (type === "view") {
|
||||
rows = await fetchViewData(datasource)
|
||||
} else if (type === "query") {
|
||||
|
|
|
@ -16,3 +16,18 @@ export const fetchTableData = async tableId => {
|
|||
const rows = await API.get({ url: `/api/${tableId}/rows` })
|
||||
return await enrichRows(rows, tableId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a mango query against an internal table
|
||||
* @param {String} tableId - id of the table to search
|
||||
* @param {Object} search - Mango Compliant search object
|
||||
*/
|
||||
export const searchTableData = async (tableId, search) => {
|
||||
const rows = await API.post({
|
||||
url: `/api/${tableId}/rows/search`,
|
||||
body: {
|
||||
query: search,
|
||||
},
|
||||
})
|
||||
return await enrichRows(rows, tableId)
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
"pino-pretty": "^4.0.0",
|
||||
"pouchdb": "^7.2.1",
|
||||
"pouchdb-all-dbs": "^1.0.2",
|
||||
"pouchdb-find": "^7.2.2",
|
||||
"pouchdb-replication-stream": "^1.2.9",
|
||||
"sanitize-s3-objectkey": "^0.0.1",
|
||||
"server-destroy": "^1.0.1",
|
||||
|
|
|
@ -215,6 +215,30 @@ exports.fetchView = async function(ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
exports.search = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
|
||||
// special case for users, fetch through the user controller
|
||||
// let rows
|
||||
// SHOULD WE PREVENT SEARCHING FOR USERS?
|
||||
// if (ctx.params.tableId === ViewNames.USERS) {
|
||||
// await usersController.fetch(ctx)
|
||||
// rows = ctx.body
|
||||
// } else {
|
||||
|
||||
const db = new CouchDB(appId)
|
||||
|
||||
const query = ctx.request.body.query
|
||||
query.tableId = ctx.params.tableId
|
||||
|
||||
const response = await db.find({
|
||||
selector: query,
|
||||
})
|
||||
ctx.body = response.docs
|
||||
// const rows = response.docs.map(row => row.doc)
|
||||
// ctx.body = await linkRows.attachLinkInfo(appId, rows)
|
||||
}
|
||||
|
||||
exports.fetchTableRows = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
|
||||
|
@ -225,6 +249,7 @@ exports.fetchTableRows = async function(ctx) {
|
|||
rows = ctx.body
|
||||
} else {
|
||||
const db = new CouchDB(appId)
|
||||
|
||||
const response = await db.allDocs(
|
||||
getRowParams(ctx.params.tableId, null, {
|
||||
include_docs: true,
|
||||
|
|
|
@ -31,6 +31,11 @@ router
|
|||
usage,
|
||||
rowController.save
|
||||
)
|
||||
.post(
|
||||
"/api/:tableId/rows/search",
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||
rowController.search
|
||||
)
|
||||
.patch(
|
||||
"/api/:tableId/rows/:id",
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||
|
|
|
@ -2,12 +2,14 @@ const PouchDB = require("pouchdb")
|
|||
const replicationStream = require("pouchdb-replication-stream")
|
||||
const allDbs = require("pouchdb-all-dbs")
|
||||
const { budibaseAppsDir } = require("../utilities/budibaseDir")
|
||||
const find = require("pouchdb-find")
|
||||
const env = require("../environment")
|
||||
|
||||
const COUCH_DB_URL = env.COUCH_DB_URL || `leveldb://${budibaseAppsDir()}/.data/`
|
||||
const isInMemory = env.NODE_ENV === "jest"
|
||||
|
||||
PouchDB.plugin(replicationStream.plugin)
|
||||
PouchDB.plugin(find)
|
||||
PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
|
||||
|
||||
let POUCH_DB_DEFAULTS = {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -114,6 +114,21 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"search": {
|
||||
"name": "Search",
|
||||
"description": "A searchable list of items.",
|
||||
"icon": "ri-search-line",
|
||||
"styleable": true,
|
||||
"hasChildren": true,
|
||||
"dataProvider": true,
|
||||
"settings": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"label": "Data",
|
||||
"key": "datasource"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataform": {
|
||||
"name": "Form",
|
||||
"icon": "ri-file-edit-line",
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { isEmpty } from "lodash/fp"
|
||||
import { Button, Label, Select, Toggle, Input } from "@budibase/bbui"
|
||||
|
||||
const { API, styleable, DataProvider, builderStore } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
export let datasource = []
|
||||
|
||||
let rows = []
|
||||
let loaded = false
|
||||
let table
|
||||
let searchableFields = []
|
||||
let search = {}
|
||||
|
||||
$: schema = table?.schema || {}
|
||||
$: searchableFields = Object.keys(schema).filter(
|
||||
key => schema[key].searchable
|
||||
)
|
||||
|
||||
$: console.log(search)
|
||||
|
||||
$: fetchData(datasource)
|
||||
|
||||
async function fetchData(datasource) {
|
||||
if (!isEmpty(datasource)) {
|
||||
table = await API.fetchTableDefinition(datasource.tableId)
|
||||
rows = await API.fetchDatasource({
|
||||
...datasource,
|
||||
search
|
||||
})
|
||||
}
|
||||
loaded = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<div use:styleable={$component.styles}>
|
||||
<div class="query-builder">
|
||||
{#each searchableFields as field}
|
||||
<div class="form-field">
|
||||
<Label extraSmall grey>{schema[field].name}</Label>
|
||||
{#if schema[field].type === 'options'}
|
||||
<Select secondary bind:value={search[field]}>
|
||||
<option value="">Choose an option</option>
|
||||
{#each schema[field].constraints.inclusion as opt}
|
||||
<option>{opt}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
<!-- {:else if schema[field].type === 'datetime'}
|
||||
<DatePicker bind:value={search[field]} /> --->
|
||||
{:else if schema[field].type === 'boolean'}
|
||||
<Toggle
|
||||
text={schema[field].name}
|
||||
bind:checked={search[field]} />
|
||||
{:else if schema[field].type === 'number'}
|
||||
<Input type="number" bind:value={search[field]} />
|
||||
{:else if schema[field].type === 'string'}
|
||||
<Input bind:value={search[field]} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
<Button blue on:click={() => fetchData(datasource)}>Search</Button>
|
||||
<Button red on:click={() => {
|
||||
search = {}
|
||||
fetchData(datasource)
|
||||
}}>Reset</Button>
|
||||
</div>
|
||||
{#if rows.length > 0}
|
||||
{#if $component.children === 0 && $builderStore.inBuilder}
|
||||
<p>Add some components too</p>
|
||||
{:else}
|
||||
{#each rows as row}
|
||||
<DataProvider {row}>
|
||||
<slot />
|
||||
</DataProvider>
|
||||
{/each}
|
||||
{/if}
|
||||
{:else if loaded && $builderStore.inBuilder}
|
||||
<p>Feed me some data</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
p {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #f5f5f5;
|
||||
border: #ccc 1px solid;
|
||||
padding: var(--spacing-m);
|
||||
}
|
||||
|
||||
.query-builder {
|
||||
padding: var(--spacing-m);
|
||||
background: var(--background);
|
||||
border-radius: var(--border-radius-s);
|
||||
}
|
||||
|
||||
.form-field {
|
||||
margin-bottom: var(--spacing-m);
|
||||
}
|
||||
</style>
|
|
@ -25,4 +25,5 @@ export { default as cardhorizontal } from "./CardHorizontal.svelte"
|
|||
export { default as cardstat } from "./CardStat.svelte"
|
||||
export { default as newrow } from "./NewRow.svelte"
|
||||
export { default as icon } from "./Icon.svelte"
|
||||
export { default as search } from "./Search.svelte"
|
||||
export * from "./charts"
|
||||
|
|
Loading…
Reference in New Issue