1
0
Fork 0
mirror of synced 2024-09-21 11:53:49 +12:00

Saving at this point - got exists working.

This commit is contained in:
mike12345567 2024-08-28 18:41:02 +01:00
parent 413628ca3f
commit 49c1f34b5d

View file

@ -343,18 +343,24 @@ class InternalBuilder {
whereCb: (query: Knex.QueryBuilder) => Knex.QueryBuilder whereCb: (query: Knex.QueryBuilder) => Knex.QueryBuilder
): Knex.QueryBuilder { ): Knex.QueryBuilder {
const mainKnex = this.knex const mainKnex = this.knex
const { relationships, endpoint } = this.query const { relationships, endpoint, tableAliases: aliases } = this.query
const tableName = endpoint.entityId const tableName = endpoint.entityId
if (!relationships) { if (!relationships) {
return query return query
} }
for (const relationship of relationships) { for (const relationship of relationships) {
// this is the relationship which is being filtered // this is the relationship which is being filtered
if (filterKey.startsWith(relationship.column) && relationship.to) { if (
const subQuery = query.whereExists(function () { filterKey.startsWith(`${relationship.tableName}.`) &&
this.select(mainKnex.raw(1)).from(relationship.tableName!) relationship.to &&
}) relationship.tableName
query = whereCb( ) {
const relatedTableName = relationship.tableName
const alias = aliases?.[relatedTableName] || relatedTableName
let subQuery = mainKnex
.select(mainKnex.raw(1))
.from({ [alias]: relatedTableName })
subQuery = whereCb(
this.addJoin( this.addJoin(
subQuery, subQuery,
{ {
@ -365,6 +371,7 @@ class InternalBuilder {
[relationship] [relationship]
) )
) )
query = query.whereExists(subQuery)
break break
} }
} }
@ -382,12 +389,13 @@ class InternalBuilder {
if (!filters) { if (!filters) {
return query return query
} }
const builder = this
filters = this.parseFilters({ ...filters }) filters = this.parseFilters({ ...filters })
const aliases = this.query.tableAliases const aliases = this.query.tableAliases
// if all or specified in filters, then everything is an or // if all or specified in filters, then everything is an or
const allOr = filters.allOr const allOr = filters.allOr
const tableName = const isSqlite = this.client === SqlClient.SQL_LITE
this.client === SqlClient.SQL_LITE ? this.table._id! : this.table.name const tableName = isSqlite ? this.table._id! : this.table.name
function getTableAlias(name: string) { function getTableAlias(name: string) {
const alias = aliases?.[name] const alias = aliases?.[name]
@ -395,13 +403,33 @@ class InternalBuilder {
} }
function iterate( function iterate(
structure: AnySearchFilter, structure: AnySearchFilter,
fn: (key: string, value: any) => void, fn: (
complexKeyFn?: (key: string[], value: any) => void query: Knex.QueryBuilder,
key: string,
value: any
) => Knex.QueryBuilder,
complexKeyFn?: (
query: Knex.QueryBuilder,
key: string[],
value: any
) => Knex.QueryBuilder
) { ) {
const handleRelationship = (
q: Knex.QueryBuilder,
key: string,
value: any
) => {
const [filterTableName, ...otherProperties] = key.split(".")
const property = otherProperties.join(".")
const alias = getTableAlias(filterTableName)
return fn(q, alias ? `${alias}.${property}` : property, value)
}
for (const key in structure) { for (const key in structure) {
const value = structure[key] const value = structure[key]
const updatedKey = dbCore.removeKeyNumbering(key) const updatedKey = dbCore.removeKeyNumbering(key)
const isRelationshipField = updatedKey.includes(".") const isRelationshipField = updatedKey.includes(".")
const shouldProcessRelationship =
opts?.relationship && isRelationshipField
let castedTypeValue let castedTypeValue
if ( if (
@ -410,7 +438,8 @@ class InternalBuilder {
complexKeyFn complexKeyFn
) { ) {
const alias = getTableAlias(tableName) const alias = getTableAlias(tableName)
complexKeyFn( query = complexKeyFn(
query,
castedTypeValue.id.map((x: string) => castedTypeValue.id.map((x: string) =>
alias ? `${alias}.${x}` : x alias ? `${alias}.${x}` : x
), ),
@ -418,27 +447,31 @@ class InternalBuilder {
) )
} else if (!isRelationshipField) { } else if (!isRelationshipField) {
const alias = getTableAlias(tableName) const alias = getTableAlias(tableName)
fn(alias ? `${alias}.${updatedKey}` : updatedKey, value) query = fn(
} query,
if (opts?.relationship && isRelationshipField) { alias ? `${alias}.${updatedKey}` : updatedKey,
// TODO: need to update fn to take the query value
const [filterTableName, property] = updatedKey.split(".") )
const alias = getTableAlias(filterTableName) } else if (isSqlite && shouldProcessRelationship) {
fn(alias ? `${alias}.${property}` : property, value) query = builder.addRelationshipForFilter(query, updatedKey, q => {
return handleRelationship(q, updatedKey, value)
})
} else if (shouldProcessRelationship) {
query = handleRelationship(query, updatedKey, value)
} }
} }
} }
const like = (key: string, value: any) => { const like = (q: Knex.QueryBuilder, key: string, value: any) => {
const fuzzyOr = filters?.fuzzyOr const fuzzyOr = filters?.fuzzyOr
const fnc = fuzzyOr || allOr ? "orWhere" : "where" const fnc = fuzzyOr || allOr ? "orWhere" : "where"
// postgres supports ilike, nothing else does // postgres supports ilike, nothing else does
if (this.client === SqlClient.POSTGRES) { if (this.client === SqlClient.POSTGRES) {
query = query[fnc](key, "ilike", `%${value}%`) return q[fnc](key, "ilike", `%${value}%`)
} else { } else {
const rawFnc = `${fnc}Raw` const rawFnc = `${fnc}Raw`
// @ts-ignore // @ts-ignore
query = query[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [ return q[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
`%${value.toLowerCase()}%`, `%${value.toLowerCase()}%`,
]) ])
} }
@ -456,13 +489,13 @@ class InternalBuilder {
return `[${value.join(",")}]` return `[${value.join(",")}]`
} }
if (this.client === SqlClient.POSTGRES) { if (this.client === SqlClient.POSTGRES) {
iterate(mode, (key, value) => { iterate(mode, (q, key, value) => {
const wrap = any ? "" : "'" const wrap = any ? "" : "'"
const op = any ? "\\?| array" : "@>" const op = any ? "\\?| array" : "@>"
const fieldNames = key.split(/\./g) const fieldNames = key.split(/\./g)
const table = fieldNames[0] const table = fieldNames[0]
const col = fieldNames[1] const col = fieldNames[1]
query = query[rawFnc]( return q[rawFnc](
`${not}COALESCE("${table}"."${col}"::jsonb ${op} ${wrap}${stringifyArray( `${not}COALESCE("${table}"."${col}"::jsonb ${op} ${wrap}${stringifyArray(
value, value,
any ? "'" : '"' any ? "'" : '"'
@ -471,8 +504,8 @@ class InternalBuilder {
}) })
} else if (this.client === SqlClient.MY_SQL) { } else if (this.client === SqlClient.MY_SQL) {
const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS" const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS"
iterate(mode, (key, value) => { iterate(mode, (q, key, value) => {
query = query[rawFnc]( return q[rawFnc](
`${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray( `${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray(
value value
)}'), FALSE)` )}'), FALSE)`
@ -480,7 +513,7 @@ class InternalBuilder {
}) })
} else { } else {
const andOr = mode === filters?.containsAny ? " OR " : " AND " const andOr = mode === filters?.containsAny ? " OR " : " AND "
iterate(mode, (key, value) => { iterate(mode, (q, key, value) => {
let statement = "" let statement = ""
const identifier = this.quotedIdentifier(key) const identifier = this.quotedIdentifier(key)
for (let i in value) { for (let i in value) {
@ -495,16 +528,16 @@ class InternalBuilder {
} }
if (statement === "") { if (statement === "") {
return return q
} }
if (not) { if (not) {
query = query[rawFnc]( return q[rawFnc](
`(NOT (${statement}) OR ${identifier} IS NULL)`, `(NOT (${statement}) OR ${identifier} IS NULL)`,
value value
) )
} else { } else {
query = query[rawFnc](statement, value) return q[rawFnc](statement, value)
} }
}) })
} }
@ -534,39 +567,39 @@ class InternalBuilder {
const fnc = allOr ? "orWhereIn" : "whereIn" const fnc = allOr ? "orWhereIn" : "whereIn"
iterate( iterate(
filters.oneOf, filters.oneOf,
(key: string, array) => { (q, key: string, array) => {
if (this.client === SqlClient.ORACLE) { if (this.client === SqlClient.ORACLE) {
key = this.convertClobs(key) key = this.convertClobs(key)
array = Array.isArray(array) ? array : [array] array = Array.isArray(array) ? array : [array]
const binding = new Array(array.length).fill("?").join(",") const binding = new Array(array.length).fill("?").join(",")
query = query.whereRaw(`${key} IN (${binding})`, array) return q.whereRaw(`${key} IN (${binding})`, array)
} else { } else {
query = query[fnc](key, Array.isArray(array) ? array : [array]) return q[fnc](key, Array.isArray(array) ? array : [array])
} }
}, },
(key: string[], array) => { (q, key: string[], array) => {
if (this.client === SqlClient.ORACLE) { if (this.client === SqlClient.ORACLE) {
const keyStr = `(${key.map(k => this.convertClobs(k)).join(",")})` const keyStr = `(${key.map(k => this.convertClobs(k)).join(",")})`
const binding = `(${array const binding = `(${array
.map((a: any) => `(${new Array(a.length).fill("?").join(",")})`) .map((a: any) => `(${new Array(a.length).fill("?").join(",")})`)
.join(",")})` .join(",")})`
query = query.whereRaw(`${keyStr} IN ${binding}`, array.flat()) return q.whereRaw(`${keyStr} IN ${binding}`, array.flat())
} else { } else {
query = query[fnc](key, Array.isArray(array) ? array : [array]) return q[fnc](key, Array.isArray(array) ? array : [array])
} }
} }
) )
} }
if (filters.string) { if (filters.string) {
iterate(filters.string, (key, value) => { iterate(filters.string, (q, key, value) => {
const fnc = allOr ? "orWhere" : "where" const fnc = allOr ? "orWhere" : "where"
// postgres supports ilike, nothing else does // postgres supports ilike, nothing else does
if (this.client === SqlClient.POSTGRES) { if (this.client === SqlClient.POSTGRES) {
query = query[fnc](key, "ilike", `${value}%`) return q[fnc](key, "ilike", `${value}%`)
} else { } else {
const rawFnc = `${fnc}Raw` const rawFnc = `${fnc}Raw`
// @ts-ignore // @ts-ignore
query = query[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [ return q[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
`${value.toLowerCase()}%`, `${value.toLowerCase()}%`,
]) ])
} }
@ -576,7 +609,7 @@ class InternalBuilder {
iterate(filters.fuzzy, like) iterate(filters.fuzzy, like)
} }
if (filters.range) { if (filters.range) {
iterate(filters.range, (key, value) => { iterate(filters.range, (q, key, value) => {
const isEmptyObject = (val: any) => { const isEmptyObject = (val: any) => {
return ( return (
val && val &&
@ -605,97 +638,93 @@ class InternalBuilder {
schema?.type === FieldType.BIGINT && schema?.type === FieldType.BIGINT &&
this.client === SqlClient.SQL_LITE this.client === SqlClient.SQL_LITE
) { ) {
query = query.whereRaw( return q.whereRaw(
`CAST(${key} AS INTEGER) BETWEEN CAST(? AS INTEGER) AND CAST(? AS INTEGER)`, `CAST(${key} AS INTEGER) BETWEEN CAST(? AS INTEGER) AND CAST(? AS INTEGER)`,
[value.low, value.high] [value.low, value.high]
) )
} else { } else {
const fnc = allOr ? "orWhereBetween" : "whereBetween" const fnc = allOr ? "orWhereBetween" : "whereBetween"
query = query[fnc](key, [value.low, value.high]) return q[fnc](key, [value.low, value.high])
} }
} else if (lowValid) { } else if (lowValid) {
if ( if (
schema?.type === FieldType.BIGINT && schema?.type === FieldType.BIGINT &&
this.client === SqlClient.SQL_LITE this.client === SqlClient.SQL_LITE
) { ) {
query = query.whereRaw( return q.whereRaw(`CAST(${key} AS INTEGER) >= CAST(? AS INTEGER)`, [
`CAST(${key} AS INTEGER) >= CAST(? AS INTEGER)`, value.low,
[value.low] ])
)
} else { } else {
const fnc = allOr ? "orWhere" : "where" const fnc = allOr ? "orWhere" : "where"
query = query[fnc](key, ">=", value.low) return q[fnc](key, ">=", value.low)
} }
} else if (highValid) { } else if (highValid) {
if ( if (
schema?.type === FieldType.BIGINT && schema?.type === FieldType.BIGINT &&
this.client === SqlClient.SQL_LITE this.client === SqlClient.SQL_LITE
) { ) {
query = query.whereRaw( return q.whereRaw(`CAST(${key} AS INTEGER) <= CAST(? AS INTEGER)`, [
`CAST(${key} AS INTEGER) <= CAST(? AS INTEGER)`, value.high,
[value.high] ])
)
} else { } else {
const fnc = allOr ? "orWhere" : "where" const fnc = allOr ? "orWhere" : "where"
query = query[fnc](key, "<=", value.high) return q[fnc](key, "<=", value.high)
} }
} }
return q
}) })
} }
if (filters.equal) { if (filters.equal) {
iterate(filters.equal, (key, value) => { iterate(filters.equal, (q, key, value) => {
const fnc = allOr ? "orWhereRaw" : "whereRaw" const fnc = allOr ? "orWhereRaw" : "whereRaw"
if (this.client === SqlClient.MS_SQL) { if (this.client === SqlClient.MS_SQL) {
query = query[fnc]( return q[fnc](
`CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 1`, `CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 1`,
[value] [value]
) )
} else if (this.client === SqlClient.ORACLE) { } else if (this.client === SqlClient.ORACLE) {
const identifier = this.convertClobs(key) const identifier = this.convertClobs(key)
query = query[fnc]( return q[fnc](`(${identifier} IS NOT NULL AND ${identifier} = ?)`, [
`(${identifier} IS NOT NULL AND ${identifier} = ?)`, value,
[value] ])
)
} else { } else {
query = query[fnc]( return q[fnc](`COALESCE(${this.quotedIdentifier(key)} = ?, FALSE)`, [
`COALESCE(${this.quotedIdentifier(key)} = ?, FALSE)`, value,
[value] ])
)
} }
}) })
} }
if (filters.notEqual) { if (filters.notEqual) {
iterate(filters.notEqual, (key, value) => { iterate(filters.notEqual, (q, key, value) => {
const fnc = allOr ? "orWhereRaw" : "whereRaw" const fnc = allOr ? "orWhereRaw" : "whereRaw"
if (this.client === SqlClient.MS_SQL) { if (this.client === SqlClient.MS_SQL) {
query = query[fnc]( return q[fnc](
`CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 0`, `CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 0`,
[value] [value]
) )
} else if (this.client === SqlClient.ORACLE) { } else if (this.client === SqlClient.ORACLE) {
const identifier = this.convertClobs(key) const identifier = this.convertClobs(key)
query = query[fnc]( return q[fnc](
`(${identifier} IS NOT NULL AND ${identifier} != ?) OR ${identifier} IS NULL`, `(${identifier} IS NOT NULL AND ${identifier} != ?) OR ${identifier} IS NULL`,
[value] [value]
) )
} else { } else {
query = query[fnc]( return q[fnc](`COALESCE(${this.quotedIdentifier(key)} != ?, TRUE)`, [
`COALESCE(${this.quotedIdentifier(key)} != ?, TRUE)`, value,
[value] ])
)
} }
}) })
} }
if (filters.empty) { if (filters.empty) {
iterate(filters.empty, key => { iterate(filters.empty, (q, key) => {
const fnc = allOr ? "orWhereNull" : "whereNull" const fnc = allOr ? "orWhereNull" : "whereNull"
query = query[fnc](key) return q[fnc](key)
}) })
} }
if (filters.notEmpty) { if (filters.notEmpty) {
iterate(filters.notEmpty, key => { iterate(filters.notEmpty, (q, key) => {
const fnc = allOr ? "orWhereNotNull" : "whereNotNull" const fnc = allOr ? "orWhereNotNull" : "whereNotNull"
query = query[fnc](key) return q[fnc](key)
}) })
} }
if (filters.contains) { if (filters.contains) {