Merge pull request #14423 from Budibase/fix/conditions-on-views
Fix query conditions on views
This commit is contained in:
commit
9f9c93bc18
|
@ -339,7 +339,7 @@ class InternalBuilder {
|
||||||
if (!filters) {
|
if (!filters) {
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -468,18 +468,20 @@ class InternalBuilder {
|
||||||
|
|
||||||
if (filters.$and) {
|
if (filters.$and) {
|
||||||
const { $and } = filters
|
const { $and } = filters
|
||||||
query = query.where(x => {
|
for (const condition of $and.conditions) {
|
||||||
for (const condition of $and.conditions) {
|
query = query.where(b => {
|
||||||
x = this.addFilters(x, condition, opts)
|
this.addFilters(b, condition, opts)
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters.$or) {
|
if (filters.$or) {
|
||||||
const { $or } = filters
|
const { $or } = filters
|
||||||
query = query.where(x => {
|
query = query.where(b => {
|
||||||
for (const condition of $or.conditions) {
|
for (const condition of $or.conditions) {
|
||||||
x = this.addFilters(x, { ...condition, allOr: true }, opts)
|
b.orWhere(c =>
|
||||||
|
this.addFilters(c, { ...condition, allOr: true }, opts)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ export async function searchView(
|
||||||
let query = dataFilters.buildQuery(view.query || [])
|
let query = dataFilters.buildQuery(view.query || [])
|
||||||
if (body.query) {
|
if (body.query) {
|
||||||
// Delete extraneous search params that cannot be overridden
|
// Delete extraneous search params that cannot be overridden
|
||||||
delete body.query.allOr
|
|
||||||
delete body.query.onEmptyFilter
|
delete body.query.onEmptyFilter
|
||||||
|
|
||||||
if (!isExternalTableID(view.tableId) && !db.isSqsEnabledForTenant()) {
|
if (!isExternalTableID(view.tableId) && !db.isSqsEnabledForTenant()) {
|
||||||
|
|
|
@ -1492,83 +1492,189 @@ describe.each([
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
isLucene &&
|
it("can query on top of the view filters", async () => {
|
||||||
it("in lucene, cannot override a view filter", async () => {
|
await config.api.row.save(table._id!, {
|
||||||
await config.api.row.save(table._id!, {
|
one: "foo",
|
||||||
one: "foo",
|
two: "bar",
|
||||||
two: "bar",
|
})
|
||||||
})
|
await config.api.row.save(table._id!, {
|
||||||
const two = await config.api.row.save(table._id!, {
|
one: "foo2",
|
||||||
one: "foo2",
|
two: "bar2",
|
||||||
two: "bar2",
|
})
|
||||||
})
|
const three = await config.api.row.save(table._id!, {
|
||||||
|
one: "foo3",
|
||||||
const view = await config.api.viewV2.create({
|
two: "bar3",
|
||||||
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 }),
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
!isLucene &&
|
const view = await config.api.viewV2.create({
|
||||||
it("can filter a view without a view filter", async () => {
|
tableId: table._id!,
|
||||||
const one = await config.api.row.save(table._id!, {
|
name: generator.guid(),
|
||||||
one: "foo",
|
query: [
|
||||||
two: "bar",
|
{
|
||||||
})
|
operator: BasicOperator.NOT_EQUAL,
|
||||||
await config.api.row.save(table._id!, {
|
field: "one",
|
||||||
one: "foo2",
|
value: "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 },
|
|
||||||
},
|
},
|
||||||
})
|
],
|
||||||
|
schema: {
|
||||||
|
id: { visible: true },
|
||||||
|
one: { visible: true },
|
||||||
|
two: { visible: true },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const response = await config.api.viewV2.search(view.id, {
|
const response = await config.api.viewV2.search(view.id, {
|
||||||
query: {
|
query: {
|
||||||
equal: {
|
[BasicOperator.EQUAL]: {
|
||||||
two: "bar",
|
two: "bar3",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
[BasicOperator.NOT_EMPTY]: {
|
||||||
expect(response.rows).toHaveLength(1)
|
two: null,
|
||||||
expect(response.rows).toEqual([
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
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: 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 &&
|
!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!, {
|
await config.api.row.save(table._id!, {
|
||||||
one: "foo",
|
one: "foo",
|
||||||
two: "bar",
|
two: "bar",
|
||||||
|
@ -1597,6 +1703,7 @@ describe.each([
|
||||||
|
|
||||||
const response = await config.api.viewV2.search(view.id, {
|
const response = await config.api.viewV2.search(view.id, {
|
||||||
query: {
|
query: {
|
||||||
|
allOr,
|
||||||
equal: {
|
equal: {
|
||||||
two: "bar",
|
two: "bar",
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue