sgnm-neo4j
Version:
neo4j module for nestJs
1,495 lines (1,428 loc) • 362 kB
text/typescript
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