139 lines
2.9 KiB
JavaScript
139 lines
2.9 KiB
JavaScript
|
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)
|
||
|
}
|