UNPKG

sgnm-neo4j

Version:
1,495 lines (1,428 loc) 362 kB
import { Injectable, Inject, OnApplicationShutdown, HttpException, HttpStatus, OnModuleInit, } from "@nestjs/common"; import neo4j, { Driver, Result, int, Transaction, QueryResult, RecordShape, } from "neo4j-driver"; import { Neo4jConfig } from "./interfaces/neo4j-config.interface"; import { NEO4J_OPTIONS, NEO4J_DRIVER } from "./neo4j.constants"; import TransactionImpl from "neo4j-driver-core/lib/transaction"; import { newError } from "neo4j-driver-core"; import { changeObjectKeyName, createDynamicCyperCreateQuery, createDynamicCypherCreateWithDatesQuery, dateFilterGenerator, dynamicFilterPropertiesAdder, dynamicFilterPropertiesAdderAndAddParameterKey, dynamicFilterPropertiesAdderAndAddParameterKeyNew, dynamicLabelAdder, dynamicNotLabelAdder, dynamicOrderByColumnAdder, dynamicOrLabelAdder, dynamicUpdatePropertyAdder, dynamicUpdatePropertyAdderAndAddParameterKey, filterArrayForEmptyString, updateNodeQuery, } from "./func/common.func"; import { successResponse } from "./constant/success.response.object"; import { failedResponse } from "./constant/failed.response.object"; import { add_relation_with_relation_name__create_relation_error, create_node__must_entered_error, create_node__node_not_created_error, deleteParentRelationError, find_with_children_by_realm_as_tree__find_by_realm_error, node_not_found, parent_of_child_not_found, tree_structure_not_found_by_realm_name_error, library_server_error, invalid_direction_error, required_fields_must_entered, } from "./constant/custom.error.object"; import { RelationDirection } from "./constant/relation.direction.enum"; import { queryObjectType } from "./dtos/dtos"; import { SearchType } from "./constant/pagination.enum"; import { otherNodesObjProps } from "./constant/pagination.object.type"; import { FilterPropertiesType } from "./constant/filter.properties.type.enum"; import { IFindMultipleNodesWithFiltersAndId } from "./index"; @Injectable() export class Neo4jService implements OnApplicationShutdown, OnModuleInit { private readonly driver: Driver; private readonly config: Neo4jConfig; protected serverTimezone: QueryResult<RecordShape>; constructor( @Inject(NEO4J_OPTIONS) config: Neo4jConfig, @Inject(NEO4J_DRIVER) driver: Driver ) { this.driver = driver; this.config = config; } getDriver(): Driver { return this.driver; } getConfig(): Neo4jConfig { return this.config; } int(value: number) { return int(value); } beginTransaction(database?: string): Transaction { const session = this.getWriteSession(database); return session.beginTransaction(); } getReadSession(database?: string) { return this.driver.session({ database: database || this.config.database, defaultAccessMode: neo4j.session.READ, }); } getWriteSession(database?: string) { return this.driver.session({ database: database || this.config.database, defaultAccessMode: neo4j.session.WRITE, }); } read( cypher: string, params?: Record<string, any>, databaseOrTransaction?: string | Transaction ): Result { if (databaseOrTransaction instanceof TransactionImpl) { return (<Transaction>databaseOrTransaction).run(cypher, params); } const session = this.getReadSession(<string>databaseOrTransaction); return session.run(cypher, params); } write( cypher: string, params?: Record<string, any>, databaseOrTransaction?: string | Transaction ): Result { if (databaseOrTransaction instanceof TransactionImpl) { return (<Transaction>databaseOrTransaction).run(cypher, params); } const session = this.getWriteSession(<string>databaseOrTransaction); return session.run(cypher, params); } convertStringToDateTime(dateTime: string) { return neo4j.types.DateTime.fromStandardDate(new Date(dateTime)); } convertStringToLocalDateTime(localDateTime: string) { return neo4j.types.LocalDateTime.fromStandardDate(new Date(localDateTime)); } onApplicationShutdown() { return this.driver.close(); } async getLocalDateTime(localDateTime: string) { return await this.convertStringToLocalDateTime(localDateTime) } async getDateTime(dateTime: string) { return await this.convertStringToDateTime(dateTime) } async changeObjectChildOfPropToChildren(node: any) { node["root"]["children"] = node["root"]["parent_of"]; delete node["root"]["parent_of"]; if (node["root"]["children"]) { for (let i = 0; i < node["root"]["children"].length; i++) { node["root"]["children"][i]["children"] = node["root"]["children"][i]["parent_of"]; delete node["root"]["children"][i]["parent_of"]; if (node["root"]["children"][i]["children"]) { for ( let j = 0; j < node["root"]["children"][i]["children"].length; j++ ) { node["root"]["children"][i]["children"][j]["children"] = node["root"]["children"][i]["children"][j]["parent_of"]; delete node["root"]["children"][i]["children"][j]["parent_of"]; if (node["root"]["children"][i]["children"][j]["children"]) { for ( let k = 0; k < node["root"]["children"][i]["children"][j]["children"].length; k++ ) { node["root"]["children"][i]["children"][j]["children"][k][ "children" ] = node["root"]["children"][i]["children"][j]["children"][k][ "parent_of" ]; delete node["root"]["children"][i]["children"][j]["children"][ k ]["parent_of"]; if ( node["root"]["children"][i]["children"][j]["children"][k][ "children" ] ) { for ( let l = 0; l < node["root"]["children"][i]["children"][j]["children"][k][ "children" ].length; l++ ) { node["root"]["children"][i]["children"][j]["children"][k][ "children" ][l]["children"] = node["root"]["children"][i]["children"][j]["children"][k][ "children" ][l]["parent_of"]; delete node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["parent_of"]; if ( node["root"]["children"][i]["children"][j]["children"][k][ "children" ][l]["children"] ) { for ( let m = 0; m < node["root"]["children"][i]["children"][j]["children"][ k ]["children"][l]["children"].length; m++ ) { node["root"]["children"][i]["children"][j]["children"][ k ]["children"][l]["children"][m]["children"] = node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["parent_of"]; delete node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["parent_of"]; if ( node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"] ) { for ( let n = 0; n < node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"] .length; n++ ) { node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"][n][ "children" ] = node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"][n][ "parent_of" ]; delete node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"][n][ "parent_of" ]; if ( node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"][n][ "children" ] ) { for ( let o = 0; o < node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"][ n ]["children"].length; o++ ) { node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m]["children"][ n ]["children"][o]["children"] = node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m][ "children" ][n]["children"][o]["parent_of"]; delete node["root"]["children"][i]["children"][ j ]["children"][k]["children"][l]["children"][m][ "children" ][n]["children"][o]["parent_of"]; if ( node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m][ "children" ][n]["children"][o]["children"] ) { for ( let p = 0; p < node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m][ "children" ][n]["children"][o]["children"].length; p++ ) { node["root"]["children"][i]["children"][j][ "children" ][k]["children"][l]["children"][m][ "children" ][n]["children"][o]["children"][p][ "children" ] = node["root"]["children"][i]["children"][ j ]["children"][k]["children"][l][ "children" ][m]["children"][n]["children"][o][ "children" ][p]["parent_of"]; delete node["root"]["children"][i][ "children" ][j]["children"][k]["children"][l][ "children" ][m]["children"][n]["children"][o][ "children" ][p]["parent_of"]; } } } } } } } } } } } } } } } } return node; } async findByIdAndFilters( id: number, labels: string[], filter_properties: object = {}, excluded_labels: Array<string> = [], databaseOrTransaction?: string | Transaction ) { const LabelsWithoutEmptyString = filterArrayForEmptyString(labels); const excludedLabelsLabelsWithoutEmptyString = filterArrayForEmptyString(excluded_labels); let query = "match (n" + dynamicLabelAdder(LabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(filter_properties) + ` where id(n)=${id} `; if ( excludedLabelsLabelsWithoutEmptyString && excludedLabelsLabelsWithoutEmptyString.length > 0 ) { query = query + " and " + dynamicNotLabelAdder("n", excludedLabelsLabelsWithoutEmptyString) + ` return n`; } else { query = query + ` return n`; } filter_properties["id"] = id; const node = await this.read( query, filter_properties, databaseOrTransaction ); delete filter_properties["id"]; if (node.records.length === 0) { throw new HttpException(node_not_found, 404); } else { return node.records[0]["_fields"][0]; } } async findByLabelAndFilters( labels: Array<string> = [""], filter_properties: object = {}, excluded_labels: Array<string> = [""], databaseOrTransaction?: string | Transaction ) { const excludedLabelsLabelsWithoutEmptyString = filterArrayForEmptyString(excluded_labels); let query = "match (n" + dynamicLabelAdder(labels) + dynamicFilterPropertiesAdder(filter_properties); if ( excludedLabelsLabelsWithoutEmptyString && excludedLabelsLabelsWithoutEmptyString.length > 0 ) { query = query + " where " + dynamicNotLabelAdder("n", excludedLabelsLabelsWithoutEmptyString) + ` return n`; } else { query = query + ` return n`; } console.log("findByIdAndFilters",query) console.log("findByIdAndFilters",filter_properties) const node = await this.read( query, filter_properties, databaseOrTransaction ); return node.records; } async findByOrLabelsAndFilters( or_labels: Array<string> = [""], filter_properties: object = {}, databaseOrTransaction?: string | Transaction ) { const orLabelsWithoutEmptyString = filterArrayForEmptyString(or_labels); let query = "match (n " + dynamicFilterPropertiesAdder(filter_properties); if (orLabelsWithoutEmptyString && orLabelsWithoutEmptyString.length > 0) { query = query + " where " + dynamicOrLabelAdder("n", orLabelsWithoutEmptyString) + ` return n`; } else { query = query + ` return n`; } const node = await this.read( query, filter_properties, databaseOrTransaction ); if (node) { return node.records; } else { return []; } } async findByIdAndOrLabelsAndFilters( id: number, or_labels: Array<string> = [""], filter_properties: object = {}, databaseOrTransaction?: string | Transaction ) { const orLabelsWithoutEmptyString = filterArrayForEmptyString(or_labels); let query = "match (n " + dynamicFilterPropertiesAdder(filter_properties) + ` where id(n)=${id} `; if (orLabelsWithoutEmptyString && orLabelsWithoutEmptyString.length > 0) { query = query + " and" + dynamicOrLabelAdder("n", orLabelsWithoutEmptyString) + ` return n`; } else { query = query + ` return n`; } const node = await this.read( query, filter_properties, databaseOrTransaction ); return node.records; } async updateByIdAndFilter( id: number, labels: string[] = [""], filter_properties: object = {}, update_labels: Array<string> = [], update_properties: object = {}, databaseOrTransaction?: string | Transaction ) { try { const labelsWithoutEmptyString = filterArrayForEmptyString(labels); const updateLabelsWithoutEmptyString = filterArrayForEmptyString(update_labels); const isNodeExist = await this.findByIdAndFilters( id, labelsWithoutEmptyString, filter_properties ); if (!isNodeExist) { throw new HttpException(node_not_found, 404); } let query = `match (n` + dynamicLabelAdder(labelsWithoutEmptyString) + `)` + ` where id(n)=${id} set ` + dynamicUpdatePropertyAdder("n", update_properties); if ( updateLabelsWithoutEmptyString && updateLabelsWithoutEmptyString.length > 0 ) { if (!update_properties || Object.keys(update_properties).length === 0) { query = query + " n" + dynamicLabelAdder(updateLabelsWithoutEmptyString) + " return n"; } else { query = query + ", n" + dynamicLabelAdder(updateLabelsWithoutEmptyString) + " return n"; } } else { query = query + " return n"; } update_properties["id"] = id; const parameters = update_properties; const node = await this.write(query, parameters, databaseOrTransaction); if (node.records.length === 0) { return null; } else { return node.records[0]["_fields"][0]; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException( library_server_error, HttpStatus.INTERNAL_SERVER_ERROR ); } } } async updateByLabelAndFilter( labels: Array<string> = [], filter_properties: object = {}, update_labels: Array<string> = [], update_properties: object = {}, databaseOrTransaction?: string | Transaction ) { try { const nodelabelsWithoutEmptyString = filterArrayForEmptyString(labels); const updateLabelsWithoutEmptyString = filterArrayForEmptyString(update_labels); let query = "match (n" + dynamicLabelAdder(nodelabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(filter_properties) + ` set ` + dynamicUpdatePropertyAdderAndAddParameterKey("n", update_properties); if ( updateLabelsWithoutEmptyString && updateLabelsWithoutEmptyString.length > 0 ) { query = query + ", n" + dynamicLabelAdder(updateLabelsWithoutEmptyString) + " return n"; } else { query = query + " return n"; } const update_properties1 = changeObjectKeyName(update_properties, "1"); const parameters = { ...filter_properties, ...update_properties1 }; console.log("updateByLabelAndFilter",query) console.log("updateByLabelAndFilter",parameters) const result = this.write(query, parameters, databaseOrTransaction); return result; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR); } } } async findChildrensByLabelsAsTree( root_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); const cypher = `MATCH p=(n` + dynamicLabelAdder(rootLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(root_filters) + "-[:PARENT_OF*]->(m" + dynamicLabelAdder(childrenLabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey(children_filters) + ` WITH COLLECT(p) AS ps CALL apoc.convert.toTree(ps) yield value RETURN value`; children_filters = changeObjectKeyName(children_filters); const parameters = { ...root_filters, ...children_filters }; const result = await this.read(cypher, parameters, databaseOrTransaction); return result["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException( library_server_error, HttpStatus.INTERNAL_SERVER_ERROR ); } } } async findByLabelAndFiltersWithTreeStructure( root_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); let tree = await this.findChildrensByLabelsAsTree( rootLabelsWithoutEmptyString, root_filters, childrenLabelsWithoutEmptyString, children_filters ); if (!tree) { throw new HttpException( tree_structure_not_found_by_realm_name_error, 404 ); } else if (Object.keys(tree).length === 0) { tree = await this.findByLabelAndFilters( rootLabelsWithoutEmptyString, root_filters ); if (!tree.length) { const rootNodeObject = { root: {} }; return rootNodeObject; } const rootNodeObject = { root: tree[0]["_fields"][0] }; return rootNodeObject; } else { const rootNodeObject = { root: tree }; return rootNodeObject; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException( library_server_error, HttpStatus.INTERNAL_SERVER_ERROR ); } } } async findChildrensByIdsAsTree( root_id: number, root_labels: string[] = [""], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); const rootNode = await this.findByIdAndFilters( root_id, rootLabelsWithoutEmptyString, root_filters ); if (!rootNode) { throw new HttpException( find_with_children_by_realm_as_tree__find_by_realm_error, 404 ); } const rootId = rootNode.identity.low; const cypher = `MATCH p=(n` + dynamicLabelAdder(rootLabelsWithoutEmptyString) + `)-[:PARENT_OF*]->(m` + dynamicLabelAdder(childrenLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(children_filters) + ` WHERE id(n) = $rootId WITH COLLECT(p) AS ps CALL apoc.convert.toTree(ps) yield value RETURN value`; children_filters["rootId"] = rootId; const result = await this.read( cypher, children_filters, databaseOrTransaction ); return result["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException( library_server_error, HttpStatus.INTERNAL_SERVER_ERROR ); } } } async findByIdAndFiltersWithTreeStructure( root_id: number, root_labels: string[] = [""], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); let tree = await this.findChildrensByIdsAsTree( root_id, root_labels, root_filters, childrenLabelsWithoutEmptyString, children_filters ); if (!tree) { throw new HttpException( tree_structure_not_found_by_realm_name_error, 404 ); } else if (Object.keys(tree).length === 0) { tree = await this.findByIdAndFilters( root_id, root_labels, root_filters ); const rootNodeObject = { root: tree }; return rootNodeObject; } else { const rootNodeObject = { root: tree }; return rootNodeObject; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException( library_server_error, HttpStatus.INTERNAL_SERVER_ERROR ); } } } async getParentByIdAndFilters( id: number, node_labels: string[] = [""], node_filters: object = {}, parent_labels: string[] = [""], parent_filters: object = {}, relation_name: string, relation_filters, relation_depth: number | "", databaseOrTransaction?: string | Transaction ) { try { const nodeLabelsWithoutEmptyString = filterArrayForEmptyString(node_labels); const parentLabelsWithoutEmptyString = filterArrayForEmptyString(parent_labels); const node = await this.findByIdAndFilters( +id, node_labels, node_filters ); if (!node) { throw new HttpException(node_not_found, 404); } const query = "MATCH (n" + dynamicLabelAdder(nodeLabelsWithoutEmptyString) + ") where id(n)= $id match(m" + dynamicLabelAdder(parentLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(parent_filters) + "match (m)-" + `[r:${relation_name}*1..${relation_depth}` + dynamicFilterPropertiesAdderAndAddParameterKey( relation_filters, FilterPropertiesType.RELATION ) + `]->(n) return m as parent,n as children`; relation_filters = changeObjectKeyName(relation_filters); const parameters = { id, ...parent_filters, ...relation_filters }; const res = await this.read(query, parameters, databaseOrTransaction); if (!res["records"][0].length) { throw new HttpException(parent_of_child_not_found, 404); } return res["records"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response.message, code: error.response.code }, error.status ); } else { throw new HttpException( "library_server_error", HttpStatus.INTERNAL_SERVER_ERROR ); } } } async addRelationByLabelsAndFiltersAndRelationName( first_node_labels: Array<string> = [], first_node_properties: object = {}, second_node_labels: Array<string> = [], second_node_properties: object = {}, relation_name: string, relation_properties: object = {}, relation_direction: RelationDirection = RelationDirection.RIGHT, databaseOrTransaction?: string | Transaction ) { try { if (!relation_name) { throw new HttpException(required_fields_must_entered, 404); } const firstNodelabelsWithoutEmptyString = filterArrayForEmptyString(first_node_labels); const secondNodelabelsWithoutEmptyString = filterArrayForEmptyString(second_node_labels); let cyper; let res; let parameters; switch (relation_direction) { case RelationDirection.RIGHT: cyper = `MATCH (n` + dynamicLabelAdder(firstNodelabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(first_node_properties) + `MATCH (m` + dynamicLabelAdder(secondNodelabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey( second_node_properties ) + `MERGE (n)-[:${relation_name} ` + dynamicFilterPropertiesAdderAndAddParameterKey( relation_properties, FilterPropertiesType.RELATION, "3" ) + `]-> (m) return n as parent,m as children`; second_node_properties = changeObjectKeyName(second_node_properties); relation_properties = changeObjectKeyName(relation_properties, "3"); parameters = { ...second_node_properties, ...first_node_properties, ...relation_properties, }; res = await this.write(cyper, parameters, databaseOrTransaction); break; case RelationDirection.LEFT: cyper = `MATCH (m` + dynamicLabelAdder(firstNodelabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(first_node_properties) + `MATCH (n` + dynamicLabelAdder(secondNodelabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey( second_node_properties ) + `MERGE (m)<-[:${relation_name}` + dynamicFilterPropertiesAdderAndAddParameterKey( relation_properties, FilterPropertiesType.RELATION, "3" ) + `]- (n) return n as parent,m as children`; second_node_properties = changeObjectKeyName(second_node_properties); relation_properties = changeObjectKeyName(relation_properties, "3"); parameters = { ...second_node_properties, ...first_node_properties, ...relation_properties, }; res = await this.write(cyper, parameters, databaseOrTransaction); break; default: throw new HttpException(invalid_direction_error, 400); } const { relationshipsCreated } = await res.summary.updateStatistics.updates(); if (relationshipsCreated === 0) { throw new HttpException( add_relation_with_relation_name__create_relation_error, 400 ); } return res; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response.message, code: error.response.code }, error.status ); } else { throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR); } } } async findChildrensByLabelsAsTreeOneLevel( root_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); const cypher = `MATCH p=(n` + dynamicLabelAdder(rootLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(root_filters) + `-[:PARENT_OF]->(m` + dynamicLabelAdder(childrenLabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey(children_filters) + ` WITH COLLECT(p) AS ps CALL apoc.convert.toTree(ps) yield value RETURN value`; children_filters = changeObjectKeyName(children_filters); const parameters = { ...root_filters, ...children_filters }; const result = await this.read(cypher, parameters, databaseOrTransaction); return result["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async findByLabelAndFiltersWithTreeStructureOneLevel( root_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootlabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenlabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); let tree = await this.findChildrensByLabelsAsTreeOneLevel( rootlabelsWithoutEmptyString, root_filters, childrenlabelsWithoutEmptyString, children_filters ); if (!tree) { throw new HttpException( tree_structure_not_found_by_realm_name_error, 404 ); } else if (Object.keys(tree).length === 0) { tree = await this.findByLabelAndFilters( rootlabelsWithoutEmptyString, root_filters ); if (!tree.length) { const rootNodeObject = { root: {} }; return rootNodeObject; } const rootNodeObject = { root: tree[0]["_fields"][0] }; return rootNodeObject; } else { const rootNodeObject = { root: tree }; return rootNodeObject; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async findChildrensByLabelsOneLevel( root_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); const cypher = `MATCH p=(n` + dynamicLabelAdder(rootLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(root_filters) + `-[:PARENT_OF]->(m` + dynamicLabelAdder(childrenLabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey(children_filters) + ` RETURN n as parent,m as children`; children_filters = changeObjectKeyName(children_filters); const parameters = { ...root_filters, ...children_filters }; const result = await this.read(cypher, parameters, databaseOrTransaction); return result["records"]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async findChildrensByIdsAsTreeOneLevel( id: number, root_labels: string[] = [""], root_filters: object = {}, children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootNode = await this.findByIdAndFilters( id, root_labels, root_filters ); if (!rootNode || rootNode.length == 0) { throw new HttpException( find_with_children_by_realm_as_tree__find_by_realm_error, 404 ); } const rootId = id; const cypher = `MATCH p=(n` + dynamicLabelAdder(rootNode.labels) + `)-[:PARENT_OF]->(m` + dynamicFilterPropertiesAdder(children_filters) + ` WHERE id(n) = $rootId WITH COLLECT(p) AS ps CALL apoc.convert.toTree(ps) yield value RETURN value`; children_filters["rootId"] = rootId; const result = await this.read( cypher, children_filters, databaseOrTransaction ); return result["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async findByIdAndFiltersWithTreeStructureOneLevel( id: number, root_labels: string[] = [""], root_filters: object = {}, children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { let tree = await this.findChildrensByIdsAsTreeOneLevel( id, root_labels, root_filters, children_filters ); if (!tree) { throw new HttpException( tree_structure_not_found_by_realm_name_error, 404 ); } else if (Object.keys(tree).length === 0) { tree = await this.findByIdAndFilters(id, root_labels, root_filters); if (!tree.length) { const rootNodeObject = { root: {} }; return rootNodeObject; } const rootNodeObject = { root: tree[0]["_fields"][0] }; return rootNodeObject; } else { const rootNodeObject = { root: tree }; return rootNodeObject; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async updateNodeChildrensByIdAndFilter( id: number, root_labels: string[] = [""], root_filters: object = {}, children_labels: Array<string> = [], children_filters: object = {}, relation_name: string, relation_filters: object = {}, relation_depth: number | "", update_labels: Array<string> = [], update_properties: object = {}, databaseOrTransaction?: string | Transaction ) { try { if (!relation_name) { throw new HttpException(required_fields_must_entered, 404); } const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const updateLabelsWithoutEmptyString = filterArrayForEmptyString(update_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); await this.findByIdAndFilters( id, rootLabelsWithoutEmptyString, root_filters ); let query = `match (m` + dynamicLabelAdder(rootLabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey( root_filters, FilterPropertiesType.NODE, "3" ) + `match(n` + dynamicLabelAdder(childrenLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(children_filters) + ` match (m)-[:${relation_name}*1..${relation_depth} ` + dynamicFilterPropertiesAdderAndAddParameterKey( relation_filters, FilterPropertiesType.RELATION, "2" ) + `]->(n)` + ` where id(m)=$rootId set ` + dynamicUpdatePropertyAdderAndAddParameterKey("n", update_properties); if ( updateLabelsWithoutEmptyString && updateLabelsWithoutEmptyString.length > 0 ) { query = query + ", n" + dynamicLabelAdder(updateLabelsWithoutEmptyString) + " return m as parent, n as children"; } else { query = query + " return m as parent, n as children"; } root_filters = changeObjectKeyName(root_filters, "3"); update_properties = changeObjectKeyName(update_properties, "1"); relation_filters = changeObjectKeyName(relation_filters, "2"); const parameters = { ...root_filters, ...children_filters, ...update_properties, ...relation_filters, }; parameters["rootId"] = id; console.log("updateNodeChildrensByIdAndFilter",query) console.log("updateNodeChildrensByIdAndFilter",parameters) const node = await this.write(query, parameters, databaseOrTransaction); if (node.records.length === 0) { throw new HttpException("nodes not updates", 400); } else { return node.records; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR); } } } async findChildrensByLabelsAndNotLabelsAsTreeOneLevel( root_labels: Array<string> = [], root_not_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_not_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootLabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const rootNotLabelsWithoutEmptyString = filterArrayForEmptyString(root_not_labels); const childrenLabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); const childrenNotLabelsWithoutEmptyString = filterArrayForEmptyString(children_not_labels); let cypher = `MATCH p=(n ` + dynamicLabelAdder(rootLabelsWithoutEmptyString) + dynamicFilterPropertiesAdder(root_filters) + ` -[:PARENT_OF]->(m ` + dynamicLabelAdder(childrenLabelsWithoutEmptyString) + dynamicFilterPropertiesAdderAndAddParameterKey(children_filters) + "where "; if ( rootNotLabelsWithoutEmptyString && rootNotLabelsWithoutEmptyString.length > 0 ) { cypher = cypher + dynamicNotLabelAdder("n", rootNotLabelsWithoutEmptyString); } if ( childrenNotLabelsWithoutEmptyString && childrenNotLabelsWithoutEmptyString.length > 0 ) { cypher = cypher + " and " + dynamicNotLabelAdder("m", childrenNotLabelsWithoutEmptyString); } cypher = cypher + ` WITH COLLECT(p) AS ps CALL apoc.convert.toTree(ps) yield value RETURN value`; Object.keys(root_filters).forEach((element_root) => { let i = 0; Object.keys(children_filters).forEach((element_child) => { if (element_root === element_child) { i = 1; } }); if (i == 0) { children_filters[element_root] = root_filters[element_root]; } }); children_filters = changeObjectKeyName(children_filters); const parameters = { ...root_filters, ...children_filters }; const result = await this.read(cypher, parameters, databaseOrTransaction); return result["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async findByLabelAndNotLabelAndFiltersWithTreeStructureOneLevel( root_labels: Array<string> = [], root_not_labels: Array<string> = [], root_filters: object = {}, children_labels: Array<string> = [], children_not_labels: Array<string> = [], children_filters: object = {}, databaseOrTransaction?: string | Transaction ) { try { const rootlabelsWithoutEmptyString = filterArrayForEmptyString(root_labels); const childrenlabelsWithoutEmptyString = filterArrayForEmptyString(children_labels); const rootNotLabelsWithoutEmptyString = root_not_labels.filter((item) => { if (item.trim() !== "") { return item; } }); const childrenNotLabelsWithoutEmptyString = children_not_labels.filter( (item) => { if (item.trim() !== "") { return item; } } ); let tree = await this.findChildrensByLabelsAndNotLabelsAsTreeOneLevel( rootlabelsWithoutEmptyString, rootNotLabelsWithoutEmptyString, root_filters, childrenlabelsWithoutEmptyString, childrenNotLabelsWithoutEmptyString, children_filters, databaseOrTransaction ); if (!tree) { throw new HttpException( tree_structure_not_found_by_realm_name_error, 404 ); } else if (Object.keys(tree).length === 0) { tree = await this.findByLabelAndFilters( rootlabelsWithoutEmptyString, root_filters ); if (!tree.length) { const rootNodeObject = { root: {} }; return rootNodeObject; } const rootNodeObject = { root: tree[0]["_fields"][0] }; return rootNodeObject; } else { const rootNodeObject = { root: tree }; return rootNodeObject; } } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw new HttpException(error, 500); } } } async createNode( params: object, labels?: string[], databaseOrTransaction?: string | Transaction ) { try { if (!params || Object.keys(params).length === 0) { throw new HttpException(create_node__must_entered_error, 400); } let cyperQuery; let labelsWithoutEmptyString; if (labels) { labelsWithoutEmptyString = filterArrayForEmptyString(labels); cyperQuery = createDynamicCyperCreateQuery( params, labelsWithoutEmptyString ); } else { cyperQuery = createDynamicCyperCreateQuery(params); } console.log("createNode",cyperQuery) console.log("createNode",params) const res = await this.write(cyperQuery, params, databaseOrTransaction); if (!res["records"][0].length) { throw new HttpException(create_node__node_not_created_error, 400); } return res["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw newError(error, "500"); } } } async createNodeWithDateAttributes( params: object, dateParamKeys: string[] = [], dateTimesParamKeys: string[] = [], labels?: string[], databaseOrTransaction?: string | Transaction ) { try { if (!params || Object.keys(params).length === 0) { throw new HttpException(create_node__must_entered_error, 400); } let cyperQuery; let labelsWithoutEmptyString; if (labels) { labelsWithoutEmptyString = filterArrayForEmptyString(labels); cyperQuery = createDynamicCypherCreateWithDatesQuery( params, dateParamKeys, dateTimesParamKeys, labelsWithoutEmptyString ); } else { cyperQuery = createDynamicCypherCreateWithDatesQuery( params, dateParamKeys, dateTimesParamKeys ); } const res = await this.write(cyperQuery, params, databaseOrTransaction); if (!res["records"][0].length) { throw new HttpException(create_node__node_not_created_error, 400); } return res["records"][0]["_fields"][0]; } catch (error) { if (error.response?.code) { throw new HttpException( { message: error.response?.message, code: error.response?.code }, error.status ); } else { throw newError(error, "500"); } } } async deleteRelationByIdAndRelatio