diff --git a/packages/db-postgres/src/queries/parseParams.ts b/packages/db-postgres/src/queries/parseParams.ts index a1e6184090..700b9b0871 100644 --- a/packages/db-postgres/src/queries/parseParams.ts +++ b/packages/db-postgres/src/queries/parseParams.ts @@ -110,8 +110,7 @@ export async function parseParams({ if ( ['json', 'richText'].includes(field.type) && Array.isArray(pathSegments) && - pathSegments.length > 1 && - !['exists'].includes(operator) + pathSegments.length > 1 ) { const segments = pathSegments.slice(1) segments.unshift(table[columnName].name) @@ -126,12 +125,28 @@ export async function parseParams({ }) constraints.push(sql.raw(jsonQuery)) + break } - if (field.type === 'json') { - const jsonQuery = convertPathToJSONTraversal(pathSegments) - constraints.push(sql.raw(`${table[columnName].name}${jsonQuery} = '%${val}%'`)) + const jsonQuery = convertPathToJSONTraversal(pathSegments) + const operatorKeys = { + contains: { operator: 'ilike', wildcard: '%' }, + equals: { operator: '=', wildcard: '' }, + exists: { operator: val === true ? 'is not null' : 'is null' }, + like: { operator: 'like', wildcard: '%' }, + not_equals: { operator: '<>', wildcard: '' }, } + let formattedValue = `'${operatorKeys[operator].wildcard}${val}${operatorKeys[operator].wildcard}'` + + if (operator === 'exists') { + formattedValue = '' + } + + constraints.push( + sql.raw( + `${table[columnName].name}${jsonQuery} ${operatorKeys[operator].operator} ${formattedValue}`, + ), + ) break } diff --git a/test/fields/int.spec.ts b/test/fields/int.spec.ts index 0f0813b3d9..b6e0eff575 100644 --- a/test/fields/int.spec.ts +++ b/test/fields/int.spec.ts @@ -658,7 +658,12 @@ describe('Fields', () => { id, collection, locale: 'all', - })) as unknown as { localized: { en: unknown; es: unknown } } + })) as unknown as { + localized: { + en: unknown + es: unknown + } + } expect(enDoc.localized[0].text).toStrictEqual(enText) expect(esDoc.localized[0].text).toStrictEqual(esText) @@ -989,22 +994,86 @@ describe('Fields', () => { let fooBar let bazBar - beforeAll(async () => { + beforeEach(async () => { fooBar = await payload.create({ collection: 'json-fields', data: { - json: { foo: 'bar' }, + json: { foo: 'foobar', number: 5 }, }, }) bazBar = await payload.create({ collection: 'json-fields', data: { - json: { baz: 'bar' }, + json: { baz: 'bar', number: 10 }, }, }) }) - it('should query exists', async () => { + it('should query nested properties - like', async () => { + const { docs } = await payload.find({ + collection: 'json-fields', + where: { + 'json.foo': { like: 'bar' }, + }, + }) + + const docIDs = docs.map(({ id }) => id) + + expect(docIDs).toContain(fooBar.id) + expect(docIDs).not.toContain(bazBar.id) + }) + + it('should query nested properties - equals', async () => { + const { docs } = await payload.find({ + collection: 'json-fields', + where: { + 'json.foo': { equals: 'foobar' }, + }, + }) + + const notEquals = await payload.find({ + collection: 'json-fields', + where: { + 'json.foo': { equals: 'bar' }, + }, + }) + + const docIDs = docs.map(({ id }) => id) + + expect(docIDs).toContain(fooBar.id) + expect(docIDs).not.toContain(bazBar.id) + expect(notEquals.docs).toHaveLength(0) + }) + + it('should query nested numbers - equals', async () => { + const { docs } = await payload.find({ + collection: 'json-fields', + where: { + 'json.number': { equals: 5 }, + }, + }) + + const docIDs = docs.map(({ id }) => id) + + expect(docIDs).toContain(fooBar.id) + expect(docIDs).not.toContain(bazBar.id) + }) + + it('should query nested properties - exists', async () => { + const { docs } = await payload.find({ + collection: 'json-fields', + where: { + 'json.foo': { exists: true }, + }, + }) + + const docIDs = docs.map(({ id }) => id) + + expect(docIDs).toContain(fooBar.id) + expect(docIDs).not.toContain(bazBar.id) + }) + + it('should query - exists', async () => { const nullJSON = await payload.create({ collection: 'json-fields', data: {},