Skip to content
This repository was archived by the owner on May 19, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2a447c3
feat(query-builder): add initial draft Query BUilde Model topic
teodosiah Mar 10, 2025
5dbfa74
chore(*): add sql sample code view
igdmdimitrov Mar 11, 2025
1219969
docs(query-builder): fix code-snippets in Sub-Query section
teodosiah Mar 12, 2025
fb17cdf
feat(query-builder): add SQL Example section
igdmdimitrov Mar 12, 2025
251ed94
Merge branch 'thristodorova/feat-6054' of https://github.com/IgniteUI…
igdmdimitrov Mar 12, 2025
1f20c4c
chore(*): address PR comments
igdmdimitrov Mar 13, 2025
3184c06
docs(query-builder): remove unnecessary section
teodosiah Mar 18, 2025
2629652
Merge branch 'vnext' into thristodorova/feat-6054
teodosiah Mar 18, 2025
b1fe366
Update en/components/query-builder-model.md
teodosiah Mar 18, 2025
ec6281c
docs(query-builder): add Request data from an Endpoint section
teodosiah Mar 20, 2025
26c30a6
docs(query-builder): update sections
teodosiah Mar 21, 2025
a4dc866
Update en/components/query-builder-model.md
teodosiah Mar 21, 2025
3dca0b7
Update en/components/query-builder-model.md
teodosiah Mar 21, 2025
56742c0
Update en/components/query-builder-model.md
teodosiah Mar 21, 2025
feee57b
Merge branch 'vnext' into thristodorova/feat-6054
teodosiah Mar 21, 2025
627afcc
feat(query-builder): update text and code snippets
igdmdimitrov Mar 24, 2025
69c28c7
Merge branch 'vnext' into thristodorova/feat-6054
gedinakova Mar 26, 2025
e8b4d3b
Merge branch 'vnext' into thristodorova/feat-6054
ChronosSF Mar 28, 2025
904820f
docs(query-builder): add requested changes
teodosiah Mar 31, 2025
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
243 changes: 243 additions & 0 deletions en/components/query-builder-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
---
title: Using Query Builder Model
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: Using Query Builder Model
title: Using the Query Builder Model

_description: Angular Query Builder provides easily serializable/deserializable JSON format model, making it easily to build SQL queries. Try it Now.
Comment thread
teodosiah marked this conversation as resolved.
Outdated
_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
---

## Query Builder Model
In order to set an expression tree to the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) you need to define [`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 field name, entity name and an array of return fields. If all fields in certain entity should be returned, the `returnFields` property could be set to ['*']:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In order to set an expression tree to the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) you need to define [`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 field name, entity name and an array of return fields. If all fields in certain entity should be returned, the `returnFields` property could be set to ['*']:
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 ['*']:


```ts
const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Entity A', ['*']);
```
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) and [`IFilteringExpressionsTree`]({environment:angularApiUrl}/interfaces/ifilteringexpressionstree.html) (subquery).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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) and [`IFilteringExpressionsTree`]({environment:angularApiUrl}/interfaces/ifilteringexpressionstree.html) (subquery).
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).

Each [`IFilteringExpression`]({environment:angularApiUrl}/interfaces/ifilteringexpression.html) and [`IFilteringExpressionsTree`]({environment:angularApiUrl}/interfaces/ifilteringexpressionstree.html) should have `fieldName` that is the name of the column where the filtering expression is placed. If required, you could also set a `condition` of type [`IFilteringOperation`]({environment:angularApiUrl}/interfaces/ifilteringoperation.html), `conditionName`, `searchVal`, `searchTree` of type [`IExpressionTree`]({environment:angularApiUrl}/interfaces/iexpressiontree.html) and `ignoreCase` properties.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Each [`IFilteringExpression`]({environment:angularApiUrl}/interfaces/ifilteringexpression.html) and [`IFilteringExpressionsTree`]({environment:angularApiUrl}/interfaces/ifilteringexpressionstree.html) should have `fieldName` that is the name of the column where the filtering expression is placed. If required, you could also set a `condition` of type [`IFilteringOperation`]({environment:angularApiUrl}/interfaces/ifilteringoperation.html), `conditionName`, `searchVal`, `searchTree` of type [`IExpressionTree`]({environment:angularApiUrl}/interfaces/iexpressiontree.html) and `ignoreCase` properties.
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. If required, you could also set a `condition` of type [`IFilteringOperation`]({environment:angularApiUrl}/interfaces/ifilteringoperation.html), `conditionName`, `searchVal`, `searchTree` of type [`IExpressionTree`]({environment:angularApiUrl}/interfaces/iexpressiontree.html), and `ignoreCase` properties.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way this is written implies that conditionName is optional, which it isn't.


