cassandra-jpa
v3.0.5
Published
Imergo Node.js Javascript Persistence API for Apache Cassandra.
Downloads
68
Maintainers
Readme
Javascript (Node.js) Persistence API (JPA) for Apache Cassandra
A persistence layer for using Apache Cassandra with Node.js based on the latest DataStax Cassandra Driver.
This module brings features from the Java World (JPA) and try to make life with Cassandra easier, mostly for people coming from the JAVA World.
The idea is to make the API as similar as possible to the latest JPA so that Java - and not only - developers to start easily with cassandra. Last but not least, this modules provides a good base for any Node.js developer for any kind of project that uses cassandra as its reposistory.
Installation
$ npm install cassandra-jpa
Prerequistics
- ES6 (Node.js > 4.1.0)
- CQL 3
- Compatible with Cassandra / Cassandra driver 3
Design
- Flexible configuration
- Datastax Cassandra Driver Configuration
- Configurable entity class, (TODO: allow for not having to extend - interface-like style)
- OOP using classes
- Self explainable Java (JPA) API based/inspired
- NODE.js best practices
Features
- Create, Drop Cassandra Tables programmatically
- Create Indexes
- Persist Javascript entities (JS Class extending Entity or Pure JS Objects)
- Remove Javascript entities
- Flexible configuration
- Datastax Cassandra Driver Configuration
- Configurable base entity class, (or use of Symbol as Interface still TODO)
- Logging through the
debug
module (set the environment variableDEBUG
tocassandra-jpa
) - ES6 (Node.js > 4.0.0)
- OOP using classes
- Self explainable API
- Following NODE.js best practices
Basic usage
Import the cassandra-persistence module
let jpa = require('cassandra-jpa');
Create your Entity Class Extending (optionally, see baseEntityClass in the configuration) the base Entity
Requirements
- toJSON function should return entity property names same with table fields
Configure your persistence
configuration = new jpa.JPAConfiguration();
configuration.cassandra.contactPoints = ["localhost"];
configuration.cassandra.keyspace = "tests";
Make sure you defined the TTL - default in config or in every MetaModel. Set undefined if you do not need a TTL
You can use also ENV vars:
$ export CASSANDRA_DBHOST=host1,host2,host3
$ export CASSANDRA_KEYSPACE=mykeyspace
$ export CASSANDRA_TTL=Number of seconds for default TTL
Implement your MetaModel class by extending the MetaModel
If you need to override the default toRow, fromRow function of the MetaModel, you need to extend the MetaModel class.
class FooMetaModel extends MetaModel {
constructor(jpaConfig)
{
super(jpaConfig);
this.name = "foo";
this.fields = new Map([["id", "timeuuid"], ["name", "text"], ["created", "timeuuid"],
["entity", "text"], ["entities", "list<text>"], ["simpleObjects", "list<text>"],
["enabled", "boolean"]]);
this.partitionKeys = ["id"];
this.clusteringColumns = new Map([["name", "ASC"]]);
this.secondaryIndexes = ["name"];
this.entityClass = Foo;
this.ttl = undefined; //no ttl, or X secs, e.g. 86400 for one day
}
}
Building criteriaQuery example
let emFactory = m.Persistence.createEntityManagerFactory("Foo", config);
let entityManager = emFactory.createEntityManager(fooMetaModel);
let cb = entityManager.getCriteriaBuilder();
let cq = cb.createQuery();
let op1 = cb.equal("id", TimeUuid.fromString(foo.id));
let op2 = cb.equal("name", foo.name);
let limit = 1;
let orderBy = "name";
let criteriaQuery = cq.where(cb.and([op1, op2]),limit, orderBy, true);
entityManager.findOne(function (error, res)
{
assert(newFoo instanceof Foo);
return callback(error, res);
}, criteriaQuery);
Override - extend object model adaptation methods in you MetaModel
This need to be done if the DefaultRowInterceptor does not cover your needs. Alternatively you could extend or replace the DefaultRowInterceptor and maybe also contribute the ideas here.
toRow(entity)
{
// my specifics
let row = super.toRow(entity);
// my specifics
return row;
}
fromRow(row)
{
let entity = super.fromRow(row);
// my specifics
return entity;
}
When cassandra table should have extra properties comparing to the entity
In our metamodel class, initialize the extra params by giving the param names and an initial value (mostly null).
// add the extra field to the db schema
this.fields.set("newField", "text");
// allow for taking into account when querying
this.extraParams.set("newField", null);
Then when persisting we add the extra params like this:
fooMetaModel.extraParams.set("newField", "some value");
And when we select from db we get of course the entity (missing the extra params) but we can get the extra ones doing:
fooMetaModel.extraParams.get("newField")
API
EntityManger
| Function | Arguments |Returns |Description | | ------------- | ------------- |------------- |------------- | | persist | entity, callback, [metaModel] | callback(error, result) | Persist an entity | | persistAll | entities, callback, [metaModel] | callback(error, result) | Persist an arry of entities | | updateByCriteria | entity, callback, criteriaQuery, [metaModel] | callback(error, result) | Update Row based on criteriaQuery | | removeByCriteria | callback, criteriaQuery, [metaModel] | callback(error, result) | Update Row(s) based on criteriaQuery | | findOne | callback, criteriaQuery, [metaModel] | callback(error, result) | Find one entity based on criteriaQuery | | findAll | callback, criteriaQuery, [metaModel] | callback(error, result) | Find all entities based on criteriaQuery | | query | queryObject, callback | callback(error, result) | general cassandra driver query | | truncate | queryObjects, callback | callback(error, result) | Truncate a table based on MetaModel | | createTable | callback, [metaModel] | callback(error, result) | Create a table based on MetaModel | | insertIndexes | callback, [metaModel] | callback(error, result) | Create indexes based on MetaModel | | dropIndexes | callback, [metaModel] | callback(error, result) | Drop indexes based on MetaModel | | dropTable | callback, [metaModel] | callback(error, result) | Drop Table based on MetaModel | | getCriteriaBuilder | [metaModel] | CriteriaBuilder | get the CriteriaBuilder | | getCriteriaQuery | [metaModel] | CriteriaQuery | get the CriteriaQuery |
Note: When an EntityManger is initiated using metaModel argument, the [metaModel] can be ommited.
CriteriaBuilder
| Function | Arguments |Returns |Description | | ------------- | ------------- |------------- |------------- | | and | expressions:array | q:string | combine expressions with and AND | | equal | x:string, y:ALL | q:string | Tests whether two expressions are equal | gt | x:string, y:ALL | q:string | Tests whether the first numeric expression is greater than the second numeric expression | | ge | x:string, y:ALL | q:string | Tests whether the first numeric expression is greater than or equal to the second numeric expression | | lt | x:string, y:ALL | q:string | Tests whether the first numeric expression is less than the second numeric expression | | le | x:string, y:ALL | q:string | Tests whether the first numeric expression is less than or equal to the second numeric expression |
CriteriaQuery
| Function | Arguments |Returns |Description | | ------------- | ------------- |------------- |------------- | | from | q | q:string | | | where | q, [limit:Number] , [orderBy:String], [allowFiltering:boolean] | q:string | |
Extending
DefaultRowInterceptor
The DefaultRowInterceptor
is the defaul and abstract way that jpa maps the entity field to cassandra rows depending on the field type defined in MetaModel. The following types are supported:
- uuid mapped to require('cassandra-driver').types.TimeUuid
- timeuuid mapped to require('cassandra-driver').types.TimeUuid
- text mapped to JSON.stringnify string
- varchar mapped to JSON.stringnify string
- list mapped to array of JSON.stringnify
- list mapped to array of require('cassandra-driver').types.TimeUuid
The ones not listed are kept as they are, i.e. native support like string, Map etc...
The DefaultRowInterceptor
can be extended and overriden.
TODO Features
- Count Results
- Count time
Licence
See license file.