• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 13390451169

18 Feb 2025 12:22PM CUT coverage: 91.688% (+0.07%) from 91.622%
13390451169

Pull #14647

github

web-flow
Merge 7d79199ab into 10ddb05cf
Pull Request #14647: feat(query-builder): support for nested queries and other improvements

13327 of 15595 branches covered (85.46%)

902 of 976 new or added lines in 19 files covered. (92.42%)

1 existing line in 1 file now uncovered.

26882 of 29319 relevant lines covered (91.69%)

33815.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.78
/projects/igniteui-angular/src/lib/data-operations/expressions-tree-util.ts
1
import { DateTimeUtil } from '../date-common/util/date-time.util';
2
import { EntityType, FieldType } from '../grids/common/grid.interface';
3
import { GridColumnDataType } from './data-util';
4
import { IFilteringOperation, IgxBooleanFilteringOperand, IgxDateFilteringOperand, IgxDateTimeFilteringOperand, IgxFilteringOperand, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxTimeFilteringOperand } from './filtering-condition';
5
import { IFilteringExpression } from './filtering-expression.interface';
6
import { IExpressionTree, IFilteringExpressionsTree } from './filtering-expressions-tree';
7

8
export class ExpressionsTreeUtil {
9
    /**
10
     * Returns the filtering expression for a column with the provided tree and fieldName.
11
     * ```typescript
12
     * let filteringExpression = ExpressionsTreeUtil.find(gridExpressionTree, 'Column Field');
13
     * ```
14
     */
15
    public static find(tree: IFilteringExpressionsTree, fieldName: string): IFilteringExpressionsTree | IFilteringExpression {
16
        const index = this.findIndex(tree, fieldName);
65,354✔
17

18
        if (index > -1) {
65,354✔
19
            return tree.filteringOperands[index];
6,324✔
20
        }
21

22
        return null;
59,030✔
23
    }
24

25
    /**
26
     * Returns the index of the filtering expression for a column with the provided tree and fieldName.
27
     * ```typescript
28
     * let filteringExpressionIndex = ExpressionsTreeUtil.findIndex(gridExpressionTree, 'Column Field');
29
     * ```
30
     */
31
    public static findIndex(tree: IFilteringExpressionsTree, fieldName: string): number {
32
        for (let i = 0; i < tree.filteringOperands.length; i++) {
66,471✔
33
            const expr = tree.filteringOperands[i];
15,635✔
34
            if ((expr as IFilteringExpressionsTree).operator !== undefined) {
15,635!
35
                if (this.isFilteringExpressionsTreeForColumn(expr as IFilteringExpressionsTree, fieldName)) {
15,635✔
36
                    return i;
6,550✔
37
                }
38
            } else if ((expr as IFilteringExpression).fieldName === fieldName) {
×
39
                return i;
×
40
            }
41
        }
42

43
        return -1;
59,921✔
44
    }
45

46
    protected static isFilteringExpressionsTreeForColumn(expressionsTree: IFilteringExpressionsTree, fieldName: string): boolean {
47
        if (expressionsTree.fieldName === fieldName) {
15,811✔
48
            return true;
6,550✔
49
        }
50

51
        for (const expr of expressionsTree.filteringOperands) {
9,261✔
52
            if ((expr as IFilteringExpressionsTree).operator !== undefined) {
12,048✔
53
                return this.isFilteringExpressionsTreeForColumn(expr as IFilteringExpressionsTree, fieldName);
176✔
54
            } else if ((expr as IFilteringExpression).fieldName === fieldName) {
11,872!
55
                return true;
×
56
            }
57
        }
58
        return false;
9,085✔
59
    }
60
}
61

62
/**
63
 * Recreates the search value for a given expression.
64
 * @param searchValue The search value to recreate.
65
 * @param dataType The data type of the field.
66
 * @returns The recreated search value.
67
 */
68
function recreateSearchValue(searchValue: any, dataType: string): any {
69
    if (!dataType && !Array.isArray(searchValue)) {
1,861✔
70
        return searchValue;
549✔
71
    }
72
    // In ESF, values are stored as a Set.
73
    // Those values are converted to an array before returning string in the stringifyCallback
74
    // now we need to convert those back to Set
75
    if (Array.isArray(searchValue)) {
1,312✔
76
        return new Set(searchValue);
2✔
77
    } else if ((dataType.toLowerCase().includes('date') || dataType.toLowerCase().includes('time')) && !(searchValue instanceof Date)) {
1,310✔
78
        return DateTimeUtil.parseIsoDate(searchValue) ?? searchValue;
112✔
79
    }
80

81
    return searchValue;
1,198✔
82
}
83

84
/**
85
 * Returns the filtering logic function for a given dataType and condition (contains, greaterThan, etc.)
86
 * @param dataType The data type of the field.
87
 * @param name The name of the filtering condition.
88
 * @returns The filtering logic function.
89
 */
