Socket
Socket
Sign inDemoInstall

ee-orm

Package Overview
Dependencies
Maintainers
2
Versions
156
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ee-orm - npm Package Compare versions

Comparing version 0.2.2 to 0.2.3

8

lib/Database.js

@@ -20,5 +20,5 @@ !function(){

// orm
this._setProrperty('_orm', options.orm);
this._setProrperty('_database', options.database);
this._setProrperty('_queryBuilders', {});
this._setProperty('_orm', options.orm);
this._setProperty('_database', options.database);
this._setProperty('_queryBuilders', {});

@@ -45,3 +45,3 @@ // initialize the orm

, _setProrperty: function(name, value){
, _setProperty: function(name, value){
Object.defineProperty(this, name, {value: value, writable: true});

@@ -48,0 +48,0 @@ }

@@ -18,4 +18,4 @@ !function(){

//log.error('initializing', options.definition.name);
this._setProrperty('_defintion', options.definition);
this._setProrperty('_orm', options.orm);
this._setProperty('_defintion', options.definition);
this._setProperty('_orm', options.orm);

@@ -26,14 +26,14 @@ // check for changes

this._setProrperty('_values', {});
this._setProrperty('_changedValues', []);
this._setProrperty('_mappings', {});
this._setProrperty('_belongsTo', {});
//this._setProrperty('_columns', {});
this._setProrperty('_references', {});
this._setProrperty('_changedReferences', []);
this._setProrperty('_mappingIds', [], true);
this._setProrperty('_hasChanges', false, true);
this._setProrperty('_relatingSets', options.relatingSets);
this._setProperty('_values', {});
this._setProperty('_changedValues', []);
this._setProperty('_mappings', {});
this._setProperty('_belongsTo', {});
//this. _setProperty('_columns', {});
this._setProperty('_references', {});
this._setProperty('_changedReferences', []);
this._setProperty('_mappingIds', [], true);
this._setProperty('_hasChanges', false, true);
this._setProperty('_relatingSets', options.relatingSets);
this._setProrperty('_fromDb', options.isFromDB || false);
this._setProperty('_fromDb', options.isFromDB || false);

@@ -99,3 +99,3 @@ //this._initializeRelatingSets(true);

, _setProrperty: function(name, value, writable){
, _setProperty: function(name, value, writable){
Object.defineProperty(this, name, {value: value, writable: writable});

@@ -286,3 +286,3 @@ }

else {
}

@@ -329,4 +329,33 @@ }.bind(this));

, _saveChildren: function(transaction, noReload, callback) {
asyn.wait(function(done) {
this._saveReferences(transaction, noReload, done);
}.bind(this)
, function(done) {
this._saveMappings(transaction, noReload, done);
}.bind(this)
, function(done) {
this._saveBelongsTo(transaction, noReload, done);
}.bind(this), callback);
}
// save references
, _saveBelongsTo: function(transaction, noReload, callback) {
async.each(this._belongsTo, function(belongsTo, next){
belongsTo.save(transaction, noReload, next);
}.bind(this), callback);
}
, _saveMappings: function(transaction, noReload, callback) {
async.each(this._mappings, function(mapping, next){
mapping.save(transaction, noReload, next);
}.bind(this), callback);
}
, _saveReferences: function(transaction, noReload, callback) {
async.each(this._changedReferences, function(key, next){

@@ -360,8 +389,5 @@ var value = this._references[key]

}
}.bind(this), function(err, results){
if (err) callback(err);
else callback();
}.bind(this));
}.bind(this), callback);
}
});
}();

@@ -127,2 +127,3 @@ !function(){

, database: options.databaseName
, isMapping: true
});

@@ -233,2 +234,3 @@ }

, database: databaseName
, isMapping: false
});

@@ -235,0 +237,0 @@

