Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query with object value - support Mysql and Postgres #508

Merged
merged 3 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion apps/velo-external-db/test/e2e/app_data.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as matchers from '../drivers/schema_api_rest_matchers'
import * as data from '../drivers/data_api_rest_test_support'
import * as authorization from '../drivers/authorization_test_support'
import { initApp, teardownApp, dbTeardown, setupDb, currentDbImplementationName, supportedOperations } from '../resources/e2e_resources'
const { UpdateImmediately, DeleteImmediately, Truncate, Aggregate, FindWithSort, Projection, FilterByEveryField, QueryNestedFields, PrimaryKey, AtomicBulkInsert } = SchemaOperations
const { UpdateImmediately, DeleteImmediately, Truncate, Aggregate, FindWithSort, Projection, FilterByEveryField, QueryNestedFields, PrimaryKey, AtomicBulkInsert, FindObject } = SchemaOperations

const chance = Chance()

Expand Down Expand Up @@ -315,6 +315,20 @@ describe(`Velo External DB Data REST API: ${currentDbImplementationName()}`, ()
})
})

testIfSupportedOperationsIncludes(supportedOperations, [ FindObject ])('query on object fields', async() => {
await schema.givenCollection(ctx.collectionName, [ctx.objectColumn], authOwner)
await data.givenItems([ctx.objectItem], ctx.collectionName, authAdmin)

const filter = {
[ctx.objectColumn.name]: ctx.objectItem[ctx.objectColumn.name]
}

await expect(data.queryCollectionAsArray(ctx.collectionName, [], undefined, authOwner, filter)).resolves.toEqual({
items: expect.toIncludeSameMembers([ctx.objectItem]),
pagingMetadata: data.pagingMetadata(1, 1)
})
})

testIfSupportedOperationsIncludes(supportedOperations, [ QueryNestedFields ])('query on nested fields', async() => {
await schema.givenCollection(ctx.collectionName, [ctx.objectColumn], authOwner)
await data.givenItems([ctx.objectItem], ctx.collectionName, authAdmin)
Expand Down
15 changes: 14 additions & 1 deletion libs/external-db-mysql/src/sql_filter_transformer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,20 @@ describe('Sql Parser', () => {
})
})

