budibase/packages/server/src/api/controllers/search.js

139 lines
2.9 KiB
JavaScript
Raw Normal View History

const fetch = require("node-fetch")
const { SearchIndexes } = require("../../db/utils")
const { checkSlashesInUrl } = require("../../utilities")
const env = require("../../environment")
function buildSearchUrl(
appId,
query,
bookmark = null,
limit = 50,
includeDocs = true
) {
let url = `${env.COUCH_DB_URL}/${appId}/_design/database/_search`
url += `/${SearchIndexes.ROWS}?q=${query}`
if (includeDocs) {
url += "&include_docs=true"
}
if (limit) {
url += `&limit=${limit}`
}
if (bookmark) {
url += `&bookmark=${bookmark}`
}
return checkSlashesInUrl(url)
}
class QueryBuilder {
constructor(appId, base) {
this.appId = appId
this.query = {
string: {},
fuzzy: {},
range: {},
equal: {},
meta: {},
...base,
}
this.limit = 50
this.bookmark = null
}
setLimit(limit) {
this.limit = limit
return this
}
setBookmark(bookmark) {
this.bookmark = bookmark
return this
}
addString(key, partial) {
this.query.string[key] = partial
return this
}
addFuzzy(key, fuzzy) {
this.query.fuzzy[key] = fuzzy
return this
}
addRange(key, low, high) {
this.query.range = {
low,
high,
}
return this
}
addEqual(key, value) {
this.query.equal[key] = value
return this
}
addTable(tableId) {
this.query.equal.tableId = tableId
return this
}
complete() {
let output = ""
function build(structure, queryFn) {
for (let [key, value] of Object.entries(structure)) {
if (output.length !== 0) {
output += " AND "
}
output += queryFn(key, value)
}
}
if (this.query.string) {
build(this.query.string, (key, value) => `${key}:${value}*`)
}
if (this.query.number) {
build(this.query.number, (key, value) =>
value.length == null
? `${key}:${value}`
: `${key}:[${value[0]} TO ${value[1]}]`
)
}
if (this.query.fuzzy) {
build(this.query.fuzzy, (key, value) => `${key}:${value}~`)
}
return buildSearchUrl(this.appId, output, this.bookmark, this.limit)
}
}
exports.QueryBuilder = QueryBuilder
exports.search = async query => {
const response = await fetch(query, {
method: "GET",
})
const json = await response.json()
let output = {
rows: [],
}
if (json.rows != null && json.rows.length > 0) {
output.rows = json.rows.map(row => row.doc)
}
if (json.bookmark) {
output.bookmark = json.bookmark
}
return output
}
exports.rowSearch = async ctx => {
// this can't be done through pouch, have to reach for trusty node-fetch
const appId = ctx.user.appId
const bookmark = ctx.params.bookmark
let url
if (ctx.params.query) {
url = new QueryBuilder(appId, ctx.params.query, bookmark).complete()
} else if (ctx.params.raw) {
url = buildSearchUrl(appId, ctx.params.raw, bookmark)
}
ctx.body = await exports.search(url)
}