1
0
Fork 0
mirror of synced 2024-09-12 23:43:09 +12:00

All tests passing.

This commit is contained in:
Sam Rose 2024-05-20 17:01:52 +01:00
parent 402afa1df6
commit e2a1ab7eaf
No known key found for this signature in database
2 changed files with 78 additions and 25 deletions

View file

@ -22,12 +22,12 @@ import tk from "timekeeper"
import { encodeJSBinding } from "@budibase/string-templates" import { encodeJSBinding } from "@budibase/string-templates"
describe.each([ describe.each([
//["lucene", undefined], ["lucene", undefined],
["sqs", undefined], ["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 === "sqs" const isSqs = name === "sqs"
const isLucene = name === "lucene" const isLucene = name === "lucene"
@ -1288,6 +1288,7 @@ describe.each([
await createRows([ await createRows([
{ user: JSON.stringify(user1) }, { user: JSON.stringify(user1) },
{ user: JSON.stringify(user2) }, { user: JSON.stringify(user2) },
{ user: null },
]) ])
}) })
@ -1305,12 +1306,14 @@ describe.each([
it("successfully finds a row", () => it("successfully finds a row", () =>
expectQuery({ notEqual: { user: user1._id } }).toContainExactly([ expectQuery({ notEqual: { user: user1._id } }).toContainExactly([
{ user: { _id: user2._id } }, { user: { _id: user2._id } },
{},
])) ]))
it("fails to find nonexistent row", () => it("fails to find nonexistent row", () =>
expectQuery({ notEqual: { user: "us_none" } }).toContainExactly([ expectQuery({ notEqual: { user: "us_none" } }).toContainExactly([
{ user: { _id: user1._id } }, { user: { _id: user1._id } },
{ user: { _id: user2._id } }, { user: { _id: user2._id } },
{ user: {} },
])) ]))
}) })
@ -1323,6 +1326,19 @@ describe.each([
it("fails to find nonexistent row", () => it("fails to find nonexistent row", () =>
expectQuery({ oneOf: { user: ["us_none"] } }).toFindNothing()) expectQuery({ oneOf: { user: ["us_none"] } }).toFindNothing())
}) })
describe("empty", () => {
it("finds empty rows", () =>
expectQuery({ empty: { user: null } }).toContainExactly([{}]))
})
describe("notEmpty", () => {
it("finds non-empty rows", () =>
expectQuery({ notEmpty: { user: null } }).toContainExactly([
{ user: { _id: user1._id } },
{ user: { _id: user2._id } },
]))
})
}) })
describe("multi user", () => { describe("multi user", () => {
@ -1338,14 +1354,19 @@ describe.each([
name: "users", name: "users",
type: FieldType.BB_REFERENCE, type: FieldType.BB_REFERENCE,
subtype: BBReferenceFieldSubType.USER, subtype: BBReferenceFieldSubType.USER,
constraints: { type: "array" },
},
number: {
name: "number",
type: FieldType.NUMBER,
}, },
}) })
await createRows([ await createRows([
{ users: JSON.stringify([user1]) }, { number: 1, users: JSON.stringify([user1]) },
{ users: JSON.stringify([user2]) }, { number: 2, users: JSON.stringify([user2]) },
{ users: JSON.stringify([user1, user2]) }, { number: 3, users: JSON.stringify([user1, user2]) },
{ users: JSON.stringify([]) }, { number: 4, users: JSON.stringify([]) },
]) ])
}) })
@ -1389,5 +1410,19 @@ describe.each([
it("fails to find nonexistent row", () => it("fails to find nonexistent row", () =>
expectQuery({ containsAny: { users: ["us_none"] } }).toFindNothing()) expectQuery({ containsAny: { users: ["us_none"] } }).toFindNothing())
}) })
describe("multi-column equals", () => {
it("successfully finds a row", () =>
expectQuery({
equal: { number: 1 },
contains: { users: [user1._id] },
}).toContainExactly([{ users: [{ _id: user1._id }], number: 1 }]))
it("fails to find nonexistent row", () =>
expectQuery({
equal: { number: 2 },
contains: { users: [user1._id] },
}).toFindNothing())
})
}) })
}) })

