npm package: @lexxsoft/odata-http-query
Description: Creating and parsing URL query for OData and more
Author: Aleksey Komov
Latest version: 1.0.3

# @lexxsoft/odata-http-query

![GitHub package.json version](https://img.shields.io/github/package-json/v/lexxyar/lexx-odata-query-builder)
![GitHub](https://img.shields.io/github/license/lexxyar/lexx-odata-query-builder)
![GitHub all releases](https://img.shields.io/github/downloads/lexxyar/lexx-odata-query-builder/total)

# Contents

* [Installation](#installation)
* [Usage](#usage)
* [Basic usage](#basic-usage)
    * [Ordering](#ordering)
    * [Expanding](#expanding)
    * [Limiting](#limiting)
    * [Paging](#paging)
    * [Filtering](#filtering)
    * [ID](#id)
    * [Select](#select)
    * [Count](#count)
    * [Search](#search)
    * [File content](#file-content)

# Installation

```shell script
npm i @lexxsoft/odata-http-query
```

# Usage

## Basic usage

```typescript
const o: QueryBuilder = new QueryBuilder()
```

or

```typescript
const o: QueryBuilder = QueryBuilder.make()
```

Or create with flow

```typescript
const o: string = QueryBuilder.make()
    .querySet('lang', 'en')
    .toString()
```

> Output `lang=en`

## Configuration

QueryBuilder has configuration to customize process.

### Markers

QueryBuilder has markers to customize fixed query params. Type for markers shown below:

```typescript
type TQueryMarkers = {
    select: string[] | string,
    limit: string[] | string,
    offset: string[] | string,
    order: string[] | string,
    expand: string[] | string,
    search: string[] | string,
    count: string[] | string,
    filter: string[] | string,
}
```

### Markers and corresponding query field

By default, QueryBuilder markers are correspond to OData query field.

```typescript
const DefaultQueryMarkers: TQueryMarkers = {
    limit: ['$top', '$limit'],
    offset: ['$skip', '$offset'],
    order: ['$orderby', '$order'],
    expand: '$expand',
    select: '$select',
    filter: '$filter',
    count: '$count',
    search: '$search',
}
```

Of course, you can customize there names as you need.
For flexibility each marker could have alias. Pass them as array to defaults for correct work. Look at example above.

### Custom configuration

QueryBuilder configuration type:

```typescript
type TQueryBuilderDefaults = {
    limit?: number,
    markers?: TQueryMarkers,
}
```

| Field   | Type          | Default               | Description                                               |
|---------|---------------|-----------------------|-----------------------------------------------------------|
| limit   | Number        | `undefined`           | Set `limit` to use limitation for every builder instance. |
| markers | TQueryMarkers | `DefaultQueryMarkers` | Set custom query field name for every marker              |

### Ordering

Use `order` method to add order query parameters
> Note: `order` method has an alias `orderby`

```js
const o = new QueryBuilder()
o.order(new QueryOrder('name1')).toString()
```

> Output `$orderby=name1 asc`

Also, you can combine several order conditions

```js
const o = new QueryBuilder()
o.order(new QueryOrder('name1')).order(new QueryOrder('name2', QueryOrderDirection.DESC))
o.toString()
```

> Output `$orderby=name1 asc,name2 desc`

### Expanding

Use `expand` method to add expand query parameter

```js
const o = new QueryBuilder()
o.expand('company').toString()
```

> Output `$expand=company`

Or combine several expand parameters

```js
const o = new QueryBuilder()
o.expand('company').expand('jobtitle').toString()
```

> Output `$expand=company,jobtitle`

To add counter to expanding parameter, add `true` as second parameter of `expand` method

```js
const o = new QueryBuilder()
o.expand('emails', true).toString()
```

> Output `$expand=company($count=true)`

### Limiting

For limiting returned data, use `limit` and `offset` methods
> Note: `limit` has an alias `top`
> `offset` has aliases `skip` and `shift`

```js
const o = new QueryBuilder()
o.top(7).skip(4).toString()
```

> Output `$top=7&$skip=4`

### Filtering

Use `filter` method to add constrains. QueryBuilder accept only **one** filter, but you free to use `and` and `or`
methods of QueryFilter to combine them together.

```js
const oFilter = new QueryFilter('gender', 'f')
oFilter.and('age', 16, QueryFilterSign.GT)
const o = new QueryBuilder()
o.filter(oFilter).toString()
```

> Output `$filter=gender eq f and age gt 16`

#### Filter operations

- [X] EQ
- [X] GT
- [X] GE
- [X] LT
- [X] LE
- [ ] NE
- [X] SUBSTRINGOF
- [X] STARTSWITH
- [X] ENDSWITH

### Counting

`count` method will add `$count` suffix to url

```js
const o = new QueryBuilder()
o.count().toString()
```

Or use `inlineCount` and get same result

```js
const o = new QueryBuilder()
o.inlineCount()
```

> Output `$count=true`

Of course, you can use `count` with `filter` for example.

```js
const oFilter = new QueryFilter('gender', 'f')
oFilter.and('age', 16, QueryFilterSign.GT)
const o = new QueryBuilder()
o.filter(oFilter).count().toString()
```

> Output `$count=true&$filter=gender eq f and age gt 16`

### Select

Use `select` method to constrain returned fields

```js
const o = new QueryBuilder()
o.select(['id', 'name']).toString()
```

> Output `$select=id,name`

### search

Free-text search parameter, which was added for OData v4.0

```js
const o = new QueryBuilder()
o.search('John Doe')
```

> Output `$search=John Doe

# Parsing URL

For case if you already have url, and you want to add or change some parameter, you may use `parse` method, to parse url
and get instance of QueryBuilder

```js
const o = QueryBuilder.parse(`/users?$count=true`)
consle.log(o.toString())
```

> Output `$count=true`

# Making URL

To create URL use method `toUrl`. As base URL use any URL-like string **without query parameters**.

```js
const o = QueryBuilder.make()
    .filter('name1', 'John')
consle.log(o.toUrl('/users'))
```

> Output `/users?$filter=name1%20eq%20'John'`