neogm
Version:
TypeScript-first Neo4j Object Graph Mapper (OGM) for building type-safe graph database applications
175 lines • 6.62 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseEntity = void 0;
const metadata_1 = require("../decorators/metadata");
class BaseEntity {
constructor(connectionManager) {
this._isLoaded = false;
this._connectionManager = connectionManager;
}
setId(id) {
this._id = id;
}
getId() {
return this._id;
}
setConnectionManager(connectionManager) {
this._connectionManager = connectionManager;
}
getConnectionManager() {
if (!this._connectionManager) {
throw new Error('Connection manager not set. Entity must be created through repository or have connection manager injected.');
}
return this._connectionManager;
}
markAsLoaded() {
this._isLoaded = true;
}
isLoaded() {
return this._isLoaded;
}
async save() {
const connectionManager = this.getConnectionManager();
const session = connectionManager.getSession();
try {
const metadata = this.getMetadata();
const properties = this.getProperties();
// Validate required properties
this.validateProperties(metadata, properties);
const propsString = Object.keys(properties).length > 0
? `{${Object.keys(properties).map(key => `${key}: $${key}`).join(', ')}}`
: '';
let query;
let parameters = { ...properties };
if (this._id) {
// Update existing node
query = `MATCH (n:${metadata.label}) WHERE ID(n) = $id SET n += $props RETURN n`;
parameters.id = this._id;
parameters.props = properties;
}
else {
// Create new node
query = `CREATE (n:${metadata.label} ${propsString}) RETURN n`;
}
const result = await session.run(query, parameters);
if (result.records.length > 0) {
const record = result.records[0];
const node = record.get('n');
this._id = node.identity.toNumber();
// Update properties from database (in case of transformations)
this.setPropertiesFromDatabase(node.properties);
this.markAsLoaded();
}
return this;
}
finally {
await session.close();
}
}
async delete() {
if (!this._id) {
throw new Error('Cannot delete entity without ID');
}
const connectionManager = this.getConnectionManager();
const session = connectionManager.getSession();
try {
const metadata = this.getMetadata();
const query = `MATCH (n:${metadata.label}) WHERE ID(n) = $id DELETE n RETURN count(n) as deleted`;
const result = await session.run(query, { id: this._id });
const deleted = result.records[0]?.get('deleted')?.toNumber() > 0;
if (deleted) {
this._id = undefined;
this._isLoaded = false;
}
return deleted;
}
finally {
await session.close();
}
}
async reload() {
if (!this._id) {
throw new Error('Cannot reload entity without ID');
}
const connectionManager = this.getConnectionManager();
const session = connectionManager.getSession();
try {
const metadata = this.getMetadata();
const query = `MATCH (n:${metadata.label}) WHERE ID(n) = $id RETURN n`;
const result = await session.run(query, { id: this._id });
if (result.records.length === 0) {
throw new Error(`Entity with ID ${this._id} not found`);
}
const record = result.records[0];
const node = record.get('n');
this.setPropertiesFromDatabase(node.properties);
this.markAsLoaded();
return this;
}
finally {
await session.close();
}
}
getMetadata() {
const metadata = metadata_1.MetadataStorage.getInstance().getNodeMetadata(this.constructor);
if (!metadata) {
throw new Error(`No metadata found for ${this.constructor.name}. Did you forget to add @Node decorator?`);
}
return metadata;
}
getProperties() {
const metadata = this.getMetadata();
const properties = {};
for (const [key, propertyMetadata] of Array.from(metadata.properties)) {
const value = this[key];
if (value !== undefined) {
// Apply transformer if defined
const transformedValue = propertyMetadata.transformer?.to
? propertyMetadata.transformer.to(value)
: value;
properties[key] = transformedValue;
}
}
return properties;
}
setPropertiesFromDatabase(dbProperties) {
const metadata = this.getMetadata();
for (const [key, propertyMetadata] of Array.from(metadata.properties)) {
const value = dbProperties[key];
if (value !== undefined) {
// Apply reverse transformer if defined
const transformedValue = propertyMetadata.transformer?.from
? propertyMetadata.transformer.from(value)
: value;
this[key] = transformedValue;
}
}
}
validateProperties(metadata, properties) {
for (const [key, propertyMetadata] of Array.from(metadata.properties)) {
const value = properties[key];
// Check required properties
if (propertyMetadata.required && (value === undefined || value === null)) {
throw new Error(`Property '${key}' is required but not provided`);
}
// Run custom validator if provided
if (value !== undefined && propertyMetadata.validator && !propertyMetadata.validator(value)) {
throw new Error(`Property '${key}' failed validation`);
}
}
}
toJSON() {
const metadata = this.getMetadata();
const result = {
id: this._id,
label: metadata.label
};
// Add all properties
for (const [key] of Array.from(metadata.properties)) {
result[key] = this[key];
}
return result;
}
}
exports.BaseEntity = BaseEntity;
//# sourceMappingURL=base-entity.js.map