Range tests passing.
This commit is contained in:
parent
253fa0def8
commit
03b1823463
|
@ -18,12 +18,12 @@ import _ from "lodash"
|
||||||
jest.unmock("mssql")
|
jest.unmock("mssql")
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
// ["internal", undefined],
|
["internal", undefined],
|
||||||
["internal-sqs", undefined],
|
["internal-sqs", undefined],
|
||||||
// [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
[DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
||||||
// [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
[DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
||||||
// [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
[DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
||||||
// [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
[DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
||||||
])("/api/:sourceId/search (%s)", (name, dsProvider) => {
|
])("/api/:sourceId/search (%s)", (name, dsProvider) => {
|
||||||
const isSqs = name === "internal-sqs"
|
const isSqs = name === "internal-sqs"
|
||||||
const isInternal = name === "internal"
|
const isInternal = name === "internal"
|
||||||
|
@ -337,6 +337,20 @@ describe.each([
|
||||||
expectQuery({
|
expectQuery({
|
||||||
range: { age: { low: 5, high: 9 } },
|
range: { age: { low: 5, high: 9 } },
|
||||||
}).toFindNothing())
|
}).toFindNothing())
|
||||||
|
|
||||||
|
// We never implemented half-open ranges in Lucene.
|
||||||
|
!isInternal &&
|
||||||
|
it("can search using just a low value", () =>
|
||||||
|
expectQuery({
|
||||||
|
range: { age: { low: 5 } },
|
||||||
|
}).toContainExactly([{ age: 10 }]))
|
||||||
|
|
||||||
|
// We never implemented half-open ranges in Lucene.
|
||||||
|
!isInternal &&
|
||||||
|
it("can search using just a high value", () =>
|
||||||
|
expectQuery({
|
||||||
|
range: { age: { high: 5 } },
|
||||||
|
}).toContainExactly([{ age: 1 }]))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("sort", () => {
|
describe("sort", () => {
|
||||||
|
@ -441,6 +455,20 @@ describe.each([
|
||||||
expectQuery({
|
expectQuery({
|
||||||
range: { dob: { low: JAN_5TH, high: JAN_9TH } },
|
range: { dob: { low: JAN_5TH, high: JAN_9TH } },
|
||||||
}).toFindNothing())
|
}).toFindNothing())
|
||||||
|
|
||||||
|
// We never implemented half-open ranges in Lucene.
|
||||||
|
!isInternal &&
|
||||||
|
it("can search using just a low value", () =>
|
||||||
|
expectQuery({
|
||||||
|
range: { dob: { low: JAN_5TH } },
|
||||||
|
}).toContainExactly([{ dob: JAN_10TH }]))
|
||||||
|
|
||||||
|
// We never implemented half-open ranges in Lucene.
|
||||||
|
!isInternal &&
|
||||||
|
it("can search using just a high value", () =>
|
||||||
|
expectQuery({
|
||||||
|
range: { dob: { high: JAN_5TH } },
|
||||||
|
}).toContainExactly([{ dob: JAN_1ST }]))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("sort", () => {
|
describe("sort", () => {
|
||||||
|
@ -616,7 +644,7 @@ describe.each([
|
||||||
// we've decided not to spend time on it.
|
// we've decided not to spend time on it.
|
||||||
!isInternal &&
|
!isInternal &&
|
||||||
describe("range", () => {
|
describe("range", () => {
|
||||||
it.only("successfully finds a row", () =>
|
it("successfully finds a row", () =>
|
||||||
expectQuery({
|
expectQuery({
|
||||||
range: { num: { low: SMALL, high: "5" } },
|
range: { num: { low: SMALL, high: "5" } },
|
||||||
}).toContainExactly([{ num: SMALL }]))
|
}).toContainExactly([{ num: SMALL }]))
|
||||||
|
@ -635,6 +663,16 @@ describe.each([
|
||||||
expectQuery({
|
expectQuery({
|
||||||
range: { num: { low: "5", high: "5" } },
|
range: { num: { low: "5", high: "5" } },
|
||||||
}).toFindNothing())
|
}).toFindNothing())
|
||||||
|
|
||||||
|
it("can search using just a low value", () =>
|
||||||
|
expectQuery({
|
||||||
|
range: { num: { low: MEDIUM } },
|
||||||
|
}).toContainExactly([{ num: MEDIUM }, { num: BIG }]))
|
||||||
|
|
||||||
|
it("can search using just a high value", () =>
|
||||||
|
expectQuery({
|
||||||
|
range: { num: { high: MEDIUM } },
|
||||||
|
}).toContainExactly([{ num: SMALL }, { num: MEDIUM }]))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -146,7 +146,7 @@ class InternalBuilder {
|
||||||
addFilters(
|
addFilters(
|
||||||
query: Knex.QueryBuilder,
|
query: Knex.QueryBuilder,
|
||||||
filters: SearchFilters | undefined,
|
filters: SearchFilters | undefined,
|
||||||
tableName: string,
|
table: Table,
|
||||||
opts: { aliases?: Record<string, string>; relationship?: boolean }
|
opts: { aliases?: Record<string, string>; relationship?: boolean }
|
||||||
): Knex.QueryBuilder {
|
): Knex.QueryBuilder {
|
||||||
function getTableName(name: string) {
|
function getTableName(name: string) {
|
||||||
|
@ -161,7 +161,7 @@ class InternalBuilder {
|
||||||
const updatedKey = dbCore.removeKeyNumbering(key)
|
const updatedKey = dbCore.removeKeyNumbering(key)
|
||||||
const isRelationshipField = updatedKey.includes(".")
|
const isRelationshipField = updatedKey.includes(".")
|
||||||
if (!opts.relationship && !isRelationshipField) {
|
if (!opts.relationship && !isRelationshipField) {
|
||||||
fn(`${getTableName(tableName)}.${updatedKey}`, value)
|
fn(`${getTableName(table.name)}.${updatedKey}`, value)
|
||||||
}
|
}
|
||||||
if (opts.relationship && isRelationshipField) {
|
if (opts.relationship && isRelationshipField) {
|
||||||
const [filterTableName, property] = updatedKey.split(".")
|
const [filterTableName, property] = updatedKey.split(".")
|
||||||
|
@ -276,6 +276,9 @@ class InternalBuilder {
|
||||||
}
|
}
|
||||||
if (filters.range) {
|
if (filters.range) {
|
||||||
iterate(filters.range, (key, value) => {
|
iterate(filters.range, (key, value) => {
|
||||||
|
const fieldName = key.split(".")[1]
|
||||||
|
const field = table.schema[fieldName]
|
||||||
|
|
||||||
const isEmptyObject = (val: any) => {
|
const isEmptyObject = (val: any) => {
|
||||||
return (
|
return (
|
||||||
val &&
|
val &&
|
||||||
|
@ -293,16 +296,46 @@ class InternalBuilder {
|
||||||
highValid = isValidFilter(value.high)
|
highValid = isValidFilter(value.high)
|
||||||
if (lowValid && highValid) {
|
if (lowValid && highValid) {
|
||||||
// Use a between operator if we have 2 valid range values
|
// Use a between operator if we have 2 valid range values
|
||||||
const fnc = allOr ? "orWhereBetween" : "whereBetween"
|
if (
|
||||||
query = query[fnc](key, [value.low, value.high])
|
field.type === FieldType.BIGINT &&
|
||||||
|
this.client === SqlClient.SQL_LITE
|
||||||
|
) {
|
||||||
|
query = query.whereRaw(
|
||||||
|
`CAST(${key} AS INTEGER) BETWEEN CAST(? AS INTEGER) AND CAST(? AS INTEGER)`,
|
||||||
|
[value.low, value.high]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const fnc = allOr ? "orWhereBetween" : "whereBetween"
|
||||||
|
query = query[fnc](key, [value.low, value.high])
|
||||||
|
}
|
||||||
} else if (lowValid) {
|
} else if (lowValid) {
|
||||||
// Use just a single greater than operator if we only have a low
|
// Use just a single greater than operator if we only have a low
|
||||||
const fnc = allOr ? "orWhere" : "where"
|
if (
|
||||||
query = query[fnc](key, ">", value.low)
|
field.type === FieldType.BIGINT &&
|
||||||
|
this.client === SqlClient.SQL_LITE
|
||||||
|
) {
|
||||||
|
query = query.whereRaw(
|
||||||
|
`CAST(${key} AS INTEGER) >= CAST(? AS INTEGER)`,
|
||||||
|
[value.low]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const fnc = allOr ? "orWhere" : "where"
|
||||||
|
query = query[fnc](key, ">=", value.low)
|
||||||
|
}
|
||||||
} else if (highValid) {
|
} else if (highValid) {
|
||||||
// Use just a single less than operator if we only have a high
|
// Use just a single less than operator if we only have a high
|
||||||
const fnc = allOr ? "orWhere" : "where"
|
if (
|
||||||
query = query[fnc](key, "<", value.high)
|
field.type === FieldType.BIGINT &&
|
||||||
|
this.client === SqlClient.SQL_LITE
|
||||||
|
) {
|
||||||
|
query = query.whereRaw(
|
||||||
|
`CAST(${key} AS INTEGER) <= CAST(? AS INTEGER)`,
|
||||||
|
[value.high]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const fnc = allOr ? "orWhere" : "where"
|
||||||
|
query = query[fnc](key, "<=", value.high)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -532,7 +565,7 @@ class InternalBuilder {
|
||||||
if (foundOffset) {
|
if (foundOffset) {
|
||||||
query = query.offset(foundOffset)
|
query = query.offset(foundOffset)
|
||||||
}
|
}
|
||||||
query = this.addFilters(query, filters, tableName, {
|
query = this.addFilters(query, filters, json.meta?.table!, {
|
||||||
aliases: tableAliases,
|
aliases: tableAliases,
|
||||||
})
|
})
|
||||||
// add sorting to pre-query
|
// add sorting to pre-query
|
||||||
|
@ -553,7 +586,7 @@ class InternalBuilder {
|
||||||
endpoint.schema,
|
endpoint.schema,
|
||||||
tableAliases
|
tableAliases
|
||||||
)
|
)
|
||||||
return this.addFilters(query, filters, tableName, {
|
return this.addFilters(query, filters, json.meta?.table!, {
|
||||||
relationship: true,
|
relationship: true,
|
||||||
aliases: tableAliases,
|
aliases: tableAliases,
|
||||||
})
|
})
|
||||||
|
@ -563,7 +596,7 @@ class InternalBuilder {
|
||||||
const { endpoint, body, filters, tableAliases } = json
|
const { endpoint, body, filters, tableAliases } = json
|
||||||
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
||||||
const parsedBody = parseBody(body)
|
const parsedBody = parseBody(body)
|
||||||
query = this.addFilters(query, filters, endpoint.entityId, {
|
query = this.addFilters(query, filters, json.meta?.table!, {
|
||||||
aliases: tableAliases,
|
aliases: tableAliases,
|
||||||
})
|
})
|
||||||
// mysql can't use returning
|
// mysql can't use returning
|
||||||
|
@ -577,7 +610,7 @@ class InternalBuilder {
|
||||||
delete(knex: Knex, json: QueryJson, opts: QueryOptions): Knex.QueryBuilder {
|
delete(knex: Knex, json: QueryJson, opts: QueryOptions): Knex.QueryBuilder {
|
||||||
const { endpoint, filters, tableAliases } = json
|
const { endpoint, filters, tableAliases } = json
|
||||||
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
let query = this.knexWithAlias(knex, endpoint, tableAliases)
|
||||||
query = this.addFilters(query, filters, endpoint.entityId, {
|
query = this.addFilters(query, filters, json.meta?.table!, {
|
||||||
aliases: tableAliases,
|
aliases: tableAliases,
|
||||||
})
|
})
|
||||||
// mysql can't use returning
|
// mysql can't use returning
|
||||||
|
|
|
@ -185,6 +185,6 @@ export async function search(
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const msg = typeof err === "string" ? err : err.message
|
const msg = typeof err === "string" ? err : err.message
|
||||||
throw new Error(`Unable to search by SQL - ${msg}`)
|
throw new Error(`Unable to search by SQL - ${msg}`, { cause: err })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,13 @@ export interface SearchFilters {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
range?: {
|
range?: {
|
||||||
[key: string]: {
|
[key: string]:
|
||||||
high: number | string
|
| {
|
||||||
low: number | string
|
high: number | string
|
||||||
}
|
low: number | string
|
||||||
|
}
|
||||||
|
| { high: number | string }
|
||||||
|
| { low: number | string }
|
||||||
}
|
}
|
||||||
equal?: {
|
equal?: {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
|
|
Loading…
Reference in New Issue