Making progress.

This commit is contained in:
Sam Rose 2024-06-12 17:58:13 +01:00
parent ae6539161f
commit 22bf0d05ad
No known key found for this signature in database
2 changed files with 52 additions and 37 deletions

View File

@ -1706,7 +1706,7 @@ describe.each([
}) })
describe("contains", () => { describe("contains", () => {
it.only("successfully finds a row", () => it("successfully finds a row", () =>
expectQuery({ contains: { users: [user1._id] } }).toContainExactly([ expectQuery({ contains: { users: [user1._id] } }).toContainExactly([
{ users: [{ _id: user1._id }] }, { users: [{ _id: user1._id }] },
{ users: [{ _id: user1._id }, { _id: user2._id }] }, { users: [{ _id: user1._id }, { _id: user2._id }] },
@ -1763,9 +1763,12 @@ describe.each([
// This will never work for Lucene. // This will never work for Lucene.
!isLucene && !isLucene &&
// It also can't work for in-memory searching because the related table name
// isn't available.
!isInMemory &&
describe("relations", () => { describe("relations", () => {
let otherTable: Table let otherTable: Table
let rows: Row[] let otherRows: Row[]
beforeAll(async () => { beforeAll(async () => {
otherTable = await createTable({ otherTable = await createTable({
@ -1785,7 +1788,7 @@ describe.each([
}, },
}) })
rows = await Promise.all([ otherRows = await Promise.all([
config.api.row.save(otherTable._id!, { one: "foo" }), config.api.row.save(otherTable._id!, { one: "foo" }),
config.api.row.save(otherTable._id!, { one: "bar" }), config.api.row.save(otherTable._id!, { one: "bar" }),
]) ])
@ -1793,18 +1796,22 @@ describe.each([
await Promise.all([ await Promise.all([
config.api.row.save(table._id!, { config.api.row.save(table._id!, {
two: "foo", two: "foo",
other: [rows[0]._id], other: [otherRows[0]._id],
}), }),
config.api.row.save(table._id!, { config.api.row.save(table._id!, {
two: "bar", two: "bar",
other: [rows[1]._id], other: [otherRows[1]._id],
}), }),
]) ])
rows = await config.api.row.fetch(table._id!)
}) })
it("can search through relations", () => it("can search through relations", () =>
expectQuery({ expectQuery({
equal: { [`${otherTable.name}.one`]: "foo" }, equal: { [`${otherTable.name}.one`]: "foo" },
}).toContainExactly([{ two: "foo", other: [{ _id: rows[0]._id }] }])) }).toContainExactly([
{ two: "foo", other: [{ _id: otherRows[0]._id }] },
]))
}) })
}) })

View File

@ -14,6 +14,7 @@ import {
import dayjs from "dayjs" import dayjs from "dayjs"
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants" import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
import { deepGet, schema } from "./helpers" import { deepGet, schema } from "./helpers"
import _ from "lodash"
const HBS_REGEX = /{{([^{].*?)}}/g const HBS_REGEX = /{{([^{].*?)}}/g
@ -339,15 +340,36 @@ export const runQuery = (
} }
) )
// This function exists to check that either the docValue is equal to the
// testValue, or if the docValue is an object or array of objects, that the
// _id of the docValue is equal to the testValue.
const _valueMatches = (docValue: any, testValue: any) => {
if (Array.isArray(docValue)) {
for (const item of docValue) {
if (_valueMatches(item, testValue)) {
return true
}
}
return false
}
if (typeof docValue === "object" && typeof testValue === "string") {
return docValue._id === testValue
}
return docValue === testValue
}
const not = const not =
<T extends any[]>(f: (...args: T) => boolean) => <T extends any[]>(f: (...args: T) => boolean) =>
(...args: T): boolean => (...args: T): boolean =>
!f(...args) !f(...args)
const _equal = (docValue: any, testValue: any) => docValue === testValue const equalMatch = match(SearchFilterOperator.EQUAL, _valueMatches)
const notEqualMatch = match(
const equalMatch = match(SearchFilterOperator.EQUAL, _equal) SearchFilterOperator.NOT_EQUAL,
const notEqualMatch = match(SearchFilterOperator.NOT_EQUAL, not(_equal)) not(_valueMatches)
)
const _empty = (docValue: any) => { const _empty = (docValue: any) => {
if (typeof docValue === "string") { if (typeof docValue === "string") {
@ -379,54 +401,40 @@ export const runQuery = (
return false return false
} }
return testValue.includes(docValue) return testValue.some(item => _valueMatches(docValue, item))
} }
) )
const _contains =
(f: "some" | "every") => (docValue: any, testValue: any) => {
if (!Array.isArray(docValue)) {
return false
}
if (typeof testValue === "string") {
testValue = testValue.split(",")
if (typeof docValue[0] === "number") {
testValue = testValue.map((item: string) => parseFloat(item))
}
}
if (!Array.isArray(testValue)) {
return false
}
return testValue[f](item => _valueMatches(docValue, item))
}
const contains = match(SearchFilterOperator.CONTAINS, _contains("every"))
const notContains = match(
SearchFilterOperator.NOT_CONTAINS,
not(_contains("every"))
)
const containsAny = match( const containsAny = match(
SearchFilterOperator.CONTAINS_ANY, SearchFilterOperator.CONTAINS_ANY,
(docValue: any, testValue: any) => { _contains("some")
if (!Array.isArray(docValue)) {
return false
}
if (typeof testValue === "string") {
testValue = testValue.split(",")
if (typeof docValue[0] === "number") {
testValue = testValue.map((item: string) => parseFloat(item))
}
}
if (!Array.isArray(testValue)) {
return false
}
return testValue.some(item => docValue.includes(item))
}
) )
const _contains = (docValue: any, testValue: any) => {
if (!Array.isArray(docValue)) {
return false
}
if (typeof testValue === "string") {
testValue = testValue.split(",")
if (typeof docValue[0] === "number") {
testValue = testValue.map((item: string) => parseFloat(item))
}
}
if (!Array.isArray(testValue)) {
return false
}
return testValue.every(item => docValue.includes(item))
}
const contains = match(SearchFilterOperator.CONTAINS, _contains)
const notContains = match(SearchFilterOperator.NOT_CONTAINS, not(_contains))
const docMatch = (doc: Record<string, any>) => { const docMatch = (doc: Record<string, any>) => {
const filterFunctions = { const filterFunctions = {
string: stringMatch, string: stringMatch,