npm install --save ravendb
- Click here to view all Releases and Changelog.
-
This readme provides short examples for the following:
Getting started,
Asynchronous call types,
Crud example,
Query documents,
Attachments,
Time series,
Bulk insert,
Changes API,
Streaming,
Revisions,
Suggestions,
Patching,
Subscriptions,
Using object literals,
Using classes,
Typescript usage,
Working with secure server,
Building & running tests -
For more information go to the online RavenDB Documentation.
- Require the
DocumentStore
class from the ravendb package
const { DocumentStore } = require('ravendb');
or (using ES6 / Typescript imports)
import { DocumentStore } from 'ravendb';
- Initialize the document store (you should have a single DocumentStore instance per application)
const store = new DocumentStore('http://live-test.ravendb.net', 'databaseName');
store.initialize();
- Open a session
const session = store.openSession();
- Call
saveChanges()
when you're done
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
});
- When you have finished using the session and the document store objects,
make sure to dispose of them properly to free up resources:
session.dispose();
store.dispose();
Most methods on the session object are asynchronous and return a Promise.
Either use async & await
or .then()
with callback functions.
- async / await
const session = store.openSession();
let user = await session.load('users/1-A');
user.password = PBKDF2('new password');
await session.saveChanges();
- .then & callback functions
session.load('Users/1-A')
.then((user) => {
user.password = PBKDF2('new password');
})
.then(() => session.saveChanges())
.then(() => {
// here session is complete
});
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();
store()
ID generation - session.store()
store document with @metadata
storing docs with same ID in same session should throw
const product = await session.load('products/1-A');
console.log(product.title); // iPhone X
console.log(product.id); // products/1-A
// 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);
can load with includes
loading data with include
loading data with passing includes
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
- Using entity
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
- Using document ID
await session.delete('products/1-A');
delete doc by entity
delete doc by ID
onBeforeDelete is called before delete by ID
cannot delete untracked entity
loading deleted doc returns null
- Use
query()
session method:
Query by collection:
const query = session.query({ collection: 'products' });
Query by index name:
const query = session.query({ indexName: 'productsByCategory' });
Query by index:
const query = session.query(Product, Product_ByName);
Query by entity type:
import { User } from "./models";
const query = session.query(User);
- Build up the query - apply search conditions, set ordering, etc.
Query supports chaining calls:
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');
- Execute the query to get results:
const results = await query.all(); // get all results
// ...
const firstResult = await query.first(); // gets first result
// ...
const single = await query.single(); // gets single result
// RQL
// from users select name
// Query
const userNames = await session.query({ collection: "users" })
.selectFields("name")
.all();
// Sample results
// John, Stefanie, Thomas
projections single field
query single property
retrieve camel case with projection
can_project_id_field
// 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 } ]
projections multiple fields
query with projection
retrieve camel case with projection
can_project_id_field
// RQL
// from users select distinct age
// Query
await session.query({ collection: "users" })
.selectFields("age")
.distinct()
.all();
// Sample results
// [ 30, 25 ]
// 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 } ]
// 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' } ]
// 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 } ]
// 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' } ]
// 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' } ]
where greater than
query with where less than
query with where less than or equal
query with where greater than
query with where greater than or equal
Checks if the field exists.
// 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' } ]
// 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' } ]
Perform full-text search.
// 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' } ]
search()
query search with or
query_CreateClausesForQueryDynamicallyWithOnBeforeQueryEvent
// 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' } ]
// 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' } ]
// 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' } ]
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.
// 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' } ]
set default operator
AND is used when default operator is not set
set default operator to OR
// 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' } ]
orderBy()
orderByDesc()
query random order
order by AlphaNumeric
query with boost - order by score
Limit the number of query results.
// 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' } ]
Skip a specified number of results from the start.
// 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' } ]
Use the statistics()
method to obtain query statistics.
// 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 }
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()
)
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();
store attachment
can put attachments
checkIfHasChangesIsTrueAfterAddingAttachment
store many attachments and docs with bulk insert
// 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());
await session.advanced.attachments.exists(doc.id, "photo.png");
// true
await session.advanced.attachments.exists(doc.id, "not_there.avi");
// false
// 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 } ]
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();
can use time series
canCreateSimpleTimeSeries
usingDifferentTags
canStoreAndReadMultipleTimestamps
canStoreLargeNumberOfValues
shouldDeleteTimeSeriesUponDocumentDeletion
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();
canCreateSimpleTimeSeries
canStoreLargeNumberOfValues
canRequestNonExistingTimeSeriesRange
canGetTimeSeriesNames2
canSkipAndTakeTimeSeries
// 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();
bulk insert example
simple bulk insert should work
bulk insert can be aborted
can modify metadata with bulk insert
Listen for database changes e.g. document changes.
// 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();
listen to changes
can obtain single document changes
can obtain all documents changes
can obtain notification about documents starting with
can obtain notification about documents in collection
// 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
})
can stream users by prefix
can stream documents starting with
// 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
});
can stream query and get stats
can stream query results
can stream query results with query statistics
can stream raw query results
NOTE: Please make sure revisions are enabled before trying the below.
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' } ]
can get revisions
canGetRevisionsByDate
can handle revisions
canGetRevisionsByChangeVectors
Suggest options for similar/misspelled terms
// 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' ] } }
can suggest
canChainSuggestions
canUseAliasInSuggestions
canUseSuggestionsWithAutoIndex
can suggest using linq
can suggest using multiple words
can get suggestions with options
// 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();
can use advanced.patch
can patch
can patch complex
can add to array
can increment
patchWillUpdateTrackedDocumentAfterSaveChanges
can patch single document
can patch multiple documents
// 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'
can subscribe
should stream all documents
should send all new and modified docs
should respect max doc count in batch
can disable subscription
can delete subscription
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.
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
using object literals for entities
using object literals
handle custom entity naming conventions + object literals
- Define your model as class. Attributes should be just public properties:
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()
});
}
}
- To store a document pass its instance to
store()
.
The collection name will automatically be detected from the entity's class name.
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();
- Loading a document
const product = await session.load('products/1-A');
console.log(product instanceof Product); // true
console.log(product.id); // products/1-A
- Querying for documents
const products = await session.query({ collection: 'products' }).all();
products.forEach((product) => {
console.log(product instanceof Product); // true
console.log(product.id.includes('products/')); // true
});
TypeScript typings are embedded into the package (see types
property in package.json
).
// 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<void> => {
let product = new Product(
null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00')
);
await session.store<Product>(product);
await session.saveChanges();
console.log(product instanceof Product); // true
console.log(product.id.includes('products/')); // true
product = await session.load<Product>('products/1-A');
console.log(product instanceof Product); // true
console.log(product.id); // products/1-A
let products: Product[] = await session
.query<Product>({ 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
});
})();
- Fill auth options object.
Pass the contents of the pem/pfx certificate, specify its type, and (optionally) a passphrase:
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:
const {DocumentStore} = require('ravendb');
const fs = require('fs');
const certificate = './cert.pfx';
let authOptions = {
certificate: fs.readFileSync(certificate),
type: "pfx",
password: 'my passphrase' // optional
};
- Pass auth options as third argument to
DocumentStore
constructor:
let store = new DocumentStore('url', 'databaseName', authOptions);
store.initialize();
npm install
npm run build
# 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