describe('handle queries on nested fields', () => {
describe('handle queries on objects', () => {
test('correctly transform fully object match query', () => {
const filter = {
operator: eq,
fieldName: ctx.fieldName,
value: { a: 1, b: 2, c: 3 }
}

expect( env.filterParser.parseFilter(filter) ).toEqual([{
filterExpr: `JSON_CONTAINS(${escapeId(ctx.fieldName)}, ?)`,
parameters: [JSON.stringify(filter.value)]
}])
})

test('correctly transform nested field query', () => {
const operator = ctx.filterWithoutInclude.operator
const filter = {
Expand Down
41 changes: 23 additions & 18 deletions libs/external-db-mysql/src/sql_filter_transformer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { errors } from '@wix-velo/velo-external-db-commons'
import { errors, isDate } from '@wix-velo/velo-external-db-commons'
import { AdapterFilter as Filter, NonEmptyAdapterAggregation as Aggregation, AdapterOperator, Sort, NotEmptyAdapterFilter as NotEmptyFilter, AdapterFunctions } from '@wix-velo/velo-external-db-types'
import { EmptyFilter, EmptySort, isObject, AdapterOperators, extractGroupByNames, extractProjectionFunctionsObjects, isEmptyFilter, isNull, specArrayToRegex } from '@wix-velo/velo-external-db-commons'
import { wildCardWith, escapeId } from './mysql_utils'
Expand Down Expand Up @@ -61,6 +61,28 @@ export default class FilterParser implements IMySqlFilterParser {
parameters: !isNull(value) ? [].concat( this.patchTrueFalseValue(value) ) : []
}]
}

if (operator === urlized) {
return [{
filterExpr: `LOWER(${escapeId(fieldName)}) RLIKE ?`,
parameters: [value.map((s: string) => s.toLowerCase()).join('[- ]')]
}]
}

if (operator === matches) {
const ignoreCase = value.ignoreCase ? 'LOWER' : ''
return [{
filterExpr: `${ignoreCase}(${escapeId(fieldName)}) RLIKE ${ignoreCase}(?)`,
parameters: [specArrayToRegex(value.spec)]
}]
}

if (operator === eq && isObject(value) && !isDate(value)) {
return [{
filterExpr: `JSON_CONTAINS(${escapeId(fieldName)}, ?)`,
parameters: [JSON.stringify(value)]
}]
}

if (this.isSingleFieldOperator(operator)) {
return [{
Expand All @@ -75,23 +97,7 @@ export default class FilterParser implements IMySqlFilterParser {
parameters: [this.valueForStringOperator(operator, value)]
}]
}


if (operator === urlized) {
return [{
filterExpr: `LOWER(${escapeId(fieldName)}) RLIKE ?`,
parameters: [value.map((s: string) => s.toLowerCase()).join('[- ]')]
}]
}

if (operator === matches) {
const ignoreCase = value.ignoreCase ? 'LOWER' : ''
return [{
filterExpr: `${ignoreCase}(${escapeId(fieldName)}) RLIKE ${ignoreCase}(?)`,
parameters: [specArrayToRegex(value.spec)]
}]
}

return []
}

Expand All @@ -116,7 +122,6 @@ export default class FilterParser implements IMySqlFilterParser {
} else if ((operator === eq || operator === ne) && isNull(value)) {
return ''
}

return '?'
}

Expand Down
2 changes: 2 additions & 0 deletions libs/external-db-postgres/src/postgres_data_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default class DataProvider implements IDataProvider {
const { sortExpr } = this.filterParser.orderBy(sort)
const projectionExpr = this.filterParser.selectFieldsFor(projection)
const sql = `SELECT ${projectionExpr} FROM ${escapeIdentifier(collectionName)} ${filterExpr} ${sortExpr} OFFSET $${offset} LIMIT $${offset + 1}`
console.log({ sqlExpression: sql, parameters: [...parameters, skip, limit] })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove it pls


const resultSet = await this.pool.query(sql, [...parameters, skip, limit])
.catch( err => translateErrorCodes(err, collectionName) )
return resultSet.rows
Expand Down
18 changes: 17 additions & 1 deletion libs/external-db-postgres/src/sql_filter_transformer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,23 @@ describe('Sql Parser', () => {
})
})

describe('handle queries on nested fields', () => {
describe('handle queries on object fields', () => {
test('correctly transform fully object match query', () => {
const filter = {
operator: eq,
fieldName: ctx.fieldName,
value: { a: 1, b: 2, c: 3 }
}

const parsedFilter = env.filterParser.parseFilter(filter, ctx.offset)

expect( parsedFilter ).toEqual([{
filterExpr: `${escapeIdentifier(ctx.fieldName)}::jsonb @> $${ctx.offset}::jsonb`,
parameters: [JSON.stringify(filter.value)],
filterColumns: [],
offset: ctx.offset + 1,
}])
})
test('correctly transform nested field query', () => {
const operator = ctx.filterWithoutInclude.operator
const filter = {
Expand Down
31 changes: 20 additions & 11 deletions libs/external-db-postgres/src/sql_filter_transformer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NonEmptyAdapterAggregation as Aggregation, AdapterFilter as Filter, AnyFixMe, NotEmptyAdapterFilter as NotEmptyFilter, Sort, AdapterFunctions } from '@wix-velo/velo-external-db-types'
import { errors } from '@wix-velo/velo-external-db-commons'
import { errors, isDate } from '@wix-velo/velo-external-db-commons'
import { EmptyFilter, EmptySort, isObject, AdapterOperators, extractProjectionFunctionsObjects, extractGroupByNames, isEmptyFilter, isNull, specArrayToRegex } from '@wix-velo/velo-external-db-commons'
import { escapeIdentifier } from './postgres_utils'
import { ParsedFilter } from './types'
Expand Down Expand Up @@ -131,6 +131,25 @@ export default class FilterParser {
}]
}

if (operator === matches) {
const ignoreCase = value.ignoreCase ? 'LOWER' : ''
return [{
filterExpr: `${ignoreCase}(${escapeIdentifier(fieldName)}) ~ ${ignoreCase}($${offset})`,
filterColumns: [],
offset: offset + 1,
parameters: [specArrayToRegex(value.spec)]
}]
}

if (operator === eq && isObject(value) && !isDate(value)) {
return [{
filterExpr: `${escapeIdentifier(fieldName)}::jsonb @> $${offset}::jsonb`,
filterColumns: [],
offset: offset + 1,
parameters: [JSON.stringify(value)]
}]
}

if (this.isSingleFieldOperator(operator)) {
const params = this.valueForOperator(value, operator, offset)

Expand All @@ -152,16 +171,6 @@ export default class FilterParser {
}]
}

if (operator === matches) {
const ignoreCase = value.ignoreCase ? 'LOWER' : ''
return [{
filterExpr: `${ignoreCase}(${escapeIdentifier(fieldName)}) ~ ${ignoreCase}($${offset})`,
filterColumns: [],
offset: offset + 1,
parameters: [specArrayToRegex(value.spec)]
}]
}

return []
}

Expand Down
2 changes: 1 addition & 1 deletion libs/external-db-spanner/src/supported_operations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AllSchemaOperations } from '@wix-velo/velo-external-db-commons'
import { SchemaOperations } from '@wix-velo/velo-external-db-types'
//change column types - https://cloud.google.com/spanner/docs/schema-updates#supported_schema_updates
const notSupportedOperations = [SchemaOperations.ChangeColumnType, SchemaOperations.NonAtomicBulkInsert]
const notSupportedOperations = [SchemaOperations.ChangeColumnType, SchemaOperations.NonAtomicBulkInsert, SchemaOperations.FindObject]

export const supportedOperations = AllSchemaOperations.filter(op => !notSupportedOperations.includes(op))
Loading