mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-30 10:14:53 -05:00
fix tag matching of filter and make hasTags op case-sensitive (#5386)
Co-authored-by: youngcw <calebyoung94@gmail.com>
This commit is contained in:
@@ -329,6 +329,10 @@ const CONDITION_TYPES = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op === 'hasTags') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
return value.toLowerCase();
|
return value.toLowerCase();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -390,30 +390,35 @@ export function conditionsToAQL(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apply = (field, op, value) => {
|
const apply = (field, aqlOp, value) => {
|
||||||
if (type === 'number') {
|
if (type === 'number') {
|
||||||
if (options) {
|
if (options) {
|
||||||
if (options.outflow) {
|
if (options.outflow) {
|
||||||
return {
|
return {
|
||||||
$and: [
|
$and: [
|
||||||
{ amount: { $lt: 0 } },
|
{ amount: { $lt: 0 } },
|
||||||
{ [field]: { $transform: '$neg', [op]: value } },
|
{ [field]: { $transform: '$neg', [aqlOp]: value } },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
} else if (options.inflow) {
|
} else if (options.inflow) {
|
||||||
return {
|
return {
|
||||||
$and: [{ amount: { $gt: 0 } }, { [field]: { [op]: value } }],
|
$and: [{ amount: { $gt: 0 } }, { [field]: { [aqlOp]: value } }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { amount: { [op]: value } };
|
return { amount: { [aqlOp]: value } };
|
||||||
} else if (type === 'string') {
|
} else if (type === 'string') {
|
||||||
return { [field]: { $transform: '$lower', [op]: value } };
|
return {
|
||||||
|
[field]: {
|
||||||
|
$transform: op !== 'hasTags' ? '$lower' : undefined,
|
||||||
|
[aqlOp]: value,
|
||||||
|
},
|
||||||
|
};
|
||||||
} else if (type === 'date') {
|
} else if (type === 'date') {
|
||||||
return { [field]: { [op]: value.date } };
|
return { [field]: { [aqlOp]: value.date } };
|
||||||
}
|
}
|
||||||
return { [field]: { [op]: value } };
|
return { [field]: { [aqlOp]: value } };
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
@@ -530,24 +535,17 @@ export function conditionsToAQL(
|
|||||||
return { $or: values.map(v => apply(field, '$eq', v)) };
|
return { $or: values.map(v => apply(field, '$eq', v)) };
|
||||||
|
|
||||||
case 'hasTags':
|
case 'hasTags':
|
||||||
const words = value.split(/\s+/);
|
|
||||||
const tagValues = [];
|
const tagValues = [];
|
||||||
words.forEach(word => {
|
for (const [_, tag] of value.matchAll(/(?<!#)(#[^#\s]+)/g)) {
|
||||||
const startsWithHash = word.startsWith('#');
|
if (!tagValues.find(t => t.tag === tag)) {
|
||||||
const containsMultipleHash = word.slice(1).includes('#');
|
tagValues.push(tag);
|
||||||
const correctlyFormatted = word.match(/#[\w\d\p{Emoji}-]+/gu);
|
|
||||||
const validHashtag =
|
|
||||||
startsWithHash && !containsMultipleHash && correctlyFormatted;
|
|
||||||
|
|
||||||
if (validHashtag) {
|
|
||||||
tagValues.push(word);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
$and: tagValues.map(v => {
|
$and: tagValues.map(v => {
|
||||||
const regex = new RegExp(
|
const regex = new RegExp(
|
||||||
`(^|\\s)${v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\s|$)`,
|
`(?<!#)${v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([\\s#]|$)`,
|
||||||
);
|
);
|
||||||
return apply(field, '$regexp', regex.source);
|
return apply(field, '$regexp', regex.source);
|
||||||
}),
|
}),
|
||||||
|
|||||||
6
upcoming-release-notes/5386.md
Normal file
6
upcoming-release-notes/5386.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Enhancements
|
||||||
|
authors: [pogman-code]
|
||||||
|
---
|
||||||
|
|
||||||
|
Consolidate `has tags` filter
|
||||||
Reference in New Issue
Block a user