An open API service indexing awesome lists of open source software.

https://github.com/ravendb/ravendb-nodejs-client

RavenDB node.js client
https://github.com/ravendb/ravendb-nodejs-client

Last synced: about 1 year ago
JSON representation

RavenDB node.js client

Awesome Lists containing this project

README

          

# Node.js client for RavenDB NoSQL Database

[![NPM](https://nodei.co/npm/ravendb.png?compact=true)](https://nodei.co/npm/ravendb/)

[![build status](https://github.com/ravendb/ravendb-nodejs-client/workflows/tests/node/badge.svg?branch=v6.0)](https://github.com/ravendb/ravendb-nodejs-client/actions) [![Known Vulnerabilities](https://snyk.io/test/github/ravendb/ravendb-nodejs-client/badge.svg)](https://snyk.io/test/github/ravendb/ravendb-nodejs-client)

## Installation

```bash
npm install --save ravendb
```

## Releases

* [Click here](https://github.com/ravendb/ravendb-nodejs-client/releases) to view all Releases and Changelog.

## Documentation

* This readme provides short examples for the following:
[Getting started](#getting-started),
[Asynchronous call types](#supported-asynchronous-call-types),
[Crud example](#crud-example),
[Query documents](#query-documents),
[Attachments](#attachments),
[Time series](#timeseries),
[Bulk insert](#bulk-insert),
[Changes API](#changes-api),
[Streaming](#streaming),
[Revisions](#revisions),
[Suggestions](#suggestions),
[Patching](#advanced-patching),
[Subscriptions](#subscriptions),
[Using object literals](#using-object-literals-for-entities),
[Using classes](#using-classes-for-entities),
[Typescript usage](#usage-with-typescript),
[Working with secure server](#working-with-a-secure-server),
[Building & running tests](#building)

* For more information go to the online [RavenDB Documentation](https://ravendb.net/docs/article-page/latest/nodejs/client-api/what-is-a-document-store).

## Getting started

1. Require the `DocumentStore` class from the ravendb package
```javascript
const { DocumentStore } = require('ravendb');
```
or (using ES6 / Typescript imports)
```javascript
import { DocumentStore } from 'ravendb';
```

2. Initialize the document store (you should have a single DocumentStore instance per application)
```javascript
const store = new DocumentStore('http://live-test.ravendb.net', 'databaseName');
store.initialize();
```

3. Open a session
```javascript
const session = store.openSession();
```

4. Call `saveChanges()` when you're done
```javascript
session
.load('users/1-A') // Load document
.then((user) => {
user.password = PBKDF2('new password'); // Update data
})
.then(() => session.saveChanges()) // Save changes
.then(() => {
// Data is now persisted
// You can proceed e.g. finish web request
});
```

5. When you have finished using the session and the document store objects,
make sure to dispose of them properly to free up resources:
```javascript
session.dispose();
store.dispose();
```

## Supported asynchronous call types

Most methods on the session object are asynchronous and return a Promise.
Either use `async & await` or `.then()` with callback functions.

1. async / await
```javascript
const session = store.openSession();
let user = await session.load('users/1-A');
user.password = PBKDF2('new password');
await session.saveChanges();
```

2. .then & callback functions
```javascript
session.load('Users/1-A')
.then((user) => {
user.password = PBKDF2('new password');
})
.then(() => session.saveChanges())
.then(() => {
// here session is complete
});
```

>##### Related tests:
> [async and await](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L55)
> [then and callbacks](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L72)

## CRUD example

### Store documents

```javascript
let product = {
id: null,
title: 'iPhone X',
price: 999.99,
currency: 'USD',
storage: 64,
manufacturer: 'Apple',
in_stock: true,
last_update: new Date('2017-10-01T00:00:00')
};

await session.store(product, 'products/1-A');
console.log(product.id); // products/1-A
await session.saveChanges();
```

>##### Related tests:
> [store()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/SessionApiTests.ts#L21)
> [ID generation - session.store()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/IdGeneration.ts#L9)
> [store document with @metadata](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RDBC_213.ts#L16)
> [storing docs with same ID in same session should throw](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TrackEntityTest.ts#L62)

### Load documents

```javascript
const product = await session.load('products/1-A');
console.log(product.title); // iPhone X
console.log(product.id); // products/1-A
```

> ##### Related tests:
> [load()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/SessionApiTests.ts#L64)

### Load documents with include

```javascript
// users/1
// {
// "name": "John",
// "kids": ["users/2", "users/3"]
// }

const session = store.openSession();
const user1 = await session
.include("kids")
.load("users/1");
// Document users/1 and all docs referenced in "kids"
// will be fetched from the server in a single request.

const user2 = await session.load("users/2"); // this won't call server again

assert.ok(user1);
assert.ok(user2);
assert.equal(session.advanced.numberOfRequests, 1);
```

>##### Related tests:
> [can load with includes](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Documents/LoadTest.ts#L29)
> [loading data with include](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L128)
> [loading data with passing includes](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L148)

### Update documents

```javascript
let product = await session.load('products/1-A');
product.in_stock = false;
product.last_update = new Date();
await session.saveChanges();
// ...
product = await session.load('products/1-A');
console.log(product.in_stock); // false
console.log(product.last_update); // the current date
```

>##### Related tests:
> [update document](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L170)
> [update document metadata](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RDBC_213.ts#L35)

### Delete documents

1. Using entity
```javascript
let product = await session.load('products/1-A');
await session.delete(product);
await session.saveChanges();

product = await session.load('products/1-A');
console.log(product); // null
```

2. Using document ID
```javascript
await session.delete('products/1-A');
```

>##### Related tests:
> [delete doc by entity](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/DeleteTest.ts#L20)
> [delete doc by ID](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/DeleteTest.ts#L38)
> [onBeforeDelete is called before delete by ID](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_15492.ts#L16)
> [cannot delete untracked entity](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TrackEntityTest.ts#L20)
> [loading deleted doc returns null](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TrackEntityTest.ts#L32)

## Query documents

1. Use `query()` session method:

Query by collection:
```javascript
const query = session.query({ collection: 'products' });
```
Query by index name:
```javascript
const query = session.query({ indexName: 'productsByCategory' });
```
Query by index:
```javascript
const query = session.query(Product, Product_ByName);
```
Query by entity type:
```javascript
import { User } from "./models";
const query = session.query(User);
```

2. Build up the query - apply search conditions, set ordering, etc.
Query supports chaining calls:
```javascript
query
.waitForNonStaleResults()
.usingDefaultOperator('AND')
.whereEquals('manufacturer', 'Apple')
.whereEquals('in_stock', true)
.whereBetween('last_update', new Date('2022-11-01T00:00:00'), new Date())
.orderBy('price');
```

3. Execute the query to get results:
```javascript
const results = await query.all(); // get all results
// ...
const firstResult = await query.first(); // gets first result
// ...
const single = await query.single(); // gets single result
```

### Query methods overview

#### selectFields() - projections using a single field
```javascript
// RQL
// from users select name

// Query
const userNames = await session.query({ collection: "users" })
.selectFields("name")
.all();

// Sample results
// John, Stefanie, Thomas
```

>##### Related tests:
> [projections single field](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L341)
> [query single property](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L231)
> [retrieve camel case with projection](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/CustomKeyCaseConventionsTests.ts#L288)
> [can_project_id_field](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_14811.ts#L58)

#### selectFields() - projections using multiple fields
```javascript
// RQL
// from users select name, age

// Query
await session.query({ collection: "users" })
.selectFields([ "name", "age" ])
.all();

// Sample results
// [ { name: 'John', age: 30 },
// { name: 'Stefanie', age: 25 },
// { name: 'Thomas', age: 25 } ]
```

>##### Related tests:
> [projections multiple fields](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L349)
> [query with projection](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L318)
> [retrieve camel case with projection](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/CustomKeyCaseConventionsTests.ts#L288)
> [can_project_id_field](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_14811.ts#L58)

#### distinct()
```javascript
// RQL
// from users select distinct age

// Query
await session.query({ collection: "users" })
.selectFields("age")
.distinct()
.all();

// Sample results
// [ 30, 25 ]
```

>##### Related tests:
> [distinct](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L360)
> [query distinct](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L350)

#### whereEquals() / whereNotEquals()
```javascript
// RQL
// from users where age = 30

// Query
await session.query({ collection: "users" })
.whereEquals("age", 30)
.all();

// Saple results
// [ User {
// name: 'John',
// age: 30,
// kids: [...],
// registeredAt: 2017-11-10T23:00:00.000Z } ]
```

>##### Related tests:
> [where equals](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L369)
> [where not equals](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L451)

#### whereIn()
```javascript
// RQL
// from users where name in ("John", "Thomas")

// Query
await session.query({ collection: "users" })
.whereIn("name", ["John", "Thomas"])
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [...],
// id: 'users/1-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
```

>##### Related tests:
> [where in](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L377)
> [query with where in](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L256)

#### whereStartsWith() / whereEndsWith()
```javascript
// RQL
// from users where startsWith(name, 'J')

// Query
await session.query({ collection: "users" })
.whereStartsWith("name", "J")
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// kids: [...],
// registeredAt: 2017-11-10T23:00:00.000Z } ]
```

>##### Related tests:
> [query with where clause](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L148)

#### whereBetween()
```javascript
// RQL
// from users where registeredAt between '2016-01-01' and '2017-01-01'

// Query
await session.query({ collection: "users" })
.whereBetween("registeredAt", new Date(2016, 0, 1), new Date(2017, 0, 1))
.all();

// Sample results
// [ User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
```

>##### Related tests:
> [where between](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L385)
> [query with where between](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L265)

#### whereGreaterThan() / whereGreaterThanOrEqual() / whereLessThan() / whereLessThanOrEqual()
```javascript
// RQL
// from users where age > 29

// Query
await session.query({ collection: "users" })
.whereGreaterThan("age", 29)
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [...],
// id: 'users/1-A' } ]
```

>##### Related tests:
> [where greater than](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L393)
> [query with where less than](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L275)
> [query with where less than or equal](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L285)
> [query with where greater than](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L294)
> [query with where greater than or equal](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L304)

#### whereExists()
Checks if the field exists.
```javascript
// RQL
// from users where exists("age")

// Query
await session.query({ collection: "users" })
.whereExists("kids")
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [...],
// id: 'users/1-A' } ]
```

>##### Related tests:
> [where exists](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L401)
> [query where exists](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L503)

#### containsAny() / containsAll()
```javascript
// RQL
// from users where kids in ('Mara')

// Query
await session.query({ collection: "users" })
.containsAll("kids", ["Mara", "Dmitri"])
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' } ]
```

>##### Related tests:
> [where contains any](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L409)
> [queries with contains](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/ContainsTest.ts#L19)

#### search()
Perform full-text search.
```javascript
// RQL
// from users where search(kids, 'Mara')

// Query
await session.query({ collection: "users" })
.search("kids", "Mara Dmitri")
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' } ]
```

>##### Related tests:
> [search()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L417)
> [query search with or](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L362)
> [query_CreateClausesForQueryDynamicallyWithOnBeforeQueryEvent](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L30)

#### openSubclause() / closeSubclause()
```javascript
// RQL
// from users where exists(kids) or (age = 25 and name != Thomas)

// Query
await session.query({ collection: "users" })
.whereExists("kids")
.orElse()
.openSubclause()
.whereEquals("age", 25)
.whereNotEquals("name", "Thomas")
.closeSubclause()
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' },
// User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' } ]
```

>##### Related tests:
> [subclause](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L425)
> [working with subclause](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_5669.ts#L40)

#### not()
```javascript
// RQL
// from users where age != 25

// Query
await session.query({ collection: "users" })
.not()
.whereEquals("age", 25)
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: ["Dmitri", "Mara"]
// id: 'users/1-A' } ]
```

>##### Related tests:
> [not()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L438)
> [query where not](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L451)

#### orElse() / andAlso()
```javascript
// RQL
// from users where exists(kids) or age < 30

// Query
await session.query({ collection: "users" })
.whereExists("kids")
.orElse()
.whereLessThan("age", 30)
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [ 'Dmitri', 'Mara' ],
// id: 'users/1-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' },
// User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' } ]
```

>##### Related tests:
> [orElse](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L447)
> [working with subclause](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_5669.ts#L40)

#### usingDefaultOperator()
If neither `andAlso()` nor `orElse()` is called then the default operator between the query filtering conditions will be `AND` .
You can override that with `usingDefaultOperator` which must be called before any other where conditions.
```javascript
// RQL
// from users where exists(kids) or age < 29

// Query
await session.query({ collection: "users" })
.usingDefaultOperator("OR") // override the default 'AND' operator
.whereExists("kids")
.whereLessThan("age", 29)
.all();

// Sample results
// [ User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [ 'Dmitri', 'Mara' ],
// id: 'users/1-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' },
// User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' } ]
```

>##### Related tests:
> [set default operator](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L457)
> [AND is used when default operator is not set](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RDBC_649.ts#L36)
> [set default operator to OR](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RDBC_649.ts#L45)

#### orderBy() / orderByDesc() / orderByScore() / randomOrdering()
```javascript
// RQL
// from users order by age

// Query
await session.query({ collection: "users" })
.orderBy("age")
.all();

// Sample results
// [ User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' },
// User {
// name: 'John',
// age: 30,
// registeredAt: 2017-11-10T23:00:00.000Z,
// kids: [ 'Dmitri', 'Mara' ],
// id: 'users/1-A' } ]
```

>##### Related tests:
> [orderBy()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L467)
> [orderByDesc()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L477)
> [query random order](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L451)
> [order by AlphaNumeric](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L548)
> [query with boost - order by score](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L517)

#### take()
Limit the number of query results.
```javascript
// RQL
// from users order by age

// Query
await session.query({ collection: "users" })
.orderBy("age")
.take(2) // only the first 2 entries will be returned
.all();

// Sample results
// [ User {
// name: 'Stefanie',
// age: 25,
// registeredAt: 2015-07-29T22:00:00.000Z,
// id: 'users/2-A' },
// User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
```

>##### Related tests:
> [take()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L487)
> [query skip take](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L385)
> [canUseOffsetWithCollectionQuery](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_17551.ts#L17)

#### skip()
Skip a specified number of results from the start.
```javascript
// RQL
// from users order by age

// Query
await session.query({ collection: "users" })
.orderBy("age")
.take(1) // return only 1 result
.skip(1) // skip the first result, return the second result
.all();

// Sample results
// [ User {
// name: 'Thomas',
// age: 25,
// registeredAt: 2016-04-24T22:00:00.000Z,
// id: 'users/3-A' } ]
```

>##### Related tests:
> [skip()](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L496)
> [query skip take](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L385)
> [canUseOffsetWithCollectionQuery](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_17551.ts#L17)

#### Getting query statistics
Use the `statistics()` method to obtain query statistics.
```javascript
// Query
let stats: QueryStatistics;
const results = await session.query({ collection: "users" })
.whereGreaterThan("age", 29)
.statistics(s => stats = s)
.all();

// Sample results
// QueryStatistics {
// isStale: false,
// durationInMs: 744,
// totalResults: 1,
// skippedResults: 0,
// timestamp: 2018-09-24T05:34:15.260Z,
// indexName: 'Auto/users/Byage',
// indexTimestamp: 2018-09-24T05:34:15.260Z,
// lastQueryTime: 2018-09-24T05:34:15.260Z,
// resultEtag: 8426908718162809000 }
```

>##### Related tests:
> [can get stats](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L506)

#### all() / first() / single() / count()
`all()` - returns all results

`first()` - first result only

`single()` - first result, throws error if there's more entries

`count()` - returns the number of entries in the results (not affected by `take()`)

>##### Related tests:
> [query first and single](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L467)
> [query count](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/QueryTest.ts#L834)

## Attachments

#### Store attachments
```javascript
const doc = new User({ name: "John" });

// Store a dcoument, the entity will be tracked.
await session.store(doc);

// Get read stream or buffer to store
const fileStream = fs.createReadStream("../photo.png");

// Store attachment using entity
session.advanced.attachments.store(doc, "photo.png", fileStream, "image/png");

// OR store attachment using document ID
session.advanced.attachments.store(doc.id, "photo.png", fileStream, "image/png");

// Persist all changes
await session.saveChanges();
```

>##### Related tests:
> [store attachment](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L203)
> [can put attachments](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Attachments/AttachmentsSessionTest.ts#L26)
> [checkIfHasChangesIsTrueAfterAddingAttachment](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_16985.ts#L17)
> [store many attachments and docs with bulk insert](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Attachments/BulkInsertAttachmentsTest.ts#L105)

#### Get attachments
```javascript
// Get an attachment
const attachment = await session.advanced.attachments.get(documentId, "photo.png")

// Attachment.details contains information about the attachment:
// {
// name: 'photo.png',
// documentId: 'users/1-A',
// contentType: 'image/png',
// hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=',
// changeVector: '"A:3-K5TR36dafUC98AItzIa6ow"',
// size: 4579
// }

// Attachment.data is a Readable. See https://nodejs.org/api/stream.html#class-streamreadable
attachment.data
.pipe(fs.createWriteStream("photo.png"))
.on("finish", () => next());
```

>##### Related tests:
> [get attachment](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L241)
> [can get & delete attachments](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Attachments/AttachmentsSessionTest.ts#L133)

#### Check if attachment exists
```javascript
await session.advanced.attachments.exists(doc.id, "photo.png");
// true

await session.advanced.attachments.exists(doc.id, "not_there.avi");
// false
```

>##### Related tests:
> [attachment exists](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L258)
> [attachment exists 2](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Attachments/AttachmentsSessionTest.ts#L316)

#### Get attachment names
```javascript
// Use a loaded entity to determine attachments' names
await session.advanced.attachments.getNames(doc);

// Sample results:
// [ { name: 'photo.png',
// hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=',
// contentType: 'image/png',
// size: 4579 } ]
```
>##### Related tests:
> [get attachment names](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L266)
> [get attachment names 2](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Attachments/AttachmentsSessionTest.ts#L288)
>
## TimeSeries

#### Store time series
```javascript
const session = store.openSession();

// Create a document with time series
await session.store({ name: "John" }, "users/1");
const tsf = session.timeSeriesFor("users/1", "heartbeat");

// Append a new time series entry
tsf.append(new Date(), 120);

await session.saveChanges();
```

>##### Related tests:
> [can use time series](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L759)
> [canCreateSimpleTimeSeries](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L18)
> [usingDifferentTags](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L217)
> [canStoreAndReadMultipleTimestamps](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L372)
> [canStoreLargeNumberOfValues](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L430)
> [shouldDeleteTimeSeriesUponDocumentDeletion](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L729)

#### Get time series for document
```javascript
const session = store.openSession();

// Get time series for document by time series name
const tsf = session.timeSeriesFor("users/1", "heartbeat");

// Get all time series entries
const heartbeats = await tsf.get();
```

>##### Related tests:
> [canCreateSimpleTimeSeries](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L18)
> [canStoreLargeNumberOfValues](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L430)
> [canRequestNonExistingTimeSeriesRange](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L544)
> [canGetTimeSeriesNames2](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L648)
> [canSkipAndTakeTimeSeries](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/TimeSeries/TimeSeriesSessionTest.ts#L772)

## Bulk Insert

```javascript
// Create a bulk insert instance from the DocumentStore
const bulkInsert = store.bulkInsert();

// Store multiple documents
for (const name of ["Anna", "Maria", "Miguel", "Emanuel", "Dayanara", "Aleida"]) {
const user = new User({ name });
await bulkInsert.store(user);
// The data stored in bulkInsert will be streamed to the server in batches
}

// Sample documents stored:
// User { name: 'Anna', id: 'users/1-A' }
// User { name: 'Maria', id: 'users/2-A' }
// User { name: 'Miguel', id: 'users/3-A' }
// User { name: 'Emanuel', id: 'users/4-A' }
// User { name: 'Dayanara', id: 'users/5-A' }
// User { name: 'Aleida', id: 'users/6-A' }

// Call finish to send all remaining data to the server
await bulkInsert.finish();
```

>##### Related tests:
> [bulk insert example](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L279)
> [simple bulk insert should work](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/BulkInsert/BulkInsertsTest.ts#L23)
> [bulk insert can be aborted](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/BulkInsert/BulkInsertsTest.ts#L95)
> [can modify metadata with bulk insert](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/BulkInsert/BulkInsertsTest.ts#L136)

## Changes API

Listen for database changes e.g. document changes.

```javascript
// Subscribe to change notifications
const changes = store.changes();

// Subscribe for all documents, or for specific collection (or other database items)
const docsChanges = changes.forAllDocuments();

// Handle changes events
docsChanges.on("data", change => {
// A sample change data recieved:
// { type: 'Put',
// id: 'users/1-A',
// collectionName: 'Users',
// changeVector: 'A:2-QCawZTDbuEa4HUBORhsWYA' }
});

docsChanges.on("error", err => {
// handle errors
})

{
const session = store.openSession();
await session.store(new User({ name: "Starlord" }));
await session.saveChanges();
}

// ...
// Dispose the changes instance when you're done
changes.dispose();
```

>##### Related tests:
> [listen to changes](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L306)
> [can obtain single document changes](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Server/Documents/Notifications/ChangesTest.ts#L25)
> [can obtain all documents changes](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Server/Documents/Notifications/ChangesTest.ts#L93)
> [can obtain notification about documents starting with](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Server/Documents/Notifications/ChangesTest.ts#L255)
> [can obtain notification about documents in collection](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Server/Documents/Notifications/ChangesTest.ts#L312)

## Streaming

#### Stream documents by ID prefix
```javascript
// Filter streamed results by passing an ID prefix
// The stream() method returns a Node.js ReadableStream
const userStream = await session.advanced.stream("users/");

// Handle stream events with callback functions
userStream.on("data", user => {
// Get only documents with ID that starts with 'users/'
// i.e.: User { name: 'John', id: 'users/1-A' }
});

userStream.on("error", err => {
// handle errors
})
```

>##### Related tests:
> [can stream users by prefix](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L525)
> [can stream documents starting with](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Core/Streaming/DocumentStreaming.ts#L39)

#### Stream documents by query
```javascript
// Define a query
const query = session.query({ collection: "users" }).whereGreaterThan("age", 29);

let streamQueryStats;
// Call stream() to execute the query, it returns a Node.js ReadableStream.
// Can get query stats by passing a stats callback to stream() method
const queryStream = await session.advanced.stream(query, _ => streamQueryStats = _);

// Handle stream events with callback functions
queryStream.on("data", user => {
// Only documents matching the query are received
// These entities are Not tracked by the session
});

// Can get query stats by using an event listener
queryStream.once("stats", queryStats => {
// Sample stats:
// { resultEtag: 7464021133404493000,
// isStale: false,
// indexName: 'Auto/users/Byage',
// totalResults: 1,
// indexTimestamp: 2018-10-01T09:04:07.145Z }
});

// Stream emits an 'end' event when there is no more data to read
queryStream.on("end", () => {
// Get info from 'streamQueryStats', the stats object
const totalResults = streamQueryStats.totalResults;
const indexUsed = streamQueryStats.indexName;
});

queryStream.on("error", err => {
// handle errors
});
```

>##### Related tests:
> [can stream query and get stats](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L546)
> [can stream query results](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Core/Streaming/QueryStreaming.ts#L76)
> [can stream query results with query statistics](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Core/Streaming/QueryStreaming.ts#L140)
> [can stream raw query results](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Core/Streaming/QueryStreaming.ts#L192)

## Revisions

NOTE: Please make sure revisions are enabled before trying the below.

```javascript
const session = store.openSession();
const user = {
name: "Marcin",
age: 30,
pet: "Cat"
};

// Store a document
await session.store(user, "users/1");
await session.saveChanges();

// Modify the document to create a new revision
user.name = "Roman";
user.age = 40;
await session.saveChanges();

// Get revisions
const revisions = await session.advanced.revisions.getFor("users/1");

// Sample results:
// [ { name: 'Roman',
// age: 40,
// pet: 'Cat',
// '@metadata': [Object],
// id: 'users/1' },
// { name: 'Marcin',
// age: 30,
// pet: 'Cat',
// '@metadata': [Object],
// id: 'users/1' } ]
```

>##### Related tests:
> [can get revisions](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L737)
> [canGetRevisionsByDate](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RavenDB_11770.ts#L21)
> [can handle revisions](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/RevisionsTest.ts#L35)
> [canGetRevisionsByChangeVectors](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/RevisionsTest.ts#L149)

## Suggestions

Suggest options for similar/misspelled terms

```javascript
// Some documents in users collection with misspelled name term
// [ User {
// name: 'Johne',
// age: 30,
// ...
// id: 'users/1-A' },
// User {
// name: 'Johm',
// age: 31,
// ...
// id: 'users/2-A' },
// User {
// name: 'Jon',
// age: 32,
// ...
// id: 'users/3-A' },
// ]

// Static index definition
class UsersIndex extends AbstractJavaScriptIndexCreationTask {
constructor() {
super();
this.map(User, doc => {
return {
name: doc.name
}
});

// Enable the suggestion feature on index-field 'name'
this.suggestion("name");
}
}

// ...
const session = store.openSession();

// Query for similar terms to 'John'
// Note: the term 'John' itself will Not be part of the results

const suggestedNameTerms = await session.query(User, UsersIndex)
.suggestUsing(x => x.byField("name", "John"))
.execute();

// Sample results:
// { name: { name: 'name', suggestions: [ 'johne', 'johm', 'jon' ] } }
```

>##### Related tests:
> [can suggest](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L581)
> [canChainSuggestions](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RavenDB_9584.ts#L19)
> [canUseAliasInSuggestions](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RavenDB_9584.ts#L42)
> [canUseSuggestionsWithAutoIndex](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Issues/RavenDB_9584.ts#L60)
> [can suggest using linq](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Suggestions/SuggestionsTest.ts#L39)
> [can suggest using multiple words](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Suggestions/SuggestionsTest.ts#L78)
> [can get suggestions with options](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Suggestions/SuggestionsTest.ts#L125)

## Advanced patching

```javascript
// Increment 'age' field by 1
session.advanced.increment("users/1", "age", 1);

// Set 'underAge' field to false
session.advanced.patch("users/1", "underAge", false);

await session.saveChanges();
```

>##### Related tests:
> [can use advanced.patch](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L708)
> [can patch](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/FirstClassPatchTest.ts#L18)
> [can patch complex](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/FirstClassPatchTest.ts#L93)
> [can add to array](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/FirstClassPatchTest.ts#L162)
> [can increment](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/FirstClassPatchTest.ts#L268)
> [patchWillUpdateTrackedDocumentAfterSaveChanges](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Issues/RavenDB_11552.ts#L27)
> [can patch single document](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/PatchTest.ts#L24)
> [can patch multiple documents](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/PatchTest.ts#L71)

## Subscriptions

```javascript
// Create a subscription task on the server
// Documents that match the query will be send to the client worker upon opening a connection

const subscriptionName = await store.subscriptions.create({
query: "from users where age >= 30"
});

// Open a connection
// Create a subscription worker that will consume document batches sent from the server
// Documents are sent from the last document that was processed for this subscription

const subscriptionWorker = store.subscriptions.getSubscriptionWorker({ subscriptionName });

// Worker handles incoming batches
subscriptionWorker.on("batch", (batch, callback) => {
try {
// Process the incoming batch items
// Sample batch.items:
// [ Item {
// changeVector: 'A:2-r6nkF5nZtUKhcPEk6/LL+Q',
// id: 'users/1-A',
// rawResult:
// { name: 'John',
// age: 30,
// registeredAt: '2017-11-11T00:00:00.0000000',
// kids: [Array],
// '@metadata': [Object],
// id: 'users/1-A' },
// rawMetadata:
// { '@collection': 'Users',
// '@nested-object-types': [Object],
// 'Raven-Node-Type': 'User',
// '@change-vector': 'A:2-r6nkF5nZtUKhcPEk6/LL+Q',
// '@id': 'users/1-A',
// '@last-modified': '2018-10-18T11:15:51.4882011Z' },
// exceptionMessage: undefined } ]
// ...

// Call the callback once you're done
// The worker will send an acknowledgement to the server, so that server can send next batch
callback();

} catch(err) {
// If processing fails for a particular batch then pass the error to the callback
callback(err);
}
});

subscriptionWorker.on("error", err => {
// handle errors
});

// Subscription event types:
'batch', 'error', 'end', 'unexpectedSubscriptionError', 'afterAcknowledgment', 'connectionRetry'
```

>##### Related tests:
> [can subscribe](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L607)
> [should stream all documents](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Subscriptions/SubscriptionsBasicTest.ts#L143)
> [should send all new and modified docs](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Subscriptions/SubscriptionsBasicTest.ts#L202)
> [should respect max doc count in batch](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Subscriptions/SubscriptionsBasicTest.ts#L263)
> [can disable subscription](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Subscriptions/SubscriptionsBasicTest.ts#L345)
> [can delete subscription](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/Subscriptions/SubscriptionsBasicTest.ts#L52)

## Using object literals for entities

To comfortably use object literals as entities,
configure the collection name that will be used in the store conventions.

This must be done *before* calling `initialize()` on the DocumentStore instance,
else, your entities will be created in the *@empty* collection.

```javascript
const store = new DocumentStore(urls, database);

// Configure the collection name that will be used
store.conventions.findCollectionNameForObjectLiteral = entity => entity["collection"];
// ...
store.initialize();

// Sample object literal
const user = {
collection: "Users",
name: "John"
};

session = store.openSession();
await session.store(user);
await session.saveChanges();

// The document will be stored in the 'Users' collection
```

>##### Related tests:
> [using object literals for entities](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/ReadmeSamples.ts#L644)
> [using object literals](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/SessionApiTests.ts#L108)
> [handle custom entity naming conventions + object literals](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Ported/BulkInsert/BulkInsertsTest.ts#L220)

## Using classes for entities

1. Define your model as class. Attributes should be just public properties:
```javascript
export class Product {
constructor(
id = null,
title = '',
price = 0,
currency = 'USD',
storage = 0,
manufacturer = '',
in_stock = false,
last_update = null
) {
Object.assign(this, {
title,
price,
currency,
storage,
manufacturer,
in_stock,
last_update: last_update || new Date()
});
}
}
```

2. To store a document pass its instance to `store()`.
The collection name will automatically be detected from the entity's class name.
```javascript
import { Product } from "./models";

let product = new Product(
null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00'));

product = await session.store(product);
console.log(product instanceof Product); // true
console.log(product.id.includes('products/')); // true
await session.saveChanges();
```

3. Loading a document
```javascript
const product = await session.load('products/1-A');
console.log(product instanceof Product); // true
console.log(product.id); // products/1-A
```

4. Querying for documents
```javascript
const products = await session.query({ collection: 'products' }).all();

products.forEach((product) => {
console.log(product instanceof Product); // true
console.log(product.id.includes('products/')); // true
});
```

>##### Related tests:
> [using classes](https://github.com/ravendb/ravendb-nodejs-client/blob/5c14565d0c307d22e134530c8d63b09dfddcfb5b/test/Documents/SessionApiTests.ts#L173)

## Usage with TypeScript

TypeScript typings are embedded into the package (see `types` property in `package.json`).

```typescript
// file models/product.ts
export class Product {
constructor(
public id: string = null,
public title: string = '',
public price: number = 0,
public currency: string = 'USD',
public storage: number = 0,
public manufacturer: string = '',
public in_stock: boolean = false,
public last_update: Date = null
) {}
}

// file app.ts
import {Product} from "models/product";
import {DocumentStore, IDocumentStore, IDocumentSession, IDocumentQuery, DocumentConstructor, QueryOperators} from 'ravendb';

const store: IDocumentStore = new DocumentStore('url', 'database name');
let session: IDocumentSession;

store.initialize();

(async (): Promise => {
let product = new Product(
null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00')
);

await session.store(product);
await session.saveChanges();
console.log(product instanceof Product); // true
console.log(product.id.includes('products/')); // true

product = await session.load('products/1-A');
console.log(product instanceof Product); // true
console.log(product.id); // products/1-A

let products: Product[] = await session
.query({ collection: 'Products' })
.waitForNonStaleResults()
.whereEquals('manufacturer', 'Apple')
.whereEquals('in_stock', true)
.whereBetween('last_update', new Date('2017-10-01T00:00:00'), new Date())
.whereGreaterThanOrEqual('storage', 64)
.all();

products.forEach((product: Product): void => {
console.log(product instanceof Product); // true
console.log(product.id.includes('products/')); // true
});
})();
```

## Working with a secure server

1. Fill auth options object.
Pass the contents of the pem/pfx certificate, specify its type, and (optionally) a passphrase:
```javascript
const {DocumentStore, Certificate} = require('ravendb');

const certificate = `
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
`;

let authOptions = {
certificate,
type: "pem",
password: "my passphrase" // optional
};
```

PFX certificates content should be passed as a `Buffer` object:

```javascript
const {DocumentStore} = require('ravendb');
const fs = require('fs');

const certificate = './cert.pfx';

let authOptions = {
certificate: fs.readFileSync(certificate),
type: "pfx",
password: 'my passphrase' // optional
};
```

2. Pass auth options as third argument to `DocumentStore` constructor:

```javascript
let store = new DocumentStore('url', 'databaseName', authOptions);
store.initialize();
```

## Building

```bash
npm install
npm run build
```

## Running tests

```bash
# To run the suite, set the following environment variables:
#
# - Location of RavenDB server binary:
# RAVENDB_TEST_SERVER_PATH="C:\\work\\test\\Server\\Raven.Server.exe"
#
# - Certificate path for tests requiring a secure server:
# RAVENDB_TEST_SERVER_CERTIFICATE_PATH="C:\\work\\test\\cluster.server.certificate.pfx"
#
# - Certificate hostname:
# RAVENDB_TEST_SERVER_HOSTNAME="a.nodejstest.development.run"
#
npm test
```