- Defining a simple **expression**:
```ts
tree.filteringOperands.push({
fieldName: 'Name',
conditionName: IgxStringFilteringOperand.instance().condition('endsWith').name,
searchVal: 'a'
});
```

- Defining a **group** of expressions:
```ts
const group = new FilteringExpressionsTree(FilteringLogic.Or, undefined, 'Entity A', ['*']);
group.filteringOperands.push({
fieldName: 'Name',
conditionName: IgxStringFilteringOperand.instance().condition('endsWith').name,
searchVal: 'a'
});
group.filteringOperands.push({
fieldName: 'DateTime created',
conditionName: IgxDateFilteringOperand.instance().condition('today').name
Comment thread
Otixa marked this conversation as resolved.
});
tree.filteringOperands.push(group);
```

- Defining a **subquery**:
```ts
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Entity B', ['Number']);
innerTree.filteringOperands.push({
fieldName: 'Number',
conditionName: IgxNumberFilteringOperand.instance().condition('equals').name,
searchVal: 123
});
tree.filteringOperands.push({
fieldName: 'Id',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
searchTree: innerTree
});
```

The model can be serialized/deserialized in JSON format, making it easily transferable between client and server:
```ts
JSON.stringify(tree, null, 2);
```

## Using Sub-Queries
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.

> [!Note]
> A subquery is a query nested inside another query used to retrieve data that will be used as a condition for the outer query.

Selecting the *IN / NOT-IN* operator in a `FilteringExpression` would create a subquery. After choosing 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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Selecting the *IN / NOT-IN* operator in a `FilteringExpression` would create a subquery. After choosing 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.
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 any of the values returned by the subquery.


The following expression tree:
```ts
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'products', ['supplier_id']);
innerTree.filteringOperands.push({
fieldName: 'supplier_id',
conditionName: IgxNumberFilteringOperand.instance().condition('greaterThan').name,
searchVal: 10
});

const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'suppliers', ['supplier_id']);
tree.filteringOperands.push({
fieldName: 'supplier_id',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
searchTree: innerTree
});
```
Could be serialized by calling:
```ts
JSON.stringify(tree, null, 2);
```

This would be transferred as:
```
{
"filteringOperands": [
{
"fieldName": "supplier_id",
"condition": {
"name": "inQuery",
"isUnary": false,
"isNestedQuery": true,
"iconName": "in"
},
"conditionName": "inQuery",
"searchVal": null,
"searchTree": {
"filteringOperands": [
{
"fieldName": "supplier_id",
"condition": {
"name": "greaterThan",
"isUnary": false,
"iconName": "filter_greater_than"
},
"conditionName": "greaterThan",
"searchVal": 10,
"searchTree": null
}
],
"operator": 0,
"entity": "suppliers",
"returnFields": [
"supplier_id"
]
}
}
],
"operator": 0,
"entity": "products",
"returnFields": [
"supplier_id"
]
}
```

## SQL Example

Let's take a look at a practical example how the Ignite UI for Angular Query Builder Component can be used to build SQL queries.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Let's take a look at a practical example how the Ignite UI for Angular Query Builder Component can be used to build SQL queries.
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.


In the sample below we have a SQL database with 3 tables - 'suppliers', 'categories' and 'products'. Once we fetch information about them we set our `entities` property of the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) and we can build queries.

Let's say we want to find all suppliers who supply products that belong to the 'Beverages' category. Since the data is distributed across the 3 tables 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.

First, we create а `categoriesTree` which will return the `category_id` for the record where `category_name` is `Beverages`. This is the innermost subquery:

```ts
const categoriesTree = new FilteringExpressionsTree(0, undefined, 'categories', ['category_id']);
categoriesTree.filteringOperands.push({
fieldName: 'category_name',
conditionName: IgxStringFilteringOperand.instance().condition('equals').name,
searchVal: 'Beverages'
});
```

The corresponding SQL query for this `FilteringExpressionsTree` will look like this:

```
SELECT category_id FROM categories WHERE category_name = 'Beverages'
```

