neo4-js
Version:
Neo4j graphdb object graph mapper for javascript
274 lines (273 loc) • 10.4 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const uuid = require("uuid");
const index_1 = require("./index");
const utils_1 = require("./utils");
function createModelInstance(model, props) {
let instance;
// @ts-ignore
if (model.modelInstanceClass) {
// @ts-ignore
instance = new model.modelInstanceClass(props);
}
else {
instance = new index_1.ModelInstance(props);
}
// @ts-ignore
model.relations.forEach(r => (instance = r.addFunctionsToInstance(instance)));
return instance;
}
exports.createModelInstance = createModelInstance;
function createFakeModelInstance(model, charGenerator) {
let instance;
// @ts-ignore
if (model.modelInstanceClass) {
// @ts-ignore
instance = new model.modelInstanceClass({});
}
else {
instance = new index_1.ModelInstance({ guid: null });
}
// @ts-ignore
model.relations.forEach(r => (instance = r.addFunctionsToInstance(instance, charGenerator)));
return instance;
}
exports.createFakeModelInstance = createFakeModelInstance;
const compareByGuid = (chainItem) => (a, b) => a[chainItem.variable].guid === b[chainItem.variable].guid;
function addChildValues(parents, all, node) {
if (!node)
return parents;
return parents.map(parent => {
const children = addChildValues(lodash_1.uniqWith(all.filter(i => i[node.prev.variable].guid === parent.props.guid), compareByGuid(node)).map(i => {
if (i[node.variable]) {
// @ts-ignore
const instance = node.model.afterFind(createModelInstance(node.model, i[node.variable]));
if (i[node.relationVariable] &&
Object.keys(i[node.relationVariable]).length > 0) {
instance.relationProps = i[node.relationVariable];
}
return instance;
}
return null;
}), all, node.next);
parent[node.propertyName] = node.hasOne
? children.length > 0
? children[0]
: null
: children;
return parent;
});
}
class IncludesQuery {
constructor(chainItem, charGenerator) {
this.chainItem = chainItem;
this.charGenerator = charGenerator;
}
include(fn) {
const { chainItem } = this;
// @ts-ignore
chainItem.next = fn(chainItem.fakeInstance);
chainItem.next.prev = chainItem;
chainItem.next.first = chainItem.first;
return new IncludesQuery(chainItem.next, this.charGenerator);
}
run() {
let match = "MATCH ";
let result = [];
let params = {};
let chainItem = this.chainItem.first;
while (chainItem) {
if (chainItem.first === chainItem) {
match += chainItem.match;
}
else {
match += ` OPTIONAL MATCH (${chainItem.prev.variable})${chainItem.match}`;
}
result = [...result, ...chainItem.result];
params = Object.assign({}, params, chainItem.flatProps);
chainItem = chainItem.next;
}
chainItem = this.chainItem.first;
const query = match + " RETURN " + result.join(", ");
return index_1.default.run(query, params).then(result => {
// @ts-ignore
const instances = addChildValues(lodash_1.uniqWith(result, compareByGuid(chainItem)).map(i => createModelInstance(chainItem.model, i[chainItem.variable])), result, chainItem.next);
return chainItem.first.firstOne
? instances.length > 0
? instances[0]
: null
: instances;
});
}
}
exports.IncludesQuery = IncludesQuery;
class Model {
constructor(label) {
this.label = label;
this.relations = [];
}
beforeCreate(props) {
return props;
}
afterCreate(instance) {
return instance;
}
beforeFind(props) {
return props;
}
afterFind(instance) {
return instance;
}
beforeUpdate(props, newProps) {
return { props, newProps };
}
afterUpdate(instance) {
return instance;
}
create(props) {
return __awaiter(this, void 0, void 0, function* () {
let defaultProps = lodash_1.mapValues(this.modelInstanceClass
? this.modelInstanceClass.prototype._defaultProps
: {}, prop => (typeof prop === "function" ? prop() : prop));
let p = this.beforeCreate(Object.assign({ guid: uuid.v4() }, defaultProps, props));
p = Object.assign({}, p);
const result = yield index_1.default.run(`
CREATE (n:${this.label} {p})
RETURN n
`, { p });
if (!result || result.length !== 1) {
throw new Error(`Create didn't work, cmd: "CREATE (n:${this.label} {p}) RETURN n" with params: ${JSON.stringify({ p })}`);
}
return this.afterCreate(createModelInstance(this, result[0].n));
});
}
findByGuidAndInclude(guid) {
const charGenerator = new utils_1.CharGenerator();
const variable = charGenerator.next();
const { where, flatProps } = utils_1.prepareWhere({ guid }, variable, charGenerator);
// @ts-ignore
const chainItem = {
model: this,
fakeInstance: createFakeModelInstance(this, charGenerator),
match: `(${variable}:${this.label}) ${where}`,
flatProps,
result: [variable],
variable,
firstOne: true,
};
chainItem.first = chainItem;
return new IncludesQuery(chainItem, charGenerator);
}
findByGuid(guid) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield index_1.default.run(`
MATCH (n:${this.label} {guid:{guid}})
RETURN n
`, { guid });
if (!result || result.length > 1) {
throw new Error(`Match didn't work, cmd: "MATCH (n:${this.label} {guid:{guid}}) RETURN n" with params: ${JSON.stringify({
guid,
})}`);
}
if (result.length === 0) {
return null;
}
return createModelInstance(this, result[0].n);
});
}
delete(props, detach = false) {
return __awaiter(this, void 0, void 0, function* () {
const { where, flatProps } = utils_1.prepareWhere(props, "n");
const result = yield index_1.default.run(`
MATCH (n:${this.label})
${where}
${detach ? " DETACH " : ""} DELETE n
`, flatProps);
return result._stats.nodesDeleted;
});
}
findAndInclude(props) {
const charGenerator = new utils_1.CharGenerator();
const variable = charGenerator.next();
const { where, flatProps } = utils_1.prepareWhere(this.beforeFind(props), variable, charGenerator);
// @ts-ignore
const chainItem = {
model: this,
fakeInstance: createFakeModelInstance(this, charGenerator),
match: `(${variable}:${this.label}) ${where}`,
flatProps,
result: [variable],
variable,
};
chainItem.first = chainItem;
return new IncludesQuery(chainItem, charGenerator);
}
find(props) {
return __awaiter(this, void 0, void 0, function* () {
const p = this.beforeFind(props);
const { where, flatProps } = utils_1.prepareWhere(p, "n");
const result = yield index_1.default.run(`
MATCH (n:${this.label})
${where}
RETURN n
`, flatProps);
return result.map(p => this.afterFind(createModelInstance(this, p.n)));
});
}
findOneAndInclude(props) {
const charGenerator = new utils_1.CharGenerator();
const variable = charGenerator.next();
const { where, flatProps } = utils_1.prepareWhere(this.beforeFind(props), variable, charGenerator);
// @ts-ignore
const chainItem = {
model: this,
fakeInstance: createFakeModelInstance(this, charGenerator),
match: `(${variable}:${this.label}) ${where}`,
flatProps,
result: [variable],
variable,
firstOne: true,
};
chainItem.first = chainItem;
return new IncludesQuery(chainItem, charGenerator);
}
findOne(props) {
return __awaiter(this, void 0, void 0, function* () {
const p = this.beforeFind(props);
const { where, flatProps } = utils_1.prepareWhere(p, "n");
const result = yield index_1.default.run(`
MATCH (n:${this.label})
${where}
RETURN n
`, flatProps);
if (result.length) {
return this.afterFind(createModelInstance(this, result[0].n));
}
return Promise.resolve(null);
});
}
update(props, newProps) {
return __awaiter(this, void 0, void 0, function* () {
const params = this.beforeUpdate(props, newProps);
const { where, flatProps } = utils_1.prepareWhere(params.props, "n");
const { str: setPropsStr, newProps: _newProps } = utils_1.prepareSet(params.newProps, "n");
let result = yield index_1.default.run(`
MATCH (n:${this.label})
${where}
SET ${setPropsStr}
RETURN n
`, Object.assign({}, flatProps, _newProps));
return result.map(p => this.afterUpdate(createModelInstance(this, p.n)));
});
}
}
exports.Model = Model;