Merge pull request #14423 from Budibase/fix/conditions-on-views

Fix query conditions on views
This commit is contained in:
Adria Navarro 2024-08-21 15:15:13 +02:00 committed by GitHub
commit 9f9c93bc18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 185 additions and 77 deletions

View File

@ -339,7 +339,7 @@ class InternalBuilder {
if (!filters) {
return query
}
filters = this.parseFilters(filters)
filters = this.parseFilters({ ...filters })
const aliases = this.query.tableAliases
// if all or specified in filters, then everything is an or
const allOr = filters.allOr
@ -468,18 +468,20 @@ class InternalBuilder {
if (filters.$and) {
const { $and } = filters
query = query.where(x => {
for (const condition of $and.conditions) {
x = this.addFilters(x, condition, opts)
}
})
for (const condition of $and.conditions) {
query = query.where(b => {
this.addFilters(b, condition, opts)
})
}
}
if (filters.$or) {
const { $or } = filters
query = query.where(x => {
query = query.where(b => {
for (const condition of $or.conditions) {
x = this.addFilters(x, { ...condition, allOr: true }, opts)
b.orWhere(c =>
this.addFilters(c, { ...condition, allOr: true }, opts)
)
}
})
}

View File

@ -38,7 +38,6 @@ export async function searchView(
let query = dataFilters.buildQuery(view.query || [])
if (body.query) {
// Delete extraneous search params that cannot be overridden
delete body.query.allOr
delete body.query.onEmptyFilter
if (!isExternalTableID(view.tableId) && !db.isSqsEnabledForTenant()) {

View File

@ -1492,83 +1492,189 @@ describe.each([
)
})
isLucene &&
it("in lucene, cannot override a view filter", async () => {
await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
})
const two = await config.api.row.save(table._id!, {
one: "foo2",
two: "bar2",
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
query: [
{
operator: BasicOperator.EQUAL,
field: "two",
value: "bar2",
},
],
schema: {
id: { visible: true },
one: { visible: false },
two: { visible: true },
},
})
const response = await config.api.viewV2.search(view.id, {
query: {
equal: {
two: "bar",
},
},
})
expect(response.rows).toHaveLength(1)
expect(response.rows).toEqual([
expect.objectContaining({ _id: two._id }),
])
it("can query on top of the view filters", async () => {
await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
})
await config.api.row.save(table._id!, {
one: "foo2",
two: "bar2",
})
const three = await config.api.row.save(table._id!, {
one: "foo3",
two: "bar3",
})
!isLucene &&
it("can filter a view without a view filter", async () => {
const one = await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
})
await config.api.row.save(table._id!, {
one: "foo2",
two: "bar2",
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
schema: {
id: { visible: true },
one: { visible: false },
two: { visible: true },
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
query: [
{
operator: BasicOperator.NOT_EQUAL,
field: "one",
value: "foo2",
},
})
],
schema: {
id: { visible: true },
one: { visible: true },
two: { visible: true },
},
})
const response = await config.api.viewV2.search(view.id, {
query: {
equal: {
two: "bar",
},
const response = await config.api.viewV2.search(view.id, {
query: {
[BasicOperator.EQUAL]: {
two: "bar3",
},
})
expect(response.rows).toHaveLength(1)
expect(response.rows).toEqual([
[BasicOperator.NOT_EMPTY]: {
two: null,
},
},
})
expect(response.rows).toHaveLength(1)
expect(response.rows).toEqual(
expect.arrayContaining([expect.objectContaining({ _id: three._id })])
)
})
it("can query on top of the view filters (using or filters)", async () => {
const one = await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
})
await config.api.row.save(table._id!, {
one: "foo2",
two: "bar2",
})
const three = await config.api.row.save(table._id!, {
one: "foo3",
two: "bar3",
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
query: [
{
operator: BasicOperator.NOT_EQUAL,
field: "two",
value: "bar2",
},
],
schema: {
id: { visible: true },
one: { visible: false },
two: { visible: true },
},
})
const response = await config.api.viewV2.search(view.id, {
query: {
allOr: true,
[BasicOperator.NOT_EQUAL]: {
two: "bar",
},
[BasicOperator.NOT_EMPTY]: {
two: null,
},
},
})
expect(response.rows).toHaveLength(2)
expect(response.rows).toEqual(
expect.arrayContaining([
expect.objectContaining({ _id: one._id }),
expect.objectContaining({ _id: three._id }),
])
})
)
})
isLucene &&
it.each([true, false])(
"in lucene, cannot override a view filter",
async allOr => {
await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
})
const two = await config.api.row.save(table._id!, {
one: "foo2",
two: "bar2",
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
query: [
{
operator: BasicOperator.EQUAL,
field: "two",
value: "bar2",
},
],
schema: {
id: { visible: true },
one: { visible: false },
two: { visible: true },
},
})
const response = await config.api.viewV2.search(view.id, {
query: {
allOr,
equal: {
two: "bar",
},
},
})
expect(response.rows).toHaveLength(1)
expect(response.rows).toEqual([
expect.objectContaining({ _id: two._id }),
])
}
)
!isLucene &&
it("cannot bypass a view filter", async () => {
it.each([true, false])(
"can filter a view without a view filter",
async allOr => {
const one = await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
})
await config.api.row.save(table._id!, {
one: "foo2",
two: "bar2",
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
schema: {
id: { visible: true },
one: { visible: false },
two: { visible: true },
},
})
const response = await config.api.viewV2.search(view.id, {
query: {
allOr,
equal: {
two: "bar",
},
},
})
expect(response.rows).toHaveLength(1)
expect(response.rows).toEqual([
expect.objectContaining({ _id: one._id }),
])
}
)
!isLucene &&
it.each([true, false])("cannot bypass a view filter", async allOr => {
await config.api.row.save(table._id!, {
one: "foo",
two: "bar",
@ -1597,6 +1703,7 @@ describe.each([
const response = await config.api.viewV2.search(view.id, {
query: {
allOr,
equal: {
two: "bar",
},