Skip to content

Commit a6097dd

Browse files
authored
Merge branch 'vnext' into apetrov/add-list-select-vnext
2 parents 966d42d + 6c3548c commit a6097dd

2 files changed

Lines changed: 237 additions & 0 deletions

File tree

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
---
2+
title: Using the Query Builder Model
3+
_description: Angular Query Builder provides a serializable/deserializable JSON format model, making it easy to build SQL queries. Try it now.
4+
_keywords: Angular Query Builder component, Angular Query Builder control, Ignite UI for Angular, UI controls, Angular widgets, web widgets, UI widgets, Angular, Native Angular Components Suite, Angular UI Components, Native Angular Components Library
5+
---
6+
7+
# Using the Query Builder Model
8+
Angular Query Builder provides a serializable/deserializable JSON format model, making it easy to build SQL queries.
9+
10+
## Overview
11+
12+
This Angular Query Builder example demonstartes how the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) expression tree could be used to request data from an endpoint [Northwind WebAPI](https://data-northwind.indigo.design/swagger/index.html) and set it as an [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html) data source.
13+
14+
<code-view style="height:700px"
15+
no-theming
16+
data-demos-base-url="{environment:demosBaseUrl}"
17+
iframe-src="{environment:demosBaseUrl}/interactions/query-builder-request-sample" >
18+
</code-view>
19+
20+
## Query Builder Model
21+
In order to set an expression tree to the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html), you need to define a[`FilteringExpressionsTree`]({environment:angularApiUrl}/classes/filteringexpressionstree.html). Each [`FilteringExpressionsTree`]({environment:angularApiUrl}/classes/filteringexpressionstree.html) should have filtering logic that represents how a data record should resolve against the tree and depending on the use case, you could pass a field name, entity name, and an array of return fields. If all fields in a certain entity should be returned, the `returnFields` property could be set to ['*']:
22+
23+
```ts
24+
const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Entity A', ['*']);
25+
```
26+
Once the root [`FilteringExpressionsTree`]({environment:angularApiUrl}/classes/filteringexpressionstree.html) is created, adding conditions, groups or subqueries, could be done by setting its `filteringOperands` property to an array of [`IFilteringExpression`]({environment:angularApiUrl}/interfaces/ifilteringexpression.html) (single expression or a group) or [`IFilteringExpressionsTree`]({environment:angularApiUrl}/interfaces/ifilteringexpressionstree.html) (subquery).
27+
Each [`IFilteringExpression`]({environment:angularApiUrl}/interfaces/ifilteringexpression.html) and [`IFilteringExpressionsTree`]({environment:angularApiUrl}/interfaces/ifilteringexpressionstree.html) should have a `fieldName` that is the name of the column where the filtering expression is placed, and either a `condition` of type [`IFilteringOperation`]({environment:angularApiUrl}/interfaces/ifilteringoperation.html) or a `conditionName`. If required, you could also set a `searchVal`, `searchTree` of type [`IExpressionTree`]({environment:angularApiUrl}/interfaces/iexpressiontree.html), and `ignoreCase` properties.
28+
29+
- Defining a simple **expression**:
30+
```ts
31+
tree.filteringOperands.push({
32+
fieldName: 'Name',
33+
conditionName: IgxStringFilteringOperand.instance().condition('endsWith').name,
34+
searchVal: 'a'
35+
});
36+
```
37+
38+
- Defining a **group** of expressions:
39+
```ts
40+
const group = new FilteringExpressionsTree(FilteringLogic.Or, undefined, 'Entity A', ['*']);
41+
group.filteringOperands.push({
42+
fieldName: 'Name',
43+
conditionName: IgxStringFilteringOperand.instance().condition('endsWith').name,
44+
searchVal: 'a'
45+
});
46+
group.filteringOperands.push({
47+
fieldName: 'DateTime created',
48+
conditionName: IgxDateFilteringOperand.instance().condition('today').name
49+
});
50+
tree.filteringOperands.push(group);
51+
```
52+
53+
- Defining a **subquery**:
54+
```ts
55+
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Entity B', ['Number']);
56+
innerTree.filteringOperands.push({
57+
fieldName: 'Number',
58+
conditionName: 'equals',
59+
searchVal: 123
60+
});
61+
tree.filteringOperands.push({
62+
fieldName: 'Id',
63+
conditionName: 'inQuery',
64+
searchTree: innerTree
65+
});
66+
```
67+
68+
The model can be serialized/deserialized in JSON format, making it easily transferable between client and server:
69+
```ts
70+
JSON.stringify(tree, null, 2);
71+
```
72+
73+
## Using Sub-Queries
74+
75+
In the context of the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) the *IN / NOT-IN* operators are used with the newly exposed subquery functionality in the *WHERE* clause.
76+
77+
> [!Note]
78+
> A subquery is a query nested inside another query used to retrieve data that will be used as a condition for the outer query.
79+
80+
Selecting the *IN / NOT-IN* operator in a `FilteringExpression` would create a subquery. After choosing an entity and a column to return, it checks if the value in the specified column in the outer query matches or not any of the values returned by the subquery.
81+
82+
The following expression tree:
83+
```ts
84+
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Products', ['supplierId']);
85+
innerTree.filteringOperands.push({
86+
fieldName: 'supplierId',
87+
conditionName: IgxNumberFilteringOperand.instance().condition('greaterThan').name,
88+
searchVal: 10
89+
});
90+
91+
const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Suppliers', ['supplierId']);
92+
tree.filteringOperands.push({
93+
fieldName: 'supplierId',
94+
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
95+
searchTree: innerTree
96+
});
97+
```
98+
Could be serialized by calling:
99+
```ts
100+
JSON.stringify(tree, null, 2);
101+
```
102+
103+
This would be transferred as:
104+
```
105+
{
106+
"filteringOperands": [
107+
{
108+
"fieldName": "supplierId",
109+
"condition": {
110+
"name": "inQuery",
111+
"isUnary": false,
112+
"isNestedQuery": true,
113+
"iconName": "in"
114+
},
115+
"conditionName": "inQuery",
116+
"searchVal": null,
117+
"searchTree": {
118+
"filteringOperands": [
119+
{
120+
"fieldName": "supplierId",
121+
"condition": {
122+
"name": "greaterThan",
123+
"isUnary": false,
124+
"iconName": "filter_greater_than"
125+
},
126+
"conditionName": "greaterThan",
127+
"searchVal": 10,
128+
"searchTree": null
129+
}
130+
],
131+
"operator": 0,
132+
"entity": "Suppliers",
133+
"returnFields": [
134+
"supplierId"
135+
]
136+
}
137+
}
138+
],
139+
"operator": 0,
140+
"entity": "Products",
141+
"returnFields": [
142+
"supplierId"
143+
]
144+
}
145+
```
146+
147+
## SQL Example
148+
149+
Let's take a look at a practical example of how the Ignite UI for Angular Query Builder Component can be used to build SQL queries.
150+
151+
In the sample below we have 3 `entities` with names 'Suppliers', 'Categories' and 'Products'.
152+
153+
Let's say we want to find all suppliers who supply products which belong to the 'Beverages' category. Since the data is distributed across all entities, we can take advantage of the *IN* operator and accomplish the task by creating subqueries. Each subquery is represented by a `FilteringExpressionsTree` and can be converted to a SQL query through the `transformExpressionTreeToSqlQuery(tree: IExpressionTree)` method.
154+
155+
First, we create а `categoriesTree` which will return the `categoryId` for the record where `name` is `Beverages`. This is the innermost subquery:
156+
157+
```ts
158+
const categoriesTree = new FilteringExpressionsTree(0, undefined, 'Categories', ['categoryId']);
159+
categoriesTree.filteringOperands.push({
160+
fieldName: 'name',
161+
conditionName: IgxStringFilteringOperand.instance().condition('equals').name,
162+
searchVal: 'Beverages'
163+
});
164+
```
165+
166+
The corresponding SQL query for this `FilteringExpressionsTree` will look like this:
167+
168+
```
169+
SELECT categoryId FROM Categories WHERE name = 'Beverages'
170+
```
171+
172+
Then we create а `productsTree` that will return the `supplierId` field from the `categoriesTree` for the records where the `categoryId` matches the `categoryId` returned by the innermost subquery. We do this by setting the `inQuery` condition and the relevant `searchTree`. This is the middle subquery:
173+
174+
```ts
175+
const productsTree = new FilteringExpressionsTree(0, undefined, 'Products', ['supplierId']);
176+
productsTree.filteringOperands.push({
177+
fieldName: 'categoryId',
178+
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
179+
searchTree: categoriesTree
180+
});
181+
```
182+
183+
This is the updated state of the SQL query:
184+
185+
```
186+
SELECT supplierId FROM Products WHERE categoryId IN (
187+
SELECT categoryId FROM Categories WHERE name = 'Beverages'
188+
)
189+
```
190+
191+
Finally, we create а `suppliersTree` that will return all fields from `Suppliers` entity where the `supplierId` matches any of the `supplierId`s returned by the middle subquery. This is the outermost query:
192+
193+
```ts
194+
const suppliersTree = new FilteringExpressionsTree(0, undefined, 'Suppliers', ['*']);
195+
suppliersTree.filteringOperands.push({
196+
fieldName: 'supplierId',
197+
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
198+
searchTree: productsTree
199+
});
200+
```
201+
202+
Our SQL query is now complete:
203+
204+
```
205+
SELECT * FROM Suppliers WHERE supplierId IN (
206+
SELECT supplierId FROM Products WHERE categoryId IN (
207+
SELECT categoryId FROM Categories WHERE name = 'Beverages'
208+
)
209+
)
210+
```
211+
212+
Now we can set the `expressionsTree` property of the `IgxQueryBuilderComponent` to `suppliersTree`. Furthermore, every change to the query triggers a new request to the endpoint and the resulting data shown in the grid is refreshed.
213+
214+
<code-view style="height:700px"
215+
no-theming
216+
data-demos-base-url="{environment:demosBaseUrl}"
217+
iframe-src="{environment:demosBaseUrl}/interactions/query-builder-sql-sample" >
218+
</code-view>
219+
220+
## API References
221+
222+
<div class="divider--half"></div>
223+
224+
* [IgxQueryBuilderComponent API]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html)
225+
* [IgxQueryBuilderComponent Styles]({environment:sassApiUrl}/index.html#function-query-builder-theme)
226+
227+
## Additional Resources
228+
229+
<div class="divider--half"></div>
230+
Our community is active and always welcoming to new ideas.
231+
232+
* [Ignite UI for Angular **Forums**](https://www.infragistics.com/community/forums/f/ignite-ui-for-angular)
233+
* [Ignite UI for Angular **GitHub**](https://github.com/IgniteUI/igniteui-angular)

en/components/toc.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,10 @@
988988
href: query-builder.md
989989
new: false
990990
updated: true
991+
items:
992+
- name: Using Query Builder Model
993+
href: query-builder-model.md
994+
new: true
991995
- name: Radio & Radio Group
992996
href: radio-button.md
993997
new: false

0 commit comments

Comments
 (0)