@@ -24,5 +24,5 @@ !function(){

, init: function(options) {
this._setProrperty('_options', options);
this._setProrperty('_dbNames', []);
this._setProrperty('_databases', {});
this._setProperty('_options', options);
this._setProperty('_dbNames', []);
this._setProperty('_databases', {});

@@ -38,3 +38,3 @@ // db connectivity

, _setProrperty: function(name, value){
, _setProperty: function(name, value){
Object.defineProperty(this, name, {value: value});

@@ -97,3 +97,2 @@ }

column.mapsTo.forEach(function(mapping){
mapping.name = this._getPluralAccessorName(mapping.name);
name = mapping.name;

@@ -118,3 +117,2 @@

column.belongsTo.forEach(function(beloning){
beloning.name = this._getPluralAccessorName(beloning.name);
name = beloning.name;

@@ -183,19 +181,7 @@

}
, _getPluralAccessorName: function(id) {
/*var parts = id.match(/(?:^|[A-Z0-9])[^A-Z0-9]+/g);
if (parts){
if (parts.length === 1) id = pluralize.plural(id);
else id = parts.slice(0, parts.length-1).join('')+pluralize.plural(parts[parts.length-1]);
}
else id = pluralize.plural(id);
*/
return id;
}
});
staticORM = new StaticORM();

@@ -202,0 +188,0 @@ Object.keys(Object.getPrototypeOf(staticORM)).forEach(function(key){

@@ -10,6 +10,8 @@ !function(){

module.exports = function(options){
var proto = Array.prototype;
var proto = Array.prototype;
module.exports = function(options){
return Object.create(Array.prototype, {

@@ -37,2 +39,10 @@

// queries beeing executed
, _workers: {
value: 0
}
// all records that were added to the colelction

@@ -49,10 +59,6 @@ , _addedRecords: {

// busy flag
, _isBusy: {
value: false
}
// collect errors when selecting records to add to this set
, _errors: {
value: []
, _error: {
value: null
}

@@ -87,2 +93,3 @@

, _executeSave: {value: function(connection, noReload, callback) {

@@ -107,5 +114,2 @@

}}

@@ -124,64 +128,65 @@

// marks the relatingset as idle (not executing select queries)
, _idle: {value: function(err) {
if (err) this._errors.push(err);
this._isBusy = false;
this.emit('idle');
}}
// the the relatinset as busy (currently fetching records to add to this relation)
, _busy: {value: function() {
this._isBusy = true;
this.emit('busy');
}}
// push all records
/*
* the push() method accepts eiteher a quer or a saved or not saved
* model of the target entity. if it gets a query it execute it immediatelly
* in order to get the records which it shoudl link. it wil lpush all models
* added or found via query to the _addedRecords array which will be stored /
* linked when the save method gets calleds
*
* @param <Object> item: query or model
* @param
*/
, push: { value: function push (item, callback) {
callback = callback || function(){};
// check if the item is a model or a query
if (item.isQuery) {
this._busy();
// execute the query, add it to
item.find(function(err, records){
this._idle(err);
if (type.object(item)) {
// check if the item is a model or a query
if (item.isQuery) {
this._workers++;
if (err) callback(err);
else {
records.forEach(function(model){
var err;
// execute the query, add it to
item.find(function(err, records){
this._workers--;
// TODO: proper typechek
if (this._isCorrectType(model)){
push.parent(model);
this._addedRecords.push(model);
}
else {
// empty array
records.splice(0, records.length);
if (err) callback(err);
else {
records.forEach(function(model){
var err;
err = new Error('Attempt to add models of type «'+model.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
if (callback) callback(err);
else this._errors.push(err);
}
}.bind(this));
}
}.bind(this));
}
else {
// TODO: proper typechek
if (this._isCorrectType(model)){
push.parent(model);
this._addedRecords.push(model);
callback();
// TODO: proper typechek
if (this._isCorrectType(model)){
this._addedRecords.push(model);
}
else {
err = new Error('Attempt to add models of type «'+model.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
this._error = err;
callback(err);
}
}.bind(this));
}
}.bind(this));
}
else {
err = new Error('Attempt to add models of type «'+model.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
if (callback) callback(err);
else this._errors.push(err);
// TODO: proper typechek
if (this._isCorrectType(item)){
proto.call(this, item);
this._addedRecords.push(item);
callback();
}
else {
err = new Error('Attempt to add models of type «'+model.getName()+'» to a relatingset of type «'+this._definition.name+'» via a query!');
this._error = err;
callback(err);
}
}
}
else {
err = new Error('Push was called with an invalid value: «'+type(item)+'»')
this._error = err;
callback(err);
}
}}

@@ -286,4 +291,8 @@

, toJSON: {value: function() {
return Array.prototype.slice.call(this);
}}
});
};
}();
{
"name" : "ee-orm"
, "description" : "a simple yet powerful javascript orm for node.js supporting mysql & postgres"
, "version" : "0.2.2"
, "version" : "0.2.3"
, "homepage" : "https://github.com/eventEmitter/ee-orm"

@@ -6,0 +6,0 @@ , "author" : "Michael van der Weg <michael@eventemitter.com> (http://eventemitter.com/)"

# ee-orm
WIP
An easy to use ORM for node.js. This ORM works only with proper designed relational databases. It gets al information needed from the DB itself, you don't have to manually create models. It supports advanced eager loading, complex queries, joins, transactions, complex database clusters & connection pooling.
-- alpha software --, expect a stable release in june 2014.
## installation
install via npm, you may only install the packages required for you rdbms. currently supported are postgres & mysql.
npm install ee-orm ee-postgres-connection ee-mysql-connection
## Versions
If the api changes the minor version number will change. So if you use the version «0.2.x» you will have always the same stable api.
## build status

@@ -13,3 +21,445 @@

## usage
## usage
### Example
var ORM = require('ee-orm');
// load models from the «eventdata» db
var orm = new ORM({
eventdata: {
type: 'postgres'
, hosts: [ // you may add as many hosts as you want
{
host : 'mydbcuster.mycompyny.mytld'
, username : 'eventsUsers'
, password : 'securebeyondanything'
, port : 5432
, mode : 'readwrite' // readonly or writeonly would be other options
}
]
}
});
// you may not interact with the orm before it was laoded
orm.on('load', function(err){
if (err) console.log('failed to load ORM!', err);
else {
// the orm creates dynamically models from your normalized relational database structure
// lets say the db has an events table. an event can have many venues (mapping, m:n).
log(orm); // { events: { events: {}, venues: {}}}
// you may create complex queries using the generated model structure. the orm
// has scanned all relations and knows whic entity belongs to which, there is also
// support for many to many reltions using mapping tables.
// load events with the ids 1-6, get also the venues for the events but select only the
// id and the name attribute
orm.eventdata.events({id: ORM.in(1,2,3,4,5,6)}, ['*']).fetchVenues(['id', 'name']).limit(10).find(function(err, events){
if (err) log(err);
else {
// get first event
log(events.first()); // {id:1, title: "prodigy", startdate: "2012-07-08 22:00:00", venues: [{id: 45, name: "via felsenau"}]};
// add new venue to the second event. the events list is an array with advanced functions
// the relation to the venues table was automatically on the events model.
events[2].venues.push(new orm.eventdata.venues({name: 'dachstock'}));
events[2].save(function(err){
// the venue was inserted to the venue table, the mapping to the event was created
});
}
});
// lets say you want to get all events for a specific venue. you have two possiblitie to do this.
orm.eventdata.events(['id']).fetchVenues({id: 56}).find(function(err, events){
log(events); // [{id:1}, {id:2}]
});
orm.eventdata.venues({id: 56}).events(['id']).find(function(err, venues){
log(venues); // [{id:56, events: [{id:1}, {id:2}]}]
});
}
});
### API
#### Constructor
The consturctor expects the complete configuration used for accessing a database or cluster. The ORM makes always use of a conenction pool which may be configured in the config described below.
var ORM = require('ee-orm');
var ormInstance = new ORM(config);
For every Database used by the ORM you have to provide a complete configuration stored on the the key which must be the name of the database. When using the postgres driver the schema has to have the same name as the database itself.
// database names «eventdata» and «shopping». the «eventdata» database
// is a single mysql server, the «shopping» database consists of a
// postgres master and a read replica. the orm tries to use the read replica
// for readonly queries
{
eventdata: {
type: 'mysql' // postgres or mysql
, hosts: [
{
host : 'mydbcuster.mycompyny.mytld'
, username : 'eventsUsers'
, password : 'securebeyondanything'
, port : 3306
, mode : 'readwrite' // readonly or writeonly would be other options
, maxConenctions : 50 // optional, defaults to 50
}
]
}
, shopping: {
type: 'postgres'
, hosts: [
{
host : 'mydbcuster.mycompyny.mytld'
, username : 'eventsUsers'
, password : 'securebeyondanything'
, port : 5432
, mode : 'readwrite'
, maxConenctions : 20
}
, {
host : 'mydbcuster.mycompyny.mytld'
, username : 'eventsUsers'
, password : 'securebeyondanything'
, port : 5432
, mode : 'readonly'
, maxConenctions : 200
}
]
}
}
The orm gets the complete structure of every datbase specified in the config. For each database it creates a representation which it stores on itself. The configuration above will result in the object described below. The ORM cannot be used before the «load» event was fired.
ormInstance.on('load', function(err){
if (err) log(err);
else {
log(ormInstance); // {eventdata: {...}, shopping: {...}}
}
});
if you wish we can add support for dynamic adding & removing of database servers.
#### QueryBuilder
The querybuilder has support for complex queries. Currently some queries are not supported (please report an issue if there is missing something you like to use). You have to use the «queryRaw» method if you need to execute such queries.
Lets take the configuration defined above an see what tables the «eventdata» database has. The ORM scans every database for all tables and all relations between them. There are on naming conventions or requirements exept from some reserved keywords (see «reserved keywords»).
log(ormInstance.eventdata);
// {
// event: {} // table containing events
// , event_image: {} // mapping to the image table
// , event_language: {} // mapping to the language table (locales)
// , event_venue: {} // mapping tot he venue table, an event can have many venues
// , image: {} // the images table
// , language: {} // the languages
// , venue: {} // th e venues table
// , venue_image: {} // the mapping between venues and images
// , venue_locale: {} // venue locales
// }
Every entity represented in the object above is a querybuilder and has the same set of methods on it:
- delete: execute the built query, delete the result set (DELETE FROM ...)
- filter: apply a subfilter for nested data loading, see below)
- find: execute the query(SELECT x, y, z FROM ...)
- findOne: same as the find method, but limit the result to one result
- limit: limit your query
- offset: add an offset to your query
- update: execute your query, update the result set (UPDATE set x=Y, ... WHERE ... )
Every querybuilder has methods linking to all referenced tables. These methods are built using the referenced tables name and the keyword «get» and «fetch». The «get» and «fetch» method do the exact same thing, they differ only in the scope returned. The «event» querybuilder for example has the the following methods added because of its relations to other tables:
- getImage
- fetchImage
- getLanague
- fetchLanguage
- getVenue
- fetchVenue
if you don't know which methods are available on an entity you can use the «describeMethods» method for listing them.
ormInstance.eventdata.event().describeMethods();
// { getImage: 'Mapping accessor for the «image» Model', .... }
So, what exactly are those generated methods useful for? They let you build complex queries with ease. They make it also possible to fetch unlimited nested data from the database (it will get slower with each additional level you're fetching).
Each of the method accepts the following arguments (of whic all are optional) in any order.
- Object: a filter statement
- Array: a select statement
Lets fetch some events:
// select all events in the table, returns only their id (the primary key is selected automatically)
ormInstance.eventdata.event().find(function(err, events){
});
// select all events, return all columns
ormInstance.eventdata.event([*]).find(function(err, events){
});
// select all events, return the id and the title columns
ormInstance.eventdata.event(['id', 'title']).find(function(err, events){
});
Lets select & filter some events:
// fecth the event with the id 9, select the id only
ormInstance.eventdata.event({id: 9}).find(function(err, events){
});
// fecth the event with the id 7,8,9, select the id and title
ormInstance.eventdata.event({id: ORM.in([7, 8, 9])}, ['id', 'title']).find(function(err, events){
});
Lets select all events of the venue with the id 56, select the events title and id
ormInstance.eventdata.event(['id', 'title']).fetchVenue({id:56}).find(function(err, events){
});
// the exact same query as above (get vs fetch)
ormInstance.eventdata.event(['id', 'title']).getVenue({id:56}).find(function(err, events){
});
Lets select all events of the venue with the id 56, select the events title and id and all columns of the venue
ormInstance.eventdata.event(['id', 'title']).getVenue(['*'], {id:56}).find(function(err, events){
});
Now lets also get the images of the venue (get vs fetch)
// loads all events of the venue with the id 56 and all images attched to the venue
//the getVenue did change the scope to the venue entity, so the fetchImage points to
// the images of the ***venue***
ormInstance.eventdata.event(['id', 'title']).getVenue(['*'], {id:56}).fetchImage(['*']).find(function(err, events){
});
// fetchVenue vs getVenue: because we used fetchVenue instead of getVenue the scope
// stays on the previuos item which is in this case the the «event» entity. this query
// fetches all events of the venue with the id 56 and all images attched to the ***event***
ormInstance.eventdata.event(['id', 'title']).fetchVenue(['*'], {id:56}).fetchImage(['*']).find(function(err, events){
});
Now lets do a complex query:
// get all columns of the event table, dont filter yet
var myQuery = ormInstance.eventdata.event(['*']);
// filter events by the venue with the id 56, select all columns of the venue
// table, get the description locale from the locale table where the language
// is english.
// the filter method is called on the language entity because we want all the
// events and not only those with english locales (join vs. left join) but we
// want only the english locales. filter events by locale vs. filter locales
myQuery.getVenue({id:56}, ['*']).fetchImage(['*']).getVenue_language(['description']).getLanguage().filter({code: 'en'});
// get all event images, get the english event locale
myQuery.fetchImage(['*']).getEvent_language(['description']).fetchLanguage({code: 'en'});
// execute the query
myQuery.find(function(err, events){
log(events);
// [
// {
// id: 34
// , title: 'bookashade'
// , event_language: [
// {
// id_event: 32
// , id_language: 1
// , description: 'lorem ipsum ...'
// }
// ]
// , image: [
// {
// id: 34
// , url: 'http://eventemitter.com/...'
// }
// , {
// id: 35
// , url: 'http://eventemitter.com/...'
// }
// ]
// , venue: [
// {
// id: 56
// , name: 'Lavo'
// , venue_language: [
// {
// id_venue: 56
// , id_language: 1
// , description: 'lorem ipsum ...'
// }
// ]
// , image: [
// {
// id: 456
// , url: 'http://eventemitter.com/...'
// }
// , {
// id: 978
// , url: 'http://eventemitter.com/...'
// }
// ]
// }
// ]
// }
// ]
});
#### Reserved Keywords
On the ORM itself the following keywords are reserved (your database should not have a name which is listed below):
- $$$$_events
- __proto__
- _initializeDatabases
- _initializeOrm
- _manageAccessorNames
- _setProperty
- addListener
- emit
- getDatabase
- init
- listener
- off
- on
- once
- prototype
- hasOwnProperty
- toString
- toJSON
On the Database level the following keywords are reserved (your database should not contain any tables using on of the names listed below):
- $$$$_events
- __proto__
- _delete
- _getChangedValues
- _getDatabase
- _getFilters
- _getOptions
- _getSelect
- _handleBelongsTo
- _handleMapping
- _handleReference
- _initialize
- _parseFilter
- _parseSelect
- _save
- _saveBelongsTo
- _saveChildren
- _saveMappings
- _saveReferences
- _setChanged
- _setProperty
- _setValues
- addListener
- clone
- createTransaction
- delete
- emit
- executeQuery
- filter
- find
- findOne
- getDefinition
- getEntityName
- hasOwnProperty
- init
- isFromDatabase
- isSaved
- limit
- listener
- loadAll
- off
- offset
- on
- once
- prototype
- reload
- save
- toJSON
- toString
On the Model level the following keywords are reserved (your tables should not contain any columns using on of the names listed below):
- $$$$_events
- __proto__
- _delete
- _getChangedValues
- _getFilters
- _getOptions
- _getSelect
- _handleBelongsTo
- _handleMapping
- _handleReference
- _parseFilter
- _parseSelect
- _save
- _saveBelongsTo
- _saveChildren
- _saveMappings
- _saveReferences
- _setChanged
- _setProperty
- _setValues
- addListener
- clone
- delete
- emit
- filter
- find
- findOne
- getDefinition
- getEntityName
- hasOwnProperty
- init
- isFromDatabase
- isQuery
- isSaved
- limit
- listener
- loadAll
- off
- offset
- on
- once
- prototype
- reload
- save
- toJSON
- toString

@@ -21,5 +21,8 @@

while(i--) arr.push(1);
//log(orm);
log(orm);
log(orm.eventbooster.resource().describeMethods());
log.wtf('hajo jüfe');
return;
/**

@@ -85,3 +88,3 @@ // insert 1000 roles

var transaction = orm.eventbox.createTransaction()
, query = transaction.event(['*']).limit(10).offset(0);
, query = transaction.event(['*'], {_:[{id:3},{id:2}]}).limit(10).offset(0);

@@ -88,0 +91,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc