Skip to content
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
60 changes: 60 additions & 0 deletions src/tests/filter/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,64 @@ 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 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'],
},
},
]);
}
});
});
});
19 changes: 19 additions & 0 deletions src/tests/utils/iterate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
8 changes: 8 additions & 0 deletions src/utils/iterate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (const [i, element] of obj.entries()) {
yield [[...currentPath, i].join('.'), element];
}
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);
Expand Down