View file

@ -226,8 +226,7 @@ class InternalBuilder {
} }
const contains = (mode: object, any: boolean = false) => { const contains = (mode: object, any: boolean = false) => {
const fnc = allOr ? "orWhere" : "where" const rawFnc = allOr ? "orWhereRaw" : "whereRaw"
const rawFnc = `${fnc}Raw`
const not = mode === filters?.notContains ? "NOT " : "" const not = mode === filters?.notContains ? "NOT " : ""
function stringifyArray(value: Array<any>, quoteStyle = '"'): string { function stringifyArray(value: Array<any>, quoteStyle = '"'): string {
for (let i in value) { for (let i in value) {
@ -240,24 +239,24 @@ class InternalBuilder {
if (this.client === SqlClient.POSTGRES) { if (this.client === SqlClient.POSTGRES) {
iterate(mode, (key: string, value: Array<any>) => { iterate(mode, (key: string, value: Array<any>) => {
const wrap = any ? "" : "'" const wrap = any ? "" : "'"
const containsOp = any ? "\\?| array" : "@>" const op = any ? "\\?| array" : "@>"
const fieldNames = key.split(/\./g) const fieldNames = key.split(/\./g)
const tableName = fieldNames[0] const table = fieldNames[0]
const columnName = fieldNames[1] const col = fieldNames[1]
// @ts-ignore
query = query[rawFnc]( query = query[rawFnc](
`${not}"${tableName}"."${columnName}"::jsonb ${containsOp} ${wrap}${stringifyArray( `${not}COALESCE("${table}"."${col}"::jsonb ${op} ${wrap}${stringifyArray(
value, value,
any ? "'" : '"' any ? "'" : '"'
)}${wrap}` )}${wrap}, FALSE)`
) )
}) })
} 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: string, value: Array<any>) => { iterate(mode, (key: string, value: Array<any>) => {
// @ts-ignore
query = query[rawFnc]( query = query[rawFnc](
`${not}${jsonFnc}(${key}, '${stringifyArray(value)}')` `${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray(
value
)}'), FALSE)`
) )
}) })
} else { } else {
@ -272,8 +271,7 @@ class InternalBuilder {
} }
statement += statement +=
(statement ? andOr : "") + (statement ? andOr : "") +
// `COALESCE(LOWER(${likeKey(this.client, key)}) LIKE ?, FALSE)` `COALESCE(LOWER(${likeKey(this.client, key)}), '') LIKE ?`
`LOWER(${likeKey(this.client, key)}) LIKE ?`
} }
if (statement === "") { if (statement === "") {
@ -338,14 +336,34 @@ class InternalBuilder {
} }
if (filters.equal) { if (filters.equal) {
iterate(filters.equal, (key, value) => { iterate(filters.equal, (key, value) => {
const fnc = allOr ? "orWhere" : "where" const fnc = allOr ? "orWhereRaw" : "whereRaw"
query = query[fnc]({ [key]: value }) if (this.client === SqlClient.MS_SQL) {
query = query[fnc](
`CASE WHEN ${likeKey(this.client, key)} = ? THEN 1 ELSE 0 END = 1`,
[value]
)
} else {
query = query[fnc](
`COALESCE(${likeKey(this.client, key)} = ?, FALSE)`,
[value]
)
}
}) })
} }
if (filters.notEqual) { if (filters.notEqual) {
iterate(filters.notEqual, (key, value) => { iterate(filters.notEqual, (key, value) => {
const fnc = allOr ? "orWhereNot" : "whereNot" const fnc = allOr ? "orWhereRaw" : "whereRaw"
query = query[fnc]({ [key]: value }) if (this.client === SqlClient.MS_SQL) {
query = query[fnc](
`CASE WHEN ${likeKey(this.client, key)} = ? THEN 1 ELSE 0 END = 0`,
[value]
)
} else {
query = query[fnc](
`COALESCE(${likeKey(this.client, key)} != ?, TRUE)`,
[value]
)
}
}) })
} }
if (filters.empty) { if (filters.empty) {