Then we create а `productsTree` that will return the `supplier_id` field from the `categoriesTree` for the records where the `category_id` matches the `category_id` returned by the innermost subquery. We do this by setting the `inQuery` condition and the relevant `searchTree`. This is the middle subquery:

```ts
const productsTree = new FilteringExpressionsTree(0, undefined, 'products', ['supplier_id']);
productsTree.filteringOperands.push({
fieldName: 'category_id',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
searchTree: categoriesTree
});
```

This is the updated state of the SQL query:

```
SELECT supplier_id FROM products WHERE category_id IN (
SELECT category_id FROM categories WHERE category_name = 'Beverages'
)
```

Finally, we create а `suppliersTree` that will return all fields from `suppliers` entity where the `supplier_id` matches any of the `supplier_id`s returned by the middle subquery. This is the outermost query:

```ts
const suppliersTree = new FilteringExpressionsTree(0, undefined, 'suppliers', ['*']);
suppliersTree.filteringOperands.push({
fieldName: 'supplier_id',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
searchTree: productsTree
});
```

Our SQL query is now complete:

```
SELECT * FROM suppliers WHERE supplier_id IN (
SELECT supplier_id FROM products WHERE category_id IN (
SELECT category_id FROM categories WHERE category_name = 'Beverages'
)
)
```

Now we can set the `expressionsTree` property of the `IgxQueryBuilderComponent` to `suppliersTree`. Furthermore, every change to the query triggers SQL query execution and refreshing the results data shown in the grid.

<code-view style="height:700px"
no-theming
data-demos-base-url="{environment:demosBaseUrl}"
iframe-src="{environment:demosBaseUrl}/interactions/query-builder-sql-sample" >
</code-view>

## Request data from an Endpoint
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this section before the SQL Example section


The demo below demonstrates how the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) expression tree could be used to requets data from an endpoint [Northwind WebAPI](https://data-northwind.indigo.design/swagger/index.html) and set it as [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html) data source.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The demo below demonstrates how the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) expression tree could be used to requets data from an endpoint [Northwind WebAPI](https://data-northwind.indigo.design/swagger/index.html) and set it as [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html) data source.
The demo below demonstrates 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 [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html) data source.


In order to update the data source when there is a change in the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html), the `expressionTreeChange` event should be handled.

In the event handler, could be called an `HttpClient` POST request that accepts an endpoint url and body - the [`IgxQueryBuilderComponent`]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html) `expressionTree`.

After subscribing to the request, the received data should be parsed to proper data type and set it as data source of the [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
After subscribing to the request, the received data should be parsed to proper data type and set it as data source of the [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html).
After subscribing to the request, the received data should be parsed to a proper data type and set as the data source of the [`IgxGridComponent`]({environment:angularApiUrl}/classes/igxgridcomponent.html).


```ts
public onChange() {
this.http.post(`${API_ENDPOINT}/QueryBuilder/ExecuteQuery`, this.expressionTree).subscribe(data =>{
this.data = Object.values(data)[0];
});
}
```

<code-view style="height:700px"
no-theming
data-demos-base-url="{environment:demosBaseUrl}"
iframe-src="{environment:demosBaseUrl}/interactions/query-builder-request-sample" >
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this sample can remain here to explain how the customer uses the api to make these requests, the "request sample" should be the main sample in the main topic, instead of the one simply listing the query as text.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically as a showcase for the QB. The main topic doesn't have to provide any additional explanation to what the sample is about.

</code-view>

## API References

<div class="divider--half"></div>

* [IgxQueryBuilderComponent API]({environment:angularApiUrl}/classes/igxquerybuildercomponent.html)
* [IgxQueryBuilderComponent Styles]({environment:sassApiUrl}/index.html#function-query-builder-theme)

## Additional Resources

<div class="divider--half"></div>
Our community is active and always welcoming to new ideas.

* [Ignite UI for Angular **Forums**](https://www.infragistics.com/community/forums/f/ignite-ui-for-angular)
* [Ignite UI for Angular **GitHub**](https://github.com/IgniteUI/igniteui-angular)
4 changes: 4 additions & 0 deletions en/components/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,10 @@
href: query-builder.md
new: false
updated: true
items:
- name: Using Query Builder Model
href: query-builder-model.md
new: true
- name: Radio & Radio Group
href: radio-button.md
new: false
Expand Down