# OData Query Builder

[![pipeline status](https://gitlab.com/Vlad160/odata-query-builder/badges/master/pipeline.svg)](https://gitlab.com/Vlad160/odata-query-builder/commits/master)
[![coverage report](https://gitlab.com/Vlad160/odata-query-builder/badges/master/coverage.svg)](https://gitlab.com/Vlad160/odata-query-builder/commits/master)

OData query builder that uses chain sytax.

## Table of Content
- [OData Query Builder](#odata-query-builder)
    - [Table of Content](#table-of-content)
    - [Install](#install)
    - [Usage](#usage)
        - [Filter](#filter)
            - [Simple filter](#simple-filter)
            - [Filter with fuctions](#filter-with-fuctions)
            - [Piping](#piping)
            - [Complex filtering with logical operator changing](#complex-filtering-with-logical-operator-changing)
        - [Count](#count)
        - [Skip/Top](#skiptop)
        - [Select](#select)
        - [Search](#search)
        - [OrderBy](#orderby)
        - [Expand](#expand)
            - [Simple multiple expand](#simple-multiple-expand)
            - [Expand nested property](#expand-nested-property)
            - [Expand with query](#expand-with-query)
            - [Object based expand](#object-based-expand)
        - [Piping](#piping-1)

## Install

```
    yarn add @vlad160/odata-query-builder
```
or
```
    npm i @vlad160/odata-query-builder --save
```

## Usage

Use type helpers to build your query expression and call ```toString()```

*NOTE*: Query is not encoded by default. Use ```toString({ encode: true })``` for query encoding

Only OData4 provider is implemented for now. PR's with OData3 are accepted :)

### Filter

You can use both ```ODataFilter``` or ```ODataQuery``` to build filter query.

Default logical operator is AND. You can change it by passing ```LogicalOperator``` directly to the ```ODataFilter``` constructor. See below for more examples

#### Simple filter
```
const query = new OData4Query();
query.filter(f => f.eq('Name', 'John')
    .gt('Age', 5)
    .toString()
)
```
Output
```
$filter=(Name eq 'John') and (Age gt 5)
```
#### Filter with fuctions
```
const query = new OData4Query();
query.filter(f => f.eq(x => x.toLower('Name'), 'John')
    .gt(x => x.round('Age'), 5)
    .toString()
)
```
Output
```
$filter=(tolower(Name) eq 'John') and (round(Age) gt 5)
```
#### Piping
```
const query = new OData4Query();
query.filter(f => f.pipe(
    eq(x => x.toLower('Name'), 'John'),
    gt(x => x.round('Age'), 5)
))
```
#### Complex filtering with logical operator changing
```
const filter = new OData4Filter();

filter
    .pipe(eq(x => x.toUpper('Surname'), 'SNOW'), gt('Age', 25))
    .eq('Name', 'John')
    .or(x => x.gt(x => x.length('Name'), 15))
    .not(x => x.eq('Name', 'Tition')
        .lt('Age', 50), LogicalOperator.OR);
```
Output
```
((toupper(Surname) eq 'SNOW') and (Age gt 25) and (Name eq 'John')) or (length(Name) gt 15) not ((Name eq 'Tition') or (Age lt 50))
```

### Count

```
const query = new OData4Query();

query
    .count()
```
Output
```
$count=true
```

### Skip/Top
```
const query = new OData4Query();

query
    .skip(5)
    .top(10)
    .toString()
```
Output
```
$top=10&$skip=5
```
### Select
```
const query = new OData4Query();

query
    .select('Name', 'Surname')
    .toString()
```
Output
```
$select=Name,Surname
```
### Search
```
const query = new OData4Query();

query
    .select('Some Query')
    .toString()
```
Output
```
$select=Some Query
```
### OrderBy

Keep in mind that ```orderBy``` returns ```OrderedQuery``` and only  ```skip```, ```top``` and ```thenBy``` operators could be applied

```
const query = new OData4Query();

query
    .orderBy('Name', Order.ASC)
    .thenBy('Surname', Order.DESC)
    .skip(5)
    .top(228)
    .toString()
```
Output
```
$orderBy=Name asc,Surname desc&$top=228&$skip=5
```
### Expand

#### Simple multiple expand
```
const query = new OData4Query();

query
    .expand(['Result', 'Items'])
    .toString()
```
Output
```
$expand=Result,Items
```
#### Expand nested property
```
const query = new OData4Query();

query
    .expand('Result/Items')
    .toString()
```
Output
```
$expand=Result($expand=Items)
```
#### Expand with query
```
const query = new OData4Query();

query
    .expand('Result', q =>
        q.select('Items')
            .filter(f => f.gt('Price', 5))
            .top(5))
    .toString()
```
Output
```
$expand=Result($select=Items;$top=5;$filter=Price gt 5)
```
#### Object based expand
```
const query = new OData4Query();

query
    .expand({
        'Result': {
            top: 5
        }, 'Items': {}
    })
    .toString()
```
Output
```
$expand=Result($top=5),Items
```
### Piping
```
const query = new OData4Query();

query
    .pipe(select('Name'),
        expand('Result'),
        skip(5),
        top(10))
    .toString()
```
Output
```
$expand=Result&$select=Name&$top=10&$skip=5
```