UNPKG

livia-orientdb

Version:

OrientDB adapter for universal database driver Livia

306 lines (250 loc) 8.59 kB
import { Adapter, Model, Index, Types } from 'livia'; import Query from './Query'; import orientjs from 'orientjs'; import { waterfall, each } from 'async'; import debug from 'debug'; import Collate from './constants/Collate'; const log = debug('livia-orientdb:adapter'); export default class OrientDBAdapter extends Adapter { constructor(options, dbOptions) { super(options); this._dbOptions = typeof dbOptions === 'string' ? { name: dbOptions } : dbOptions; } get dbOptions() { return this._dbOptions; } createConnection(callback) { const server = orientjs(this.options); const db = server.use(this.dbOptions); callback(null, db); } query(model, options) { return new Query(model, options); } getIndexType(options) { let type = options.unique ? 'UNIQUE' : 'NOTUNIQUE'; if (options.type === Index.DICTIONARY) { type = 'DICTIONARY'; } else if (options.type === Index.FULLTEXT) { if (options.engine === 'lucene') { return 'FULLTEXT ENGINE LUCENE'; } type = 'FULLTEXT'; } else if (options.type === Index.SPATIAL) { return 'SPATIAL ENGINE LUCENE'; } if (options.hash) { type += '_HASH_INDEX'; } return type; } ensureIndex(model, OClass, callback) { const adapter = this; const db = this.native; const className = model.name; const schema = model.schema; waterfall([ (cb) => { // todo speed up for each class is same db.index.list(true).then((indexes) => { // filter indexes for current class const filteredIndexes = indexes.filter((index) => { const def = index.definition; if (!def || def.className !== className) { return false; } return true; }); cb(null, filteredIndexes); }, cb); }, // remove unused indexes (indexes, cb) => { if (!model.options.dropUnusedIndexes) { return cb(null, indexes); } each(indexes, (index, cb2) => { const { name } = index; let schemaIndexName = name; const indexStartName = className + '.'; if (schemaIndexName.indexOf(indexStartName) === 0 ) { schemaIndexName = schemaIndexName.substr(indexStartName.length); } if (schema.hasIndex(schemaIndexName)) { return cb2(null); } log('Deleting unused index: ' + name); db.index.drop(name).then(() => { cb2(null); }, cb2); }, (err) => { if (err) { return cb(err); } cb(null, indexes); }); }, // add non exists indexes (indexes, cb) => { const configs = []; each(schema.indexNames, (orientIndexName, cb3) => { const index = schema.getIndex(orientIndexName); // add class name to indexName const indexName = className + '.' + orientIndexName; const oIndex = indexes.find((index2) => { return index2.name === indexName; }); if (oIndex) { return cb3(null); } let canCreate = true; Object.keys(index.properties).forEach((name) => { if (name.indexOf('.') !== -1) { canCreate = false; } log('Index for subschemas is not supported yet: ' + name); }); if (!canCreate) { return cb3(null); } log('Creating index: ' + indexName); const config = { 'class': className, name: indexName, properties: Object.keys(index.properties), type: adapter.getIndexType(index), metadata: index.metadata, }; configs.push(config); db.index.create(config).then(() => { cb3(null); }, cb3); }, (err) => { if (err) { return cb(err); } cb(null, indexes); }); }, ], callback); } ensureClass(model, callback = () => {}) { const db = this.native; const schema = model.schema; const className = model.name; waterfall([ // prepare base class (cb) => { db.class.get(className).then((OClass) => { cb(null, OClass); }, () => { db.class.create(className, schema.extendClassName, model.options.cluster, model.options.abstract).then((OClass) => { cb(null, OClass); }, cb); }); }, // retrive a current properties (OClass, cb) => { OClass.property.list().then((properties) => { cb(null, OClass, properties); }, cb); }, // drop unused properties (OClass, oProperties, cb) => { if (!model.options.dropUnusedProperties) { return cb(null, OClass, oProperties); } each(oProperties, (prop, cb2) => { if (schema.has(prop.name)) { return cb2(null); } OClass.property.drop(prop.name).then(() => { cb2(null); }, cb2); }, (err) => { if (err) { return cb(err); } cb(null, OClass, oProperties); }); }, // add new properties (OClass, oProperties, cb) => { const properties = schema.propertyNames(); each(properties, (propName, cb2) => { const prop = oProperties.find((p) => { return p.name === propName; }); if (prop) { return cb2(null); } const schemaProp = schema.getPath(propName); const SchemaType = schema.getSchemaType(propName); if (SchemaType === Types.Mixed) { return cb2(null); } const type = SchemaType.getDbType(schemaProp); if (schemaProp.options.metadata || schemaProp.options.ensure === false) { return cb2(null); } waterfall([ // create LinkedClass for embedded documents (cb3) => { if (!SchemaType.isAbstract(schemaProp)) { return cb3(null, null); } const abstractClassName = SchemaType.computeAbstractClassName(className, propName); const embeddedSchema = SchemaType.getEmbeddedSchema(schemaProp); log(`Founded abstract class: ${abstractClassName} with schema: ` + !!embeddedSchema); if (!abstractClassName || !embeddedSchema) { return cb3(null, null); } return new Model(abstractClassName, embeddedSchema, model.connection, { 'abstract': true, }, cb3); }, (model2, cb3) => { const options = schemaProp.options; const additionalConfig = SchemaType.getPropertyConfig(schemaProp); const config = { name: propName, type, mandatory: options.mandatory || options.required || false, min: typeof options.min !== 'undefined' ? options.min : null, max: typeof options.max !== 'undefined' ? options.max : null, collate: options.collate || Collate.DEFAULT, notNull: options.notNull || false, readonly: options.readonly || false, ...additionalConfig, }; if (model2) { config.linkedClass = model2.name; } if (config.linkedType && config.linkedClass) { delete config.linkedType; } OClass.property.create(config).then((oProperty) => { oProperties.push(oProperty); cb3(null); }, cb3); }, ], cb2); }, (err) => { if (err) { return cb(err); } cb(null, OClass, oProperties); }); }, (OClass, oProperties, cb) => { this.ensureIndex(model, OClass, cb); }, ], (err) => { if (err) { return callback(err); } callback(null, model); }); } }