90
function getFilteringCondition(dataType: string, name: string): IFilteringOperation {
91
    let filters: IgxFilteringOperand;
92
    switch (dataType) {
25✔
93
        case GridColumnDataType.Boolean:
94
            filters = IgxBooleanFilteringOperand.instance();
4✔
95
            break;
4✔
96
        case GridColumnDataType.Number:
97
        case GridColumnDataType.Currency:
98
        case GridColumnDataType.Percent:
99
            filters = IgxNumberFilteringOperand.instance();
8✔
100
            break;
8✔
101
        case GridColumnDataType.Date:
102
            filters = IgxDateFilteringOperand.instance();
3✔
103
            break;
3✔
104
        case GridColumnDataType.Time:
105
            filters = IgxTimeFilteringOperand.instance();
2✔
106
            break;
2✔
107
        case GridColumnDataType.DateTime:
108
            filters = IgxDateTimeFilteringOperand.instance();
3✔
109
            break;
3✔
110
        case GridColumnDataType.String:
111
        default:
112
            filters = IgxStringFilteringOperand.instance();
5✔
113
            break;
5✔
114
    }
115
    return filters.condition(name);
25✔
116
}
117

118
/**
119
 * Recreates the IFilteringOperation for a given expression.
120
 * If the `logic` is already populated - it will return the original IFilteringOperation
121
 * of the expression.
122
 * @param expression The expression for which to resolve the IFilteringOperation.
123
 * @param dataType The data type of the field.
124
 * @returns The IFilteringOperation for the given expression.
125
 */
126
function recreateOperatorFromDataType(expression: IFilteringExpression, dataType: string): IFilteringOperation {
127
    if (!expression.condition?.logic) {
25✔
128
        return getFilteringCondition(dataType, expression.conditionName || expression.condition?.name);
25✔
129
    }
130

NEW
131
    return expression.condition;
×
132
}
133

134
/**
135
 * Recreates an expression from the given fields by applying the correct operands
136
 * and adjusting the search value to be the correct type.
137
 * @param expression The expression to recreate.
138
 * @param fields An array of fields to use for recreating the expression.
139
 * @returns The recreated expression.
140
 */
141
function recreateExpression(expression: IFilteringExpression, fields: FieldType[]): IFilteringExpression {
142
    const field = fields?.find(f => f.field === expression.fieldName);
3,519✔
143

144
    if (field && !expression.condition?.logic) {
1,861✔
145
        if (!field.filters) {
74✔
146
            expression.condition = recreateOperatorFromDataType(expression, field.dataType);
25✔
147
        } else {
148
            expression.condition = field.filters.condition(expression.conditionName || expression.condition?.name);
49!
149
        }
150
    }
151

152
    if (!expression.condition && expression.conditionName) {
1,861!
NEW
153
        throw Error('Wrong `conditionName`, `condition` or `field` provided!');
×
154
    }
155

156
    if (!expression.conditionName) {
1,861✔
157
        expression.conditionName = expression.condition?.name;
48✔
158
    }
159

160
    expression.searchVal = recreateSearchValue(expression.searchVal, field?.dataType);
1,861✔
161

162
    return expression;
1,861✔
163
}
164

165
/**
166
 * Checks if the given entry is an IExpressionTree.
167
 * @param entry The entry to check.
168
 * @returns True if the entry is an IExpressionTree, false otherwise.
169
 */
170
export function isTree(entry: IExpressionTree | IFilteringExpression): entry is IExpressionTree {
171
    return 'operator' in entry;
28,026✔
172
}
173

174
/**
175
 * Recreates the tree from a given array of entities by applying the correct operands
176
 * for each expression and adjusting the search values to be the correct type.
177
 * @param tree The expression tree to recreate.
178
 * @param entities An array of entities to use for recreating the tree.
179
 * @returns The recreated expression tree.
180
 */
181
export function recreateTree(tree: IExpressionTree, entities: EntityType[]): IExpressionTree {
182
    const entity = entities.find(e => e.name === tree.entity);
577✔
183

184
    for (let i = 0; i < tree.filteringOperands.length; i++) {
412✔
185
        const operand = tree.filteringOperands[i];
1,092✔
186
        if (isTree(operand)) {
1,092✔
187
            tree.filteringOperands[i] = recreateTree(operand, entities);
61✔
188
        } else {
189
            if (operand.searchTree) {
1,031✔
190
                operand.searchTree = recreateTree(operand.searchTree, entities);
94✔
191
            }
192
            tree.filteringOperands[i] = recreateExpression(operand, entity?.fields);
1,031✔
193
        }
194
    }
195

196
    return tree;
412✔
197
}
198

199
/**
200
 * Recreates the tree from a given array of fields by applying the correct operands.
201
 * It is recommended to use `recreateTree` if there will be multiple entities in the tree
202
 * with potentially colliding field names.
203
 * @param tree The expression tree to recreate.
204
 * @param fields An array of fields to use for recreating the tree.
205
 */
206
export function recreateTreeFromFields(tree: IExpressionTree, fields: FieldType[]): IExpressionTree {
207
    for (let i = 0; i < tree.filteringOperands.length; i++) {
9,689✔
208
        const operand = tree.filteringOperands[i];
1,373✔
209
        if (isTree(operand)) {
1,373✔
210
            tree.filteringOperands[i] = recreateTreeFromFields(operand, fields);
543✔
211
        } else {
212
            if (operand.searchTree) {
830!
NEW
213
                operand.searchTree = recreateTreeFromFields(operand.searchTree, fields);
×
214
            }
215
            tree.filteringOperands[i] = recreateExpression(operand, fields);
830✔
216
        }
217
    }
218

219
    return tree;
9,689✔
220
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc