Skip to content
mattdaly edited this page Feb 20, 2012 · 2 revisions

Using JavaScript, we don't have access to the strongly typed objects RavenDB uses so heavily (you can of course store 'anonymous' objects without a type, but this makes querying less intuitive) in C#. We can, however, provide RavenDB with the information it needs in order to store documents as named type that we can use when querying for our data.

Raven uses a metadata object to store information about each type of document, in the Raven Studio you'll notice each type of document is coloured differently, that's based on a property called Raven-Clr-Type. We don't have access to the CLR, but we can still set that property from JavaScript and Raven knows what to do with it. We can also set the plural version of that, Raven-Entity-Name, that Raven uses as a document key prefix.

The Type object is required to store new documents using node-ravendb.


Contents


Type

node-ravendb provides a base class that builds up metadata about an object that is used extensively throughout the Session object when loading, querying and storing documents. That means every object/document in your application must derive from the Type object, this isn't necessary in Raven (you could store 'anonymous' documents) but is forced in node-ravendb to encourage stronger typing of your data. It also provides helper functions to support document key generation.

The Type object can be accessed directly off the base raven require statement var Type = require('./node-ravendb').Type; or at ./node-ravendb/lib/objects/type. It requires a single parameter - the name you wish RavenDB to use for the Raven-Clr-Type property. This property is also pluralised and used in key prefixing (by default, but can be changed).

var Dog = new raven.Type('Dog');

Each new Dog object will have the properties Raven-Clr-Type: 'Dog' and Raven-Entity-Name: 'Dogs'. The inflector used to generate the plural version of the specified type is identical to the default RavenDB inflector.

Note: The Type implementation is wrapped inside an anonymous function and is compatible with the coffeescript class implementation. To use the Type with coffeescript you must extend the Type.Base object - class Dog extends Type.Base.


Constructor

JavaScript isn't strongly typed, if we try to set properties on a Document that don't already exist (e.g. we don't really want them on our objects) we won't get an error, the property will be set and saved to RavenDB. It also provides the opportunity to 'forget' to set properties when creating instances of our objects.

To combat this you can set your required properties using constructor arguments and perform checks (or not) to ensure they are passed when instantiating an object. To add a constructor to your types, just add a function named 'constructor' off your Type implementation (not on it's prototype). This isn't required when using objects but it helps to ensure your documents are consistent when we don't have strong typing.

The prototype.constructor function will automatically be called everytime you instantiate a new document.

var Dog = new raven.Type('Dog');
Dog.constructor = function (name, breed, age) {
  if (!name || !breed || !age) throw new Error('Woof!');

  this.name = name;
  this.breed = breed;
  this.age = age;
};

Now every time we create a new Dog we have to pass those three arguments to our constructor.

var max = new Dog('Max', 'Golden Retriever', 12);

Coventions

It is possible to set conventions at the document level. These conventions are identical to those from the DocumentStore object and perform the same function. It is important to note that these conventions will always override global conventions that are applied to the document store.

These conventions are:

  • idGenerationStrategy - Allows you to control the generation of keys for new entities.
  • idSeparator - A string that allows you to customize part of the document key generation.

idGenerationStrategy

idGenerationStrategy is used to tell Raven how to generate a key for each document it stores. The default is to use lower cased plural version of the Raven-Clr-Type (plural is stored as Raven-Entity-Name), followed by a forward slash (/) and then an auto generated integer id (e.g. of the format dogs/1, dogs/2, dogs/3).

The only value accepted for idGenerationStrategy is 'guid' which tells Raven to automatically generate a GUID value as the key. For this reason keys are NOT generated when calling Session.Store, but rather when calling Session.Save. This approach may change when/if HiLo key generation is supported.

idSeparator

idSeparator is used for specifying an alternative string to use between the lower case plural prefix and the actual id. The default is '/'.

Technically you can put any value in here, from a single character to a larger string but it is recommended to stick to single characters such as '/', '-' or '_'. Other characters depend on RavenDB itself and may not be valid.

Note: These conventions will always override global conventions, and setting a idGenerationStrategy strategy will override any idSeparator that may be set.

Both of these conventions can be set on a document by using the functions Type.IdGenerationStrategy(strategy) and Type.IdSeparator(seperator). We can set these inside our constructor as well.

var Dog = new raven.Type('Dog');
Dog.constructor = function (name, breed, age) {
  if (!name || !breed || !age) throw new Error('Woof!');

  this.name = name;
  this.breed = breed;
  this.age = age;

  this.IdGenerationStrategy('guid');
  this.IdSeparator('-');   // in this instance this will actually be overridden by GenerateDocumentKey
};

Identity

The default key/id for each document saved is a lower case plural of the Raven-Clr-Type, followed by a forward slash (/) and then an auto generated integer value based on existing documents and document history.

The prefix is necessary because we aren't using strongly typed objects like we would in C#. If we save two types of document with the id 'max', we have no way of determining which we want when performing loads. We could of course supply the load function with the Raven-Clr-Type name such as session.load('Dog', 1, ...);, but we may as well just prefix the id and use session.load('dogs/1', ...);. Prefixing also lends itself nicely to 'pretty' URLs.

We don't however have to use an auto generated key/id, we can supply our own natural key that gets prefixed when saved. We can set this by using the Type.Id(key) function.

var Dog = new raven.Type('Dog');
Dog.constructor = function (name, breed, age) {
  if (!name || !breed || !age) throw new Error('Woof!');

  this.name = name;
  this.breed = breed;
  this.age = age;

  this.Id(name);
};

In this example our generated key would be dogs/max.

Note: If we explicitly make a call to Idon our document with a natural key, it will always override idGenerationStrategy. If a custom idSeparator is set, that, however, will be used in place of the default forward slash.

var Dog = new raven.Type('Dog');
Dog.constructor = function (name, breed, age) {
  if (!name || !breed || !age) throw new Error('Woof!');

  this.name = name;
  this.breed = breed;
  this.age = age;

  this.Id(name);
  this.IdSeperator('-');
};

In this example our generated key would be 'dogs-max'.


Usage

Like the Store, it is recommended to store your base object implementations in their own files and require them as you need them.

Dog.js

var Dog = new raven.Type('Dog');
Dog.constructor = function (name, breed, age) {
  this.name = name;
  this.breed = breed;
  this.age = age;

  this.Id(name);
};

module.exports = Dog;

vet.js

var Dog = require('./Dog');
var max = new Dog('Max', 'Golden Retriever', 12);

You could also store them inside one file or folder and use the module system to require them.

types.js

exports.Dog = new raven.Type('Dog');
Dog.constructor = function (name) {
  this.name = name;
  this.Id(name);
};

exports.Cat = new raven.Type('Cat ');
Cat.constructor  = function (name) {
  this.name = name;
  this.Identity(name);
};

vet.js

var Dog = require('./types').Dog;
var Cat = require('./types').Cat;

var max = new Dog('Max');
var hudson = new Cat('Hudson');
Clone this wiki locally