From 0f831ab20127e53d584637cb7d05d42292dfe3dc Mon Sep 17 00:00:00 2001 From: oxdev03 <140103378+oxdev03@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:16:00 +0000 Subject: [PATCH 1/4] feat: add filtering for array fields in tests and enhance iterate function for plain arrays --- README.md | 3 ++ src/tests/filter/filter.test.ts | 51 +++++++++++++++++++++++++++++++++ src/tests/utils/iterate.test.ts | 19 ++++++++++++ src/utils/iterate.ts | 8 ++++++ 4 files changed, 81 insertions(+) diff --git a/README.md b/README.md index 46e0a25..d818305 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,9 @@ sentence:word # Search for 'word' in the object properties starting with 's' and any nested property, e.g., [{s: {a: 'word'}}] s*:word +# Search for 'word' in array e.g. [{ s: ['word','number'] }] +s:word + # Search for 'word' in the object by nested key 's.a' s.a:word diff --git a/src/tests/filter/filter.test.ts b/src/tests/filter/filter.test.ts index 8c9868d..8bb1b4e 100644 --- a/src/tests/filter/filter.test.ts +++ b/src/tests/filter/filter.test.ts @@ -122,4 +122,55 @@ describe(`filter`, () => { expect(result).toEqual(data.filter((d) => d._id == 13)); }); }); + + describe('filter array fields', () => { + const data = [ + { id: 1, tags: ['javascript', 'typescript'] }, + { id: 2, tags: ['python', 'typescript'] }, + { id: 3, tags: ['java'] }, + { id: 4, tags: [] }, + { id: 5 } + ]; + + it('should filter by array value', () => { + const result = filter(new QueryParser('tags:typescript'), data); + expect(result).toEqual([ + { id: 1, tags: ['javascript', 'typescript'] }, + { id: 2, tags: ['python', 'typescript'] } + ]); + }); + + it('should handle nested array fields', () => { + const nestedData = [ + { + id: 1, + nested: { + tags: ['javascript', 'typescript'] + } + }, + { + id: 2, + nested: { + tags: ['python'] + } + }, + { + id: 3, + nested: { + other: ['something'] + } + } + ]; + + const result = filter(new QueryParser('nested.tags:typescript'), nestedData); + expect(result).toEqual([ + { + id: 1, + nested: { + tags: ['javascript', 'typescript'] + } + } + ]); + }); + }); }); diff --git a/src/tests/utils/iterate.test.ts b/src/tests/utils/iterate.test.ts index c7a7fac..4a63f36 100644 --- a/src/tests/utils/iterate.test.ts +++ b/src/tests/utils/iterate.test.ts @@ -372,6 +372,25 @@ describe('iterate', () => { `); }); + it('should handle object with plain array', () => { + const someObj = { + tags: ['tagA', 'tagB'], + }; + const result = [...iterate(someObj, 'tags')]; + expect(result).toMatchInlineSnapshot(` + [ + [ + "tags.0", + "tagA", + ], + [ + "tags.1", + "tagB", + ], + ] + `); + }); + describe('iterate private fields feature', () => { const objWithPrivateFields = { private: { diff --git a/src/utils/iterate.ts b/src/utils/iterate.ts index 0b76bcf..9d7e899 100644 --- a/src/utils/iterate.ts +++ b/src/utils/iterate.ts @@ -41,6 +41,14 @@ export default function* iterate( // Check if the object is iterable and not in the NOT_ITERABLE list if (typeof obj === 'object' && obj !== null && !NOT_ITERABLE.some((cls) => obj instanceof cls)) { + // Handle plain arrays directly if we're at the target field and not using a wildcard + if (Array.isArray(obj) && currentPath.length === splittedFields.length && !isTrailingWildcard && field) { + for (let i = 0; i < obj.length; i++) { + yield [[...currentPath, i].join('.'), obj[i]]; + } + return; + } + // Check if the object is an array with elements having the current field as a key const arrayWithInnerKey = Array.isArray(obj) && obj.some((o) => objectHasField(o, currentField, checkPrivate)); const objWithCurrentField = objectHasField(obj, currentField, checkPrivate); From 1be991451327c6d0c64d198851a690f43bc55f9b Mon Sep 17 00:00:00 2001 From: oxdev03 <140103378+oxdev03@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:18:23 +0000 Subject: [PATCH 2/4] chore: use all query variants --- src/tests/filter/filter.test.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/tests/filter/filter.test.ts b/src/tests/filter/filter.test.ts index 8bb1b4e..75d6b92 100644 --- a/src/tests/filter/filter.test.ts +++ b/src/tests/filter/filter.test.ts @@ -162,15 +162,24 @@ describe(`filter`, () => { } ]; - const result = filter(new QueryParser('nested.tags:typescript'), nestedData); - expect(result).toEqual([ - { - id: 1, - nested: { - tags: ['javascript', 'typescript'] - } + const queries = [ + 'nested.tags:typescript', // explicit field path + '*.tags:typescript', // wildcard prefix + 'nested.tags*:typescript', // wildcard suffix + '*.tags*:typescript' // wildcard on both sides + ]; + + for (const query of queries) { + const result = filter(new QueryParser(query), nestedData); + expect(result).toEqual([ + { + id: 1, + nested: { + tags: ['javascript', 'typescript'] } - ]); + } + ]); + } }); }); }); From fd4319120283fc8cacad35859e8702f6318f8e09 Mon Sep 17 00:00:00 2001 From: oxdev03 <140103378+oxdev03@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:20:11 +0000 Subject: [PATCH 3/4] chore: format file --- src/tests/filter/filter.test.ts | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/tests/filter/filter.test.ts b/src/tests/filter/filter.test.ts index 75d6b92..098784d 100644 --- a/src/tests/filter/filter.test.ts +++ b/src/tests/filter/filter.test.ts @@ -129,55 +129,55 @@ describe(`filter`, () => { { id: 2, tags: ['python', 'typescript'] }, { id: 3, tags: ['java'] }, { id: 4, tags: [] }, - { id: 5 } + { id: 5 }, ]; it('should filter by array value', () => { const result = filter(new QueryParser('tags:typescript'), data); expect(result).toEqual([ { id: 1, tags: ['javascript', 'typescript'] }, - { id: 2, tags: ['python', 'typescript'] } + { id: 2, tags: ['python', 'typescript'] }, ]); }); it('should handle nested array fields', () => { const nestedData = [ - { + { id: 1, nested: { - tags: ['javascript', 'typescript'] - } + tags: ['javascript', 'typescript'], + }, }, - { + { id: 2, nested: { - tags: ['python'] - } + tags: ['python'], + }, }, - { + { id: 3, nested: { - other: ['something'] - } - } + other: ['something'], + }, + }, ]; const queries = [ - 'nested.tags:typescript', // explicit field path - '*.tags:typescript', // wildcard prefix - 'nested.tags*:typescript', // wildcard suffix - '*.tags*:typescript' // wildcard on both sides + 'nested.tags:typescript', // explicit field path + '*.tags:typescript', // wildcard prefix + 'nested.tags*:typescript', // wildcard suffix + '*.tags*:typescript', // wildcard on both sides ]; for (const query of queries) { const result = filter(new QueryParser(query), nestedData); expect(result).toEqual([ - { - id: 1, - nested: { - tags: ['javascript', 'typescript'] - } - } + { + id: 1, + nested: { + tags: ['javascript', 'typescript'], + }, + }, ]); } }); From cb7e972641c7578bd23129ce224f2944a4f83051 Mon Sep 17 00:00:00 2001 From: oxdev03 <140103378+oxdev03@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:22:24 +0000 Subject: [PATCH 4/4] chore: fix lint findings --- src/utils/iterate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/iterate.ts b/src/utils/iterate.ts index 9d7e899..cd7c25a 100644 --- a/src/utils/iterate.ts +++ b/src/utils/iterate.ts @@ -43,8 +43,8 @@ export default function* iterate( if (typeof obj === 'object' && obj !== null && !NOT_ITERABLE.some((cls) => obj instanceof cls)) { // Handle plain arrays directly if we're at the target field and not using a wildcard if (Array.isArray(obj) && currentPath.length === splittedFields.length && !isTrailingWildcard && field) { - for (let i = 0; i < obj.length; i++) { - yield [[...currentPath, i].join('.'), obj[i]]; + for (const [i, element] of obj.entries()) { + yield [[...currentPath, i].join('.'), element]; } return; }