@ductape/sdk 0.0.4-v5 → 0.0.4-v51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/services/appApi.service.d.ts +48 -2
- package/dist/api/services/appApi.service.js +101 -2
- package/dist/api/services/appApi.service.js.map +1 -1
- package/dist/api/services/pricingApi.service.d.ts +10 -0
- package/dist/api/services/pricingApi.service.js +34 -0
- package/dist/api/services/pricingApi.service.js.map +1 -0
- package/dist/api/services/processorApi.service.d.ts +12 -2
- package/dist/api/services/processorApi.service.js +12 -2
- package/dist/api/services/processorApi.service.js.map +1 -1
- package/dist/api/services/productsApi.service.d.ts +40 -1
- package/dist/api/services/productsApi.service.js +105 -1
- package/dist/api/services/productsApi.service.js.map +1 -1
- package/dist/api/services/userApi.service.js +1 -0
- package/dist/api/services/userApi.service.js.map +1 -1
- package/dist/api/services/workspaceApi.service.js +1 -0
- package/dist/api/services/workspaceApi.service.js.map +1 -1
- package/dist/api/services/workspaceSecretsApi.service.d.ts +75 -0
- package/dist/api/services/workspaceSecretsApi.service.js +62 -0
- package/dist/api/services/workspaceSecretsApi.service.js.map +1 -0
- package/dist/api/urls.d.ts +6 -1
- package/dist/api/urls.js +12 -2
- package/dist/api/urls.js.map +1 -1
- package/dist/api/utils/cache.utils.d.ts +1 -1
- package/dist/api/utils/cache.utils.js +10 -4
- package/dist/api/utils/cache.utils.js.map +1 -1
- package/dist/api/utils/strings.utils.d.ts +2 -0
- package/dist/api/utils/strings.utils.js +14 -0
- package/dist/api/utils/strings.utils.js.map +1 -1
- package/dist/apps/services/app.service.d.ts +51 -33
- package/dist/apps/services/app.service.js +488 -244
- package/dist/apps/services/app.service.js.map +1 -1
- package/dist/apps/validators/joi-validators/create.appAction.validator.d.ts +1 -2
- package/dist/apps/validators/joi-validators/create.appAction.validator.js +21 -2
- package/dist/apps/validators/joi-validators/create.appAction.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.d.ts +1 -2
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.js +2 -15
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.js +11 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appActionResponse.validator.d.ts +1 -1
- package/dist/apps/validators/joi-validators/update.appActionResponse.validator.js +34 -1
- package/dist/apps/validators/joi-validators/update.appActionResponse.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appWebhook.validator.d.ts +1 -2
- package/dist/apps/validators/joi-validators/update.appWebhook.validator.js +2 -14
- package/dist/apps/validators/joi-validators/update.appWebhook.validator.js.map +1 -1
- package/dist/clients/pricing.client.d.ts +3 -0
- package/dist/clients/pricing.client.js +33 -0
- package/dist/clients/pricing.client.js.map +1 -0
- package/dist/database/adapters/base.adapter.d.ts +176 -0
- package/dist/database/adapters/base.adapter.js +31 -0
- package/dist/database/adapters/base.adapter.js.map +1 -0
- package/dist/database/adapters/dynamodb.adapter.d.ts +91 -0
- package/dist/database/adapters/dynamodb.adapter.js +1608 -0
- package/dist/database/adapters/dynamodb.adapter.js.map +1 -0
- package/dist/database/adapters/mongodb.adapter.d.ts +71 -0
- package/dist/database/adapters/mongodb.adapter.js +1072 -0
- package/dist/database/adapters/mongodb.adapter.js.map +1 -0
- package/dist/database/adapters/mysql.adapter.d.ts +146 -0
- package/dist/database/adapters/mysql.adapter.js +1492 -0
- package/dist/database/adapters/mysql.adapter.js.map +1 -0
- package/dist/database/adapters/postgresql.adapter.d.ts +147 -0
- package/dist/database/adapters/postgresql.adapter.js +1603 -0
- package/dist/database/adapters/postgresql.adapter.js.map +1 -0
- package/dist/database/database.service.d.ts +232 -0
- package/dist/database/database.service.js +802 -0
- package/dist/database/database.service.js.map +1 -0
- package/dist/database/index.d.ts +18 -0
- package/dist/database/index.js +98 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/types/aggregation.types.d.ts +261 -0
- package/dist/database/types/aggregation.types.js +21 -0
- package/dist/database/types/aggregation.types.js.map +1 -0
- package/dist/database/types/connection.types.d.ts +132 -0
- package/dist/database/types/connection.types.js +6 -0
- package/dist/database/types/connection.types.js.map +1 -0
- package/dist/database/types/database.types.d.ts +175 -0
- package/dist/database/types/database.types.js +75 -0
- package/dist/database/types/database.types.js.map +1 -0
- package/dist/database/types/index.d.ts +12 -0
- package/dist/database/types/index.js +37 -0
- package/dist/database/types/index.js.map +1 -0
- package/dist/database/types/index.types.d.ts +220 -0
- package/dist/database/types/index.types.js +27 -0
- package/dist/database/types/index.types.js.map +1 -0
- package/dist/database/types/migration.types.d.ts +205 -0
- package/dist/database/types/migration.types.js +44 -0
- package/dist/database/types/migration.types.js.map +1 -0
- package/dist/database/types/query.types.d.ts +305 -0
- package/dist/database/types/query.types.js +57 -0
- package/dist/database/types/query.types.js.map +1 -0
- package/dist/database/types/result.types.d.ts +220 -0
- package/dist/database/types/result.types.js +6 -0
- package/dist/database/types/result.types.js.map +1 -0
- package/dist/database/types/schema.types.d.ts +190 -0
- package/dist/database/types/schema.types.js +69 -0
- package/dist/database/types/schema.types.js.map +1 -0
- package/dist/database/utils/helpers.d.ts +66 -0
- package/dist/database/utils/helpers.js +501 -0
- package/dist/database/utils/helpers.js.map +1 -0
- package/dist/database/utils/migration.utils.d.ts +151 -0
- package/dist/database/utils/migration.utils.js +476 -0
- package/dist/database/utils/migration.utils.js.map +1 -0
- package/dist/database/utils/transaction.d.ts +64 -0
- package/dist/database/utils/transaction.js +130 -0
- package/dist/database/utils/transaction.js.map +1 -0
- package/dist/database/validators/connection.validator.d.ts +20 -0
- package/dist/database/validators/connection.validator.js +267 -0
- package/dist/database/validators/connection.validator.js.map +1 -0
- package/dist/database/validators/query.validator.d.ts +31 -0
- package/dist/database/validators/query.validator.js +305 -0
- package/dist/database/validators/query.validator.js.map +1 -0
- package/dist/database/validators/schema.validator.d.ts +31 -0
- package/dist/database/validators/schema.validator.js +334 -0
- package/dist/database/validators/schema.validator.js.map +1 -0
- package/dist/graph/adapters/arangodb.adapter.d.ts +80 -0
- package/dist/graph/adapters/arangodb.adapter.js +1393 -0
- package/dist/graph/adapters/arangodb.adapter.js.map +1 -0
- package/dist/graph/adapters/base.adapter.d.ts +228 -0
- package/dist/graph/adapters/base.adapter.js +38 -0
- package/dist/graph/adapters/base.adapter.js.map +1 -0
- package/dist/graph/adapters/index.d.ts +10 -0
- package/dist/graph/adapters/index.js +23 -0
- package/dist/graph/adapters/index.js.map +1 -0
- package/dist/graph/adapters/memgraph.adapter.d.ts +85 -0
- package/dist/graph/adapters/memgraph.adapter.js +1491 -0
- package/dist/graph/adapters/memgraph.adapter.js.map +1 -0
- package/dist/graph/adapters/neo4j.adapter.d.ts +88 -0
- package/dist/graph/adapters/neo4j.adapter.js +1861 -0
- package/dist/graph/adapters/neo4j.adapter.js.map +1 -0
- package/dist/graph/adapters/neptune.adapter.d.ts +87 -0
- package/dist/graph/adapters/neptune.adapter.js +1430 -0
- package/dist/graph/adapters/neptune.adapter.js.map +1 -0
- package/dist/graph/graph.service.d.ts +278 -0
- package/dist/graph/graph.service.js +687 -0
- package/dist/graph/graph.service.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.js +42 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/types/connection.types.d.ts +158 -0
- package/dist/graph/types/connection.types.js +43 -0
- package/dist/graph/types/connection.types.js.map +1 -0
- package/dist/graph/types/graph.types.d.ts +144 -0
- package/dist/graph/types/graph.types.js +84 -0
- package/dist/graph/types/graph.types.js.map +1 -0
- package/dist/graph/types/index.d.ts +11 -0
- package/dist/graph/types/index.js +35 -0
- package/dist/graph/types/index.js.map +1 -0
- package/dist/graph/types/node.types.d.ts +193 -0
- package/dist/graph/types/node.types.js +49 -0
- package/dist/graph/types/node.types.js.map +1 -0
- package/dist/graph/types/path.types.d.ts +224 -0
- package/dist/graph/types/path.types.js +38 -0
- package/dist/graph/types/path.types.js.map +1 -0
- package/dist/graph/types/query.types.d.ts +247 -0
- package/dist/graph/types/query.types.js +23 -0
- package/dist/graph/types/query.types.js.map +1 -0
- package/dist/graph/types/relationship.types.d.ts +224 -0
- package/dist/graph/types/relationship.types.js +35 -0
- package/dist/graph/types/relationship.types.js.map +1 -0
- package/dist/graph/types/result.types.d.ts +237 -0
- package/dist/graph/types/result.types.js +7 -0
- package/dist/graph/types/result.types.js.map +1 -0
- package/dist/graph/validators/index.d.ts +81 -0
- package/dist/graph/validators/index.js +243 -0
- package/dist/graph/validators/index.js.map +1 -0
- package/dist/imports/imports.service.d.ts +3 -3
- package/dist/imports/imports.service.js +7 -7
- package/dist/imports/imports.service.js.map +1 -1
- package/dist/imports/imports.types.d.ts +8 -0
- package/dist/imports/repos/postmanV21.repo.d.ts +1 -1
- package/dist/imports/repos/postmanV21.repo.js +29 -2
- package/dist/imports/repos/postmanV21.repo.js.map +1 -1
- package/dist/index.d.ts +1244 -150
- package/dist/index.js +1309 -209
- package/dist/index.js.map +1 -1
- package/dist/inputs/inputs.service.js +2 -2
- package/dist/inputs/inputs.service.js.map +1 -1
- package/dist/inputs/utils/inputs.utils.create.js +1 -1
- package/dist/inputs/utils/inputs.utils.create.js.map +1 -1
- package/dist/logs/logs.types.d.ts +5 -0
- package/dist/logs/logs.types.js +1 -0
- package/dist/logs/logs.types.js.map +1 -1
- package/dist/parsers/index.d.ts +3 -0
- package/dist/parsers/index.js +27 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/pipelines/postman.pipelines.d.ts +15 -0
- package/dist/parsers/pipelines/postman.pipelines.js +103 -0
- package/dist/parsers/pipelines/postman.pipelines.js.map +1 -0
- package/dist/parsers/types/postman.types.d.ts +200 -0
- package/dist/parsers/types/postman.types.js +3 -0
- package/dist/parsers/types/postman.types.js.map +1 -0
- package/dist/parsers/utils/postman.utils.d.ts +12 -0
- package/dist/parsers/utils/postman.utils.js +116 -0
- package/dist/parsers/utils/postman.utils.js.map +1 -0
- package/dist/parsers/validators/postman-auth.validators.d.ts +10 -0
- package/dist/parsers/validators/postman-auth.validators.js +127 -0
- package/dist/parsers/validators/postman-auth.validators.js.map +1 -0
- package/dist/parsers/validators/postman-request.validators.d.ts +13 -0
- package/dist/parsers/validators/postman-request.validators.js +139 -0
- package/dist/parsers/validators/postman-request.validators.js.map +1 -0
- package/dist/parsers/validators/postman-response.validators.d.ts +13 -0
- package/dist/parsers/validators/postman-response.validators.js +150 -0
- package/dist/parsers/validators/postman-response.validators.js.map +1 -0
- package/dist/parsers/validators/postman-variable.validators.d.ts +14 -0
- package/dist/parsers/validators/postman-variable.validators.js +163 -0
- package/dist/parsers/validators/postman-variable.validators.js.map +1 -0
- package/dist/pricing/pricing.repo.d.ts +0 -0
- package/dist/pricing/pricing.repo.js +1 -0
- package/dist/pricing/pricing.repo.js.map +1 -0
- package/dist/pricing/pricing.service.d.ts +24 -0
- package/dist/pricing/pricing.service.js +51 -0
- package/dist/pricing/pricing.service.js.map +1 -0
- package/dist/pricing/pricing.types.d.ts +76 -0
- package/dist/pricing/pricing.types.js +21 -0
- package/dist/pricing/pricing.types.js.map +1 -0
- package/dist/pricing/utils/string.utils.d.ts +1 -0
- package/dist/pricing/utils/string.utils.js +9 -0
- package/dist/pricing/utils/string.utils.js.map +1 -0
- package/dist/processor/repos/sms.repo.d.ts +4 -4
- package/dist/processor/repos/sms.repo.js +23 -10
- package/dist/processor/repos/sms.repo.js.map +1 -1
- package/dist/processor/services/messagebrokers/kafka.service.js +0 -2
- package/dist/processor/services/messagebrokers/kafka.service.js.map +1 -1
- package/dist/processor/services/messagebrokers/rabbitmq.service.d.ts +9 -1
- package/dist/processor/services/messagebrokers/rabbitmq.service.js +40 -11
- package/dist/processor/services/messagebrokers/rabbitmq.service.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +38 -10
- package/dist/processor/services/processor.service.js +533 -248
- package/dist/processor/services/processor.service.js.map +1 -1
- package/dist/processor/services/request.service.d.ts +36 -0
- package/dist/processor/services/request.service.js +304 -0
- package/dist/processor/services/request.service.js.map +1 -0
- package/dist/processor/types/request.types.d.ts +14 -0
- package/dist/processor/types/request.types.js +3 -0
- package/dist/processor/types/request.types.js.map +1 -0
- package/dist/processor/utils/processor.utils.d.ts +3 -0
- package/dist/processor/utils/processor.utils.js +84 -22
- package/dist/processor/utils/processor.utils.js.map +1 -1
- package/dist/processor/utils/request.utils.d.ts +20 -0
- package/dist/processor/utils/request.utils.js +113 -0
- package/dist/processor/utils/request.utils.js.map +1 -0
- package/dist/products/services/products.service.d.ts +114 -77
- package/dist/products/services/products.service.js +805 -362
- package/dist/products/services/products.service.js.map +1 -1
- package/dist/products/services/utils/crypt.utils.d.ts +1 -0
- package/dist/products/services/utils/crypt.utils.js +17 -0
- package/dist/products/services/utils/crypt.utils.js.map +1 -0
- package/dist/products/services/utils/functions.utils.d.ts +13 -0
- package/dist/products/services/utils/functions.utils.js +289 -0
- package/dist/products/services/utils/functions.utils.js.map +1 -0
- package/dist/products/services/utils/objects.utils.d.ts +13 -0
- package/dist/products/services/utils/objects.utils.js +89 -0
- package/dist/products/services/utils/objects.utils.js.map +1 -0
- package/dist/products/services/utils/string.utils.d.ts +12 -0
- package/dist/products/services/utils/string.utils.js +168 -0
- package/dist/products/services/utils/string.utils.js.map +1 -0
- package/dist/products/utils/string.utils.d.ts +1 -1
- package/dist/products/utils/string.utils.js +14 -2
- package/dist/products/utils/string.utils.js.map +1 -1
- package/dist/products/validators/index.d.ts +4 -1
- package/dist/products/validators/index.js +7 -1
- package/dist/products/validators/index.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productDatabaseAction.validator.d.ts +15 -4
- package/dist/products/validators/joi-validators/create.productDatabaseAction.validator.js +501 -109
- package/dist/products/validators/joi-validators/create.productDatabaseAction.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productEnv.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.productEnv.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productGraph.validator.d.ts +3 -0
- package/dist/products/validators/joi-validators/create.productGraph.validator.js +82 -0
- package/dist/products/validators/joi-validators/create.productGraph.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/create.productGraphAction.validator.d.ts +14 -0
- package/dist/products/validators/joi-validators/create.productGraphAction.validator.js +696 -0
- package/dist/products/validators/joi-validators/create.productGraphAction.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/create.productHealthcheck.validator.d.ts +4 -0
- package/dist/products/validators/joi-validators/create.productHealthcheck.validator.js +58 -0
- package/dist/products/validators/joi-validators/create.productHealthcheck.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/create.productMessageBrokerTopic.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.productMessageBrokerTopic.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productMessageBrokers.validator.js +9 -4
- package/dist/products/validators/joi-validators/create.productMessageBrokers.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productNotification.validator.js +5 -2
- package/dist/products/validators/joi-validators/create.productNotification.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.userAuth.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.userAuth.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.dataValue.validator.js +1 -0
- package/dist/products/validators/joi-validators/update.dataValue.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productDatabaseAction.validator.d.ts +6 -0
- package/dist/products/validators/joi-validators/update.productDatabaseAction.validator.js +28 -26
- package/dist/products/validators/joi-validators/update.productDatabaseAction.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productEnv.validator.js +3 -0
- package/dist/products/validators/joi-validators/update.productEnv.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productGraph.validator.d.ts +3 -0
- package/dist/products/validators/joi-validators/update.productGraph.validator.js +87 -0
- package/dist/products/validators/joi-validators/update.productGraph.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/update.productGraphAction.validator.d.ts +7 -0
- package/dist/products/validators/joi-validators/update.productGraphAction.validator.js +72 -0
- package/dist/products/validators/joi-validators/update.productGraphAction.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/update.productMessageBrokerTopic.validator.js +1 -0
- package/dist/products/validators/joi-validators/update.productMessageBrokerTopic.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.userAuth.validator.js +1 -0
- package/dist/products/validators/joi-validators/update.userAuth.validator.js.map +1 -1
- package/dist/test/test.health.d.ts +1 -0
- package/dist/test/test.health.js +49 -0
- package/dist/test/test.health.js.map +1 -0
- package/dist/test/test.import.js +51 -4
- package/dist/test/test.import.js.map +1 -1
- package/dist/test/test.imports.js +22 -7
- package/dist/test/test.imports.js.map +1 -1
- package/dist/test/test.notifiers.d.ts +1 -0
- package/dist/test/test.notifiers.js +85 -0
- package/dist/test/test.notifiers.js.map +1 -0
- package/dist/test/test.processor.js +30 -115
- package/dist/test/test.processor.js.map +1 -1
- package/dist/test/test.products.d.ts +1 -0
- package/dist/test/test.products.js +49 -0
- package/dist/test/test.products.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/appBuilder.types.d.ts +5 -12
- package/dist/types/enums.d.ts +4 -1
- package/dist/types/enums.js +3 -0
- package/dist/types/enums.js.map +1 -1
- package/dist/types/index.types.d.ts +4 -0
- package/dist/types/pricing.types.d.ts +4 -0
- package/dist/types/pricing.types.js +3 -0
- package/dist/types/pricing.types.js.map +1 -0
- package/dist/types/processor.types.d.ts +67 -11
- package/dist/types/processor.types.js.map +1 -1
- package/dist/types/productsBuilder.types.d.ts +132 -14
- package/dist/types/productsBuilder.types.js +69 -4
- package/dist/types/productsBuilder.types.js.map +1 -1
- package/dist/types/request-tracker.interface.d.ts +0 -0
- package/dist/types/request-tracker.interface.js +1 -0
- package/dist/types/request-tracker.interface.js.map +1 -0
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -0
- package/package.json +17 -3
|
@@ -0,0 +1,1861 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Neo4j Graph Database Adapter
|
|
4
|
+
* Implements the BaseGraphAdapter for Neo4j databases using the official neo4j-driver
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.Neo4jAdapter = void 0;
|
|
41
|
+
const neo4j_driver_1 = __importStar(require("neo4j-driver"));
|
|
42
|
+
const base_adapter_1 = require("./base.adapter");
|
|
43
|
+
const graph_types_1 = require("../types/graph.types");
|
|
44
|
+
const node_types_1 = require("../types/node.types");
|
|
45
|
+
const relationship_types_1 = require("../types/relationship.types");
|
|
46
|
+
const path_types_1 = require("../types/path.types");
|
|
47
|
+
/**
|
|
48
|
+
* Neo4j connection implementation
|
|
49
|
+
*/
|
|
50
|
+
class Neo4jConnection {
|
|
51
|
+
constructor(config, id) {
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.type = graph_types_1.GraphDatabaseType.NEO4J;
|
|
54
|
+
this.status = graph_types_1.GraphConnectionStatus.DISCONNECTED;
|
|
55
|
+
this.queryLanguage = graph_types_1.GraphQueryLanguage.CYPHER;
|
|
56
|
+
this.driver = null;
|
|
57
|
+
this.session = null;
|
|
58
|
+
this.id = id || `neo4j-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
59
|
+
this.createdAt = new Date();
|
|
60
|
+
}
|
|
61
|
+
async connect() {
|
|
62
|
+
var _a;
|
|
63
|
+
if (this.status === graph_types_1.GraphConnectionStatus.CONNECTED) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.status = graph_types_1.GraphConnectionStatus.CONNECTING;
|
|
67
|
+
try {
|
|
68
|
+
const authToken = neo4j_driver_1.default.auth.basic(this.config.username, this.config.password);
|
|
69
|
+
const driverConfig = {
|
|
70
|
+
maxConnectionPoolSize: this.config.maxConnectionPoolSize || this.config.poolSize || 50,
|
|
71
|
+
connectionAcquisitionTimeout: this.config.connectionAcquisitionTimeout || 60000,
|
|
72
|
+
connectionTimeout: this.config.connectionTimeout || 30000,
|
|
73
|
+
};
|
|
74
|
+
if (this.config.maxConnectionLifetime) {
|
|
75
|
+
driverConfig.maxConnectionLifetime = this.config.maxConnectionLifetime;
|
|
76
|
+
}
|
|
77
|
+
if (this.config.encrypted !== undefined) {
|
|
78
|
+
driverConfig.encrypted = this.config.encrypted;
|
|
79
|
+
}
|
|
80
|
+
if (this.config.trust) {
|
|
81
|
+
driverConfig.trust = this.config.trust;
|
|
82
|
+
}
|
|
83
|
+
if (this.config.trustedCertificates) {
|
|
84
|
+
driverConfig.trustedCertificates = this.config.trustedCertificates;
|
|
85
|
+
}
|
|
86
|
+
if ((_a = this.config.options) === null || _a === void 0 ? void 0 : _a.logging) {
|
|
87
|
+
driverConfig.logging = this.config.options.logging;
|
|
88
|
+
}
|
|
89
|
+
this.driver = neo4j_driver_1.default.driver(this.config.uri, authToken, driverConfig);
|
|
90
|
+
// Verify connectivity
|
|
91
|
+
await this.driver.verifyConnectivity();
|
|
92
|
+
this.status = graph_types_1.GraphConnectionStatus.CONNECTED;
|
|
93
|
+
this.lastUsedAt = new Date();
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
this.status = graph_types_1.GraphConnectionStatus.ERROR;
|
|
97
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.CONNECTION_ERROR, `Failed to connect to Neo4j: ${error.message}`, error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async disconnect() {
|
|
101
|
+
if (this.session) {
|
|
102
|
+
await this.session.close();
|
|
103
|
+
this.session = null;
|
|
104
|
+
}
|
|
105
|
+
if (this.driver) {
|
|
106
|
+
await this.driver.close();
|
|
107
|
+
this.driver = null;
|
|
108
|
+
}
|
|
109
|
+
this.status = graph_types_1.GraphConnectionStatus.DISCONNECTED;
|
|
110
|
+
}
|
|
111
|
+
isConnected() {
|
|
112
|
+
return this.status === graph_types_1.GraphConnectionStatus.CONNECTED && this.driver !== null;
|
|
113
|
+
}
|
|
114
|
+
getClient() {
|
|
115
|
+
if (!this.driver) {
|
|
116
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.CONNECTION_ERROR, 'Neo4j driver not initialized. Call connect() first.');
|
|
117
|
+
}
|
|
118
|
+
return this.driver;
|
|
119
|
+
}
|
|
120
|
+
getSession() {
|
|
121
|
+
if (!this.driver) {
|
|
122
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.CONNECTION_ERROR, 'Neo4j driver not initialized. Call connect() first.');
|
|
123
|
+
}
|
|
124
|
+
const sessionConfig = {};
|
|
125
|
+
if (this.config.database) {
|
|
126
|
+
sessionConfig.database = this.config.database;
|
|
127
|
+
}
|
|
128
|
+
this.session = this.driver.session(sessionConfig);
|
|
129
|
+
this.lastUsedAt = new Date();
|
|
130
|
+
return this.session;
|
|
131
|
+
}
|
|
132
|
+
getDatabase() {
|
|
133
|
+
return this.config.database || 'neo4j';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Neo4j transaction implementation
|
|
138
|
+
*/
|
|
139
|
+
class Neo4jTransaction {
|
|
140
|
+
constructor(connection, session, transaction) {
|
|
141
|
+
this.connection = connection;
|
|
142
|
+
this.session = session;
|
|
143
|
+
this.status = graph_types_1.GraphTransactionStatus.PENDING;
|
|
144
|
+
this.id = `tx-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
145
|
+
this.createdAt = new Date();
|
|
146
|
+
this.native = transaction;
|
|
147
|
+
this.status = graph_types_1.GraphTransactionStatus.ACTIVE;
|
|
148
|
+
}
|
|
149
|
+
async commit() {
|
|
150
|
+
if (this.status !== graph_types_1.GraphTransactionStatus.ACTIVE) {
|
|
151
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.TRANSACTION_ERROR, `Cannot commit transaction in ${this.status} state`);
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
await this.native.commit();
|
|
155
|
+
this.status = graph_types_1.GraphTransactionStatus.COMMITTED;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
this.status = graph_types_1.GraphTransactionStatus.FAILED;
|
|
159
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.TRANSACTION_ERROR, `Failed to commit transaction: ${error.message}`, error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async rollback() {
|
|
163
|
+
if (this.status !== graph_types_1.GraphTransactionStatus.ACTIVE) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
await this.native.rollback();
|
|
168
|
+
this.status = graph_types_1.GraphTransactionStatus.ROLLED_BACK;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
this.status = graph_types_1.GraphTransactionStatus.FAILED;
|
|
172
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.TRANSACTION_ERROR, `Failed to rollback transaction: ${error.message}`, error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
isActive() {
|
|
176
|
+
return this.status === graph_types_1.GraphTransactionStatus.ACTIVE;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Neo4j Graph Database Adapter
|
|
181
|
+
*/
|
|
182
|
+
class Neo4jAdapter extends base_adapter_1.BaseGraphAdapter {
|
|
183
|
+
constructor() {
|
|
184
|
+
super(...arguments);
|
|
185
|
+
this.type = graph_types_1.GraphDatabaseType.NEO4J;
|
|
186
|
+
this.supportedLanguages = [graph_types_1.GraphQueryLanguage.CYPHER];
|
|
187
|
+
this.defaultLanguage = graph_types_1.GraphQueryLanguage.CYPHER;
|
|
188
|
+
}
|
|
189
|
+
// ==================== CONNECTION METHODS ====================
|
|
190
|
+
async connect(config) {
|
|
191
|
+
if (config.type !== graph_types_1.GraphDatabaseType.NEO4J) {
|
|
192
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.VALIDATION_ERROR, `Invalid config type ${config.type}. Expected ${graph_types_1.GraphDatabaseType.NEO4J}`);
|
|
193
|
+
}
|
|
194
|
+
const connection = new Neo4jConnection(config);
|
|
195
|
+
await connection.connect();
|
|
196
|
+
return connection;
|
|
197
|
+
}
|
|
198
|
+
async disconnect(connection) {
|
|
199
|
+
await connection.disconnect();
|
|
200
|
+
}
|
|
201
|
+
async testConnection(connection) {
|
|
202
|
+
var _a;
|
|
203
|
+
const startTime = Date.now();
|
|
204
|
+
try {
|
|
205
|
+
const session = connection.getSession();
|
|
206
|
+
const result = await session.run('RETURN 1 AS test');
|
|
207
|
+
await session.close();
|
|
208
|
+
const driver = connection.getClient();
|
|
209
|
+
const serverInfo = await driver.getServerInfo();
|
|
210
|
+
return {
|
|
211
|
+
connected: true,
|
|
212
|
+
message: 'Connection successful',
|
|
213
|
+
databaseType: graph_types_1.GraphDatabaseType.NEO4J,
|
|
214
|
+
queryLanguage: graph_types_1.GraphQueryLanguage.CYPHER,
|
|
215
|
+
version: (_a = serverInfo.protocolVersion) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
216
|
+
responseTime: Date.now() - startTime,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
return {
|
|
221
|
+
connected: false,
|
|
222
|
+
message: 'Connection failed',
|
|
223
|
+
databaseType: graph_types_1.GraphDatabaseType.NEO4J,
|
|
224
|
+
queryLanguage: graph_types_1.GraphQueryLanguage.CYPHER,
|
|
225
|
+
responseTime: Date.now() - startTime,
|
|
226
|
+
error: error.message,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async getDatabaseInfo(connection) {
|
|
231
|
+
const session = connection.getSession();
|
|
232
|
+
try {
|
|
233
|
+
const result = await session.run('CALL dbms.components()');
|
|
234
|
+
const record = result.records[0];
|
|
235
|
+
return {
|
|
236
|
+
version: record.get('versions')[0],
|
|
237
|
+
edition: record.get('edition'),
|
|
238
|
+
features: [],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
await session.close();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// ==================== TRANSACTION METHODS ====================
|
|
246
|
+
async beginTransaction(connection, options) {
|
|
247
|
+
const session = connection.getSession();
|
|
248
|
+
const txConfig = {};
|
|
249
|
+
if (options === null || options === void 0 ? void 0 : options.timeout) {
|
|
250
|
+
txConfig.timeout = (0, neo4j_driver_1.int)(options.timeout);
|
|
251
|
+
}
|
|
252
|
+
if (options === null || options === void 0 ? void 0 : options.metadata) {
|
|
253
|
+
txConfig.metadata = options.metadata;
|
|
254
|
+
}
|
|
255
|
+
const accessMode = (options === null || options === void 0 ? void 0 : options.accessMode) === 'READ'
|
|
256
|
+
? neo4j_driver_1.default.session.READ
|
|
257
|
+
: neo4j_driver_1.default.session.WRITE;
|
|
258
|
+
const transaction = session.beginTransaction(txConfig);
|
|
259
|
+
return new Neo4jTransaction(connection, session, transaction);
|
|
260
|
+
}
|
|
261
|
+
async commitTransaction(connection, transaction) {
|
|
262
|
+
await transaction.commit();
|
|
263
|
+
}
|
|
264
|
+
async rollbackTransaction(connection, transaction) {
|
|
265
|
+
await transaction.rollback();
|
|
266
|
+
}
|
|
267
|
+
// ==================== RAW QUERY METHODS ====================
|
|
268
|
+
async executeRaw(connection, options, transaction) {
|
|
269
|
+
const startTime = Date.now();
|
|
270
|
+
try {
|
|
271
|
+
let result;
|
|
272
|
+
if (transaction && transaction.isActive()) {
|
|
273
|
+
result = await transaction.native.run(options.query, options.params || {});
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const session = connection.getSession();
|
|
277
|
+
try {
|
|
278
|
+
result = await session.run(options.query, options.params || {});
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
await session.close();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const records = result.records.map((record) => {
|
|
285
|
+
const obj = {};
|
|
286
|
+
record.keys.forEach((key) => {
|
|
287
|
+
obj[key] = this.convertNeo4jValue(record.get(key));
|
|
288
|
+
});
|
|
289
|
+
return obj;
|
|
290
|
+
});
|
|
291
|
+
return {
|
|
292
|
+
success: true,
|
|
293
|
+
executionTime: Date.now() - startTime,
|
|
294
|
+
data: records,
|
|
295
|
+
columns: result.records.length > 0 ? result.records[0].keys.map(k => String(k)) : [],
|
|
296
|
+
count: records.length,
|
|
297
|
+
statistics: this.parseStatistics(result.summary.counters),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
throw this.wrapError(error, graph_types_1.GraphErrorType.QUERY_ERROR, `Query execution failed: ${error.message}`, options.query, options.params);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// ==================== NODE OPERATIONS ====================
|
|
305
|
+
async createNode(connection, options, transaction) {
|
|
306
|
+
const startTime = Date.now();
|
|
307
|
+
const labels = this.normalizeLabels(options.labels);
|
|
308
|
+
const labelStr = labels.map(l => this.escapeIdentifier(l)).join(':');
|
|
309
|
+
const { clause: propsClause, params } = this.buildPropertiesClause(options.properties);
|
|
310
|
+
const query = `CREATE (n:${labelStr} ${propsClause}) RETURN n`;
|
|
311
|
+
try {
|
|
312
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
313
|
+
const node = result.data.length > 0 ? this.parseNode(result.data[0].n) : undefined;
|
|
314
|
+
return {
|
|
315
|
+
success: true,
|
|
316
|
+
executionTime: Date.now() - startTime,
|
|
317
|
+
node,
|
|
318
|
+
created: true,
|
|
319
|
+
statistics: result.statistics,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
if (options.onConflict === 'ignore') {
|
|
324
|
+
return {
|
|
325
|
+
success: true,
|
|
326
|
+
executionTime: Date.now() - startTime,
|
|
327
|
+
created: false,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async findNodes(connection, options, transaction) {
|
|
334
|
+
const startTime = Date.now();
|
|
335
|
+
const labels = options.labels ? this.normalizeLabels(options.labels) : [];
|
|
336
|
+
const labelStr = labels.length > 0 ? ':' + labels.map(l => this.escapeIdentifier(l)).join(':') : '';
|
|
337
|
+
let query = `MATCH (n${labelStr})`;
|
|
338
|
+
const params = {};
|
|
339
|
+
// Build WHERE clause
|
|
340
|
+
if (options.where || options.properties) {
|
|
341
|
+
const whereParts = [];
|
|
342
|
+
if (options.where) {
|
|
343
|
+
const { clause, params: whereParams } = this.buildWhereFromClause(options.where, 'n');
|
|
344
|
+
whereParts.push(clause);
|
|
345
|
+
Object.assign(params, whereParams);
|
|
346
|
+
}
|
|
347
|
+
if (options.properties) {
|
|
348
|
+
Object.entries(options.properties).forEach(([key, value], index) => {
|
|
349
|
+
const paramName = `prop_${index}`;
|
|
350
|
+
whereParts.push(`n.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
351
|
+
params[paramName] = value;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
if (whereParts.length > 0) {
|
|
355
|
+
query += ` WHERE ${whereParts.join(' AND ')}`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Return specific properties or entire node
|
|
359
|
+
if (options.returnProperties && options.returnProperties.length > 0) {
|
|
360
|
+
const props = options.returnProperties.map(p => `n.${this.escapeIdentifier(p)} AS ${p}`).join(', ');
|
|
361
|
+
query += ` RETURN ${props}`;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
query += ` RETURN n`;
|
|
365
|
+
}
|
|
366
|
+
// ORDER BY
|
|
367
|
+
if (options.orderBy && options.orderBy.length > 0) {
|
|
368
|
+
const orderParts = options.orderBy.map(o => `n.${this.escapeIdentifier(o.property)} ${o.direction}`);
|
|
369
|
+
query += ` ORDER BY ${orderParts.join(', ')}`;
|
|
370
|
+
}
|
|
371
|
+
// SKIP and LIMIT
|
|
372
|
+
if (options.skip !== undefined) {
|
|
373
|
+
query += ` SKIP ${options.skip}`;
|
|
374
|
+
}
|
|
375
|
+
if (options.limit !== undefined) {
|
|
376
|
+
query += ` LIMIT ${options.limit}`;
|
|
377
|
+
}
|
|
378
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
379
|
+
const nodes = result.data.map(row => {
|
|
380
|
+
if (options.returnProperties) {
|
|
381
|
+
// Reconstruct node from properties
|
|
382
|
+
return {
|
|
383
|
+
id: '',
|
|
384
|
+
labels: [],
|
|
385
|
+
properties: row,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return this.parseNode(row.n);
|
|
389
|
+
});
|
|
390
|
+
return {
|
|
391
|
+
success: true,
|
|
392
|
+
executionTime: Date.now() - startTime,
|
|
393
|
+
nodes,
|
|
394
|
+
count: nodes.length,
|
|
395
|
+
hasMore: options.limit !== undefined && nodes.length === options.limit,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async findNodeById(connection, id, transaction) {
|
|
399
|
+
const query = typeof id === 'string'
|
|
400
|
+
? `MATCH (n) WHERE elementId(n) = $id RETURN n`
|
|
401
|
+
: `MATCH (n) WHERE id(n) = $id RETURN n`;
|
|
402
|
+
const result = await this.executeRaw(connection, { query, params: { id } }, transaction);
|
|
403
|
+
if (result.data.length === 0) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
return this.parseNode(result.data[0].n);
|
|
407
|
+
}
|
|
408
|
+
async updateNode(connection, options, transaction) {
|
|
409
|
+
var _a;
|
|
410
|
+
const startTime = Date.now();
|
|
411
|
+
const params = {};
|
|
412
|
+
let matchClause = 'MATCH (n)';
|
|
413
|
+
// Build match by ID or labels
|
|
414
|
+
if (options.id !== undefined) {
|
|
415
|
+
if (typeof options.id === 'string') {
|
|
416
|
+
matchClause = 'MATCH (n) WHERE elementId(n) = $nodeId';
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
matchClause = 'MATCH (n) WHERE id(n) = $nodeId';
|
|
420
|
+
}
|
|
421
|
+
params.nodeId = options.id;
|
|
422
|
+
}
|
|
423
|
+
else if (options.labels) {
|
|
424
|
+
const labels = this.normalizeLabels(options.labels);
|
|
425
|
+
matchClause = `MATCH (n:${labels.map(l => this.escapeIdentifier(l)).join(':')})`;
|
|
426
|
+
}
|
|
427
|
+
// Add WHERE clause
|
|
428
|
+
if (options.where) {
|
|
429
|
+
const { clause, params: whereParams } = this.buildWhereFromClause(options.where, 'n');
|
|
430
|
+
matchClause += options.id === undefined && !options.labels ? ` WHERE ${clause}` : ` AND ${clause}`;
|
|
431
|
+
Object.assign(params, whereParams);
|
|
432
|
+
}
|
|
433
|
+
const setParts = [];
|
|
434
|
+
const removeParts = [];
|
|
435
|
+
// SET properties
|
|
436
|
+
if (options.set) {
|
|
437
|
+
Object.entries(options.set).forEach(([key, value], index) => {
|
|
438
|
+
const paramName = `set_${index}`;
|
|
439
|
+
setParts.push(`n.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
440
|
+
params[paramName] = value;
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
// Add labels
|
|
444
|
+
if (options.addLabels && options.addLabels.length > 0) {
|
|
445
|
+
setParts.push(`n:${options.addLabels.map(l => this.escapeIdentifier(l)).join(':')}`);
|
|
446
|
+
}
|
|
447
|
+
// REMOVE properties
|
|
448
|
+
if (options.remove && options.remove.length > 0) {
|
|
449
|
+
options.remove.forEach(prop => {
|
|
450
|
+
removeParts.push(`n.${this.escapeIdentifier(prop)}`);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
// Remove labels
|
|
454
|
+
if (options.removeLabels && options.removeLabels.length > 0) {
|
|
455
|
+
removeParts.push(`n:${options.removeLabels.map(l => this.escapeIdentifier(l)).join(':')}`);
|
|
456
|
+
}
|
|
457
|
+
let query = matchClause;
|
|
458
|
+
if (setParts.length > 0) {
|
|
459
|
+
query += ` SET ${setParts.join(', ')}`;
|
|
460
|
+
}
|
|
461
|
+
if (removeParts.length > 0) {
|
|
462
|
+
query += ` REMOVE ${removeParts.join(', ')}`;
|
|
463
|
+
}
|
|
464
|
+
query += ' RETURN n';
|
|
465
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
466
|
+
return {
|
|
467
|
+
success: true,
|
|
468
|
+
executionTime: Date.now() - startTime,
|
|
469
|
+
node: result.data.length > 0 ? this.parseNode(result.data[0].n) : undefined,
|
|
470
|
+
updated: result.data.length > 0,
|
|
471
|
+
matchedCount: result.data.length,
|
|
472
|
+
modifiedCount: ((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.propertiesSet) || 0,
|
|
473
|
+
statistics: result.statistics,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
async deleteNode(connection, options, transaction) {
|
|
477
|
+
var _a, _b;
|
|
478
|
+
const startTime = Date.now();
|
|
479
|
+
const params = {};
|
|
480
|
+
let matchClause = 'MATCH (n)';
|
|
481
|
+
if (options.id !== undefined) {
|
|
482
|
+
if (typeof options.id === 'string') {
|
|
483
|
+
matchClause = 'MATCH (n) WHERE elementId(n) = $nodeId';
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
matchClause = 'MATCH (n) WHERE id(n) = $nodeId';
|
|
487
|
+
}
|
|
488
|
+
params.nodeId = options.id;
|
|
489
|
+
}
|
|
490
|
+
else if (options.labels) {
|
|
491
|
+
const labels = this.normalizeLabels(options.labels);
|
|
492
|
+
matchClause = `MATCH (n:${labels.map(l => this.escapeIdentifier(l)).join(':')})`;
|
|
493
|
+
}
|
|
494
|
+
if (options.where) {
|
|
495
|
+
const { clause, params: whereParams } = this.buildWhereFromClause(options.where, 'n');
|
|
496
|
+
matchClause += options.id === undefined && !options.labels ? ` WHERE ${clause}` : ` AND ${clause}`;
|
|
497
|
+
Object.assign(params, whereParams);
|
|
498
|
+
}
|
|
499
|
+
const deleteCmd = options.detach ? 'DETACH DELETE' : 'DELETE';
|
|
500
|
+
const query = `${matchClause} ${deleteCmd} n`;
|
|
501
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
502
|
+
return {
|
|
503
|
+
success: true,
|
|
504
|
+
executionTime: Date.now() - startTime,
|
|
505
|
+
deletedCount: ((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.nodesDeleted) || 0,
|
|
506
|
+
deletedRelationshipsCount: ((_b = result.statistics) === null || _b === void 0 ? void 0 : _b.relationshipsDeleted) || 0,
|
|
507
|
+
statistics: result.statistics,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async mergeNode(connection, options, transaction) {
|
|
511
|
+
var _a;
|
|
512
|
+
const startTime = Date.now();
|
|
513
|
+
const labels = this.normalizeLabels(options.labels);
|
|
514
|
+
const labelStr = labels.map(l => this.escapeIdentifier(l)).join(':');
|
|
515
|
+
const params = {};
|
|
516
|
+
const matchProps = [];
|
|
517
|
+
Object.entries(options.matchProperties).forEach(([key, value], index) => {
|
|
518
|
+
const paramName = `match_${index}`;
|
|
519
|
+
matchProps.push(`${this.escapeIdentifier(key)}: $${paramName}`);
|
|
520
|
+
params[paramName] = value;
|
|
521
|
+
});
|
|
522
|
+
let query = `MERGE (n:${labelStr} {${matchProps.join(', ')}})`;
|
|
523
|
+
if (options.onCreate && Object.keys(options.onCreate).length > 0) {
|
|
524
|
+
const createProps = [];
|
|
525
|
+
Object.entries(options.onCreate).forEach(([key, value], index) => {
|
|
526
|
+
const paramName = `create_${index}`;
|
|
527
|
+
createProps.push(`n.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
528
|
+
params[paramName] = value;
|
|
529
|
+
});
|
|
530
|
+
query += ` ON CREATE SET ${createProps.join(', ')}`;
|
|
531
|
+
}
|
|
532
|
+
if (options.onMatch && Object.keys(options.onMatch).length > 0) {
|
|
533
|
+
const matchSetProps = [];
|
|
534
|
+
Object.entries(options.onMatch).forEach(([key, value], index) => {
|
|
535
|
+
const paramName = `match_set_${index}`;
|
|
536
|
+
matchSetProps.push(`n.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
537
|
+
params[paramName] = value;
|
|
538
|
+
});
|
|
539
|
+
query += ` ON MATCH SET ${matchSetProps.join(', ')}`;
|
|
540
|
+
}
|
|
541
|
+
query += ' RETURN n';
|
|
542
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
543
|
+
const created = (((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.nodesCreated) || 0) > 0;
|
|
544
|
+
return {
|
|
545
|
+
success: true,
|
|
546
|
+
executionTime: Date.now() - startTime,
|
|
547
|
+
node: result.data.length > 0 ? this.parseNode(result.data[0].n) : undefined,
|
|
548
|
+
created,
|
|
549
|
+
matched: !created,
|
|
550
|
+
statistics: result.statistics,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
// ==================== RELATIONSHIP OPERATIONS ====================
|
|
554
|
+
async createRelationship(connection, options, transaction) {
|
|
555
|
+
var _a;
|
|
556
|
+
const startTime = Date.now();
|
|
557
|
+
const params = {};
|
|
558
|
+
const fromMatch = this.buildNodeReference(options.fromNode, 'from', params);
|
|
559
|
+
const toMatch = this.buildNodeReference(options.toNode, 'to', params);
|
|
560
|
+
let propsClause = '';
|
|
561
|
+
if (options.properties && Object.keys(options.properties).length > 0) {
|
|
562
|
+
const { clause, params: propParams } = this.buildPropertiesClause(options.properties, 'rel_');
|
|
563
|
+
propsClause = ` ${clause}`;
|
|
564
|
+
Object.assign(params, propParams);
|
|
565
|
+
}
|
|
566
|
+
const query = `
|
|
567
|
+
MATCH ${fromMatch}, ${toMatch}
|
|
568
|
+
CREATE (from)-[r:${this.escapeIdentifier(options.type)}${propsClause}]->(to)
|
|
569
|
+
RETURN r, from, to
|
|
570
|
+
`;
|
|
571
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
572
|
+
return {
|
|
573
|
+
success: true,
|
|
574
|
+
executionTime: Date.now() - startTime,
|
|
575
|
+
relationship: result.data.length > 0 ? this.parseRelationship(result.data[0].r) : undefined,
|
|
576
|
+
created: (((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.relationshipsCreated) || 0) > 0,
|
|
577
|
+
statistics: result.statistics,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
async findRelationships(connection, options, transaction) {
|
|
581
|
+
const startTime = Date.now();
|
|
582
|
+
const params = {};
|
|
583
|
+
// Build relationship type pattern
|
|
584
|
+
let typePattern = '';
|
|
585
|
+
if (options.type) {
|
|
586
|
+
const types = Array.isArray(options.type) ? options.type : [options.type];
|
|
587
|
+
typePattern = ':' + types.map(t => this.escapeIdentifier(t)).join('|');
|
|
588
|
+
}
|
|
589
|
+
// Build direction pattern
|
|
590
|
+
const directionStart = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
591
|
+
const directionEnd = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
592
|
+
options.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
593
|
+
let query = `MATCH (from)${directionStart}[r${typePattern}]${directionEnd}(to)`;
|
|
594
|
+
const whereParts = [];
|
|
595
|
+
// From node filter
|
|
596
|
+
if (options.fromNode) {
|
|
597
|
+
const fromWhere = this.buildNodeReferenceWhere(options.fromNode, 'from', params, 'from_');
|
|
598
|
+
if (fromWhere)
|
|
599
|
+
whereParts.push(fromWhere);
|
|
600
|
+
}
|
|
601
|
+
// To node filter
|
|
602
|
+
if (options.toNode) {
|
|
603
|
+
const toWhere = this.buildNodeReferenceWhere(options.toNode, 'to', params, 'to_');
|
|
604
|
+
if (toWhere)
|
|
605
|
+
whereParts.push(toWhere);
|
|
606
|
+
}
|
|
607
|
+
// Relationship properties filter
|
|
608
|
+
if (options.where) {
|
|
609
|
+
const { clause, params: whereParams } = this.buildRelationshipWhereClause(options.where, 'r');
|
|
610
|
+
whereParts.push(clause);
|
|
611
|
+
Object.assign(params, whereParams);
|
|
612
|
+
}
|
|
613
|
+
if (options.properties) {
|
|
614
|
+
Object.entries(options.properties).forEach(([key, value], index) => {
|
|
615
|
+
const paramName = `rel_prop_${index}`;
|
|
616
|
+
whereParts.push(`r.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
617
|
+
params[paramName] = value;
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
if (whereParts.length > 0) {
|
|
621
|
+
query += ` WHERE ${whereParts.join(' AND ')}`;
|
|
622
|
+
}
|
|
623
|
+
// Return clause
|
|
624
|
+
if (options.includeNodes) {
|
|
625
|
+
query += ' RETURN r, from, to';
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
query += ' RETURN r';
|
|
629
|
+
}
|
|
630
|
+
// ORDER BY
|
|
631
|
+
if (options.orderBy && options.orderBy.length > 0) {
|
|
632
|
+
const orderParts = options.orderBy.map(o => `r.${this.escapeIdentifier(o.property)} ${o.direction}`);
|
|
633
|
+
query += ` ORDER BY ${orderParts.join(', ')}`;
|
|
634
|
+
}
|
|
635
|
+
// SKIP and LIMIT
|
|
636
|
+
if (options.skip !== undefined) {
|
|
637
|
+
query += ` SKIP ${options.skip}`;
|
|
638
|
+
}
|
|
639
|
+
if (options.limit !== undefined) {
|
|
640
|
+
query += ` LIMIT ${options.limit}`;
|
|
641
|
+
}
|
|
642
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
643
|
+
const relationships = result.data.map(row => this.parseRelationship(row.r));
|
|
644
|
+
return {
|
|
645
|
+
success: true,
|
|
646
|
+
executionTime: Date.now() - startTime,
|
|
647
|
+
relationships,
|
|
648
|
+
count: relationships.length,
|
|
649
|
+
hasMore: options.limit !== undefined && relationships.length === options.limit,
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
async findRelationshipById(connection, id, transaction) {
|
|
653
|
+
const query = typeof id === 'string'
|
|
654
|
+
? `MATCH ()-[r]->() WHERE elementId(r) = $id RETURN r`
|
|
655
|
+
: `MATCH ()-[r]->() WHERE id(r) = $id RETURN r`;
|
|
656
|
+
const result = await this.executeRaw(connection, { query, params: { id } }, transaction);
|
|
657
|
+
if (result.data.length === 0) {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
return this.parseRelationship(result.data[0].r);
|
|
661
|
+
}
|
|
662
|
+
async updateRelationship(connection, options, transaction) {
|
|
663
|
+
var _a;
|
|
664
|
+
const startTime = Date.now();
|
|
665
|
+
const params = {};
|
|
666
|
+
let matchClause = 'MATCH ()-[r]->()';
|
|
667
|
+
if (options.id !== undefined) {
|
|
668
|
+
if (typeof options.id === 'string') {
|
|
669
|
+
matchClause = 'MATCH ()-[r]->() WHERE elementId(r) = $relId';
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
matchClause = 'MATCH ()-[r]->() WHERE id(r) = $relId';
|
|
673
|
+
}
|
|
674
|
+
params.relId = options.id;
|
|
675
|
+
}
|
|
676
|
+
else if (options.type) {
|
|
677
|
+
matchClause = `MATCH ()-[r:${this.escapeIdentifier(options.type)}]->()`;
|
|
678
|
+
}
|
|
679
|
+
if (options.where) {
|
|
680
|
+
const { clause, params: whereParams } = this.buildRelationshipWhereClause(options.where, 'r');
|
|
681
|
+
matchClause += options.id === undefined && !options.type ? ` WHERE ${clause}` : ` AND ${clause}`;
|
|
682
|
+
Object.assign(params, whereParams);
|
|
683
|
+
}
|
|
684
|
+
const setParts = [];
|
|
685
|
+
const removeParts = [];
|
|
686
|
+
if (options.set) {
|
|
687
|
+
Object.entries(options.set).forEach(([key, value], index) => {
|
|
688
|
+
const paramName = `set_${index}`;
|
|
689
|
+
setParts.push(`r.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
690
|
+
params[paramName] = value;
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
if (options.remove && options.remove.length > 0) {
|
|
694
|
+
options.remove.forEach(prop => {
|
|
695
|
+
removeParts.push(`r.${this.escapeIdentifier(prop)}`);
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
let query = matchClause;
|
|
699
|
+
if (setParts.length > 0) {
|
|
700
|
+
query += ` SET ${setParts.join(', ')}`;
|
|
701
|
+
}
|
|
702
|
+
if (removeParts.length > 0) {
|
|
703
|
+
query += ` REMOVE ${removeParts.join(', ')}`;
|
|
704
|
+
}
|
|
705
|
+
query += ' RETURN r';
|
|
706
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
707
|
+
return {
|
|
708
|
+
success: true,
|
|
709
|
+
executionTime: Date.now() - startTime,
|
|
710
|
+
relationship: result.data.length > 0 ? this.parseRelationship(result.data[0].r) : undefined,
|
|
711
|
+
updated: result.data.length > 0,
|
|
712
|
+
matchedCount: result.data.length,
|
|
713
|
+
modifiedCount: ((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.propertiesSet) || 0,
|
|
714
|
+
statistics: result.statistics,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
async deleteRelationship(connection, options, transaction) {
|
|
718
|
+
var _a;
|
|
719
|
+
const startTime = Date.now();
|
|
720
|
+
const params = {};
|
|
721
|
+
let matchClause = 'MATCH ()-[r]->()';
|
|
722
|
+
if (options.id !== undefined) {
|
|
723
|
+
if (typeof options.id === 'string') {
|
|
724
|
+
matchClause = 'MATCH ()-[r]->() WHERE elementId(r) = $relId';
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
matchClause = 'MATCH ()-[r]->() WHERE id(r) = $relId';
|
|
728
|
+
}
|
|
729
|
+
params.relId = options.id;
|
|
730
|
+
}
|
|
731
|
+
else if (options.type) {
|
|
732
|
+
const types = Array.isArray(options.type) ? options.type : [options.type];
|
|
733
|
+
matchClause = `MATCH ()-[r:${types.map(t => this.escapeIdentifier(t)).join('|')}]->()`;
|
|
734
|
+
}
|
|
735
|
+
if (options.where) {
|
|
736
|
+
const { clause, params: whereParams } = this.buildRelationshipWhereClause(options.where, 'r');
|
|
737
|
+
matchClause += options.id === undefined && !options.type ? ` WHERE ${clause}` : ` AND ${clause}`;
|
|
738
|
+
Object.assign(params, whereParams);
|
|
739
|
+
}
|
|
740
|
+
const query = `${matchClause} DELETE r`;
|
|
741
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
742
|
+
return {
|
|
743
|
+
success: true,
|
|
744
|
+
executionTime: Date.now() - startTime,
|
|
745
|
+
deletedCount: ((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.relationshipsDeleted) || 0,
|
|
746
|
+
statistics: result.statistics,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
async mergeRelationship(connection, options, transaction) {
|
|
750
|
+
var _a;
|
|
751
|
+
const startTime = Date.now();
|
|
752
|
+
const params = {};
|
|
753
|
+
const fromMatch = this.buildNodeReference(options.fromNode, 'from', params);
|
|
754
|
+
const toMatch = this.buildNodeReference(options.toNode, 'to', params);
|
|
755
|
+
let matchPropsClause = '';
|
|
756
|
+
if (options.matchProperties && Object.keys(options.matchProperties).length > 0) {
|
|
757
|
+
const { clause, params: matchParams } = this.buildPropertiesClause(options.matchProperties, 'match_');
|
|
758
|
+
matchPropsClause = ` ${clause}`;
|
|
759
|
+
Object.assign(params, matchParams);
|
|
760
|
+
}
|
|
761
|
+
let query = `
|
|
762
|
+
MATCH ${fromMatch}, ${toMatch}
|
|
763
|
+
MERGE (from)-[r:${this.escapeIdentifier(options.type)}${matchPropsClause}]->(to)
|
|
764
|
+
`;
|
|
765
|
+
if (options.onCreate && Object.keys(options.onCreate).length > 0) {
|
|
766
|
+
const createProps = [];
|
|
767
|
+
Object.entries(options.onCreate).forEach(([key, value], index) => {
|
|
768
|
+
const paramName = `create_${index}`;
|
|
769
|
+
createProps.push(`r.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
770
|
+
params[paramName] = value;
|
|
771
|
+
});
|
|
772
|
+
query += ` ON CREATE SET ${createProps.join(', ')}`;
|
|
773
|
+
}
|
|
774
|
+
if (options.onMatch && Object.keys(options.onMatch).length > 0) {
|
|
775
|
+
const matchSetProps = [];
|
|
776
|
+
Object.entries(options.onMatch).forEach(([key, value], index) => {
|
|
777
|
+
const paramName = `match_set_${index}`;
|
|
778
|
+
matchSetProps.push(`r.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
779
|
+
params[paramName] = value;
|
|
780
|
+
});
|
|
781
|
+
query += ` ON MATCH SET ${matchSetProps.join(', ')}`;
|
|
782
|
+
}
|
|
783
|
+
query += ' RETURN r';
|
|
784
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
785
|
+
const created = (((_a = result.statistics) === null || _a === void 0 ? void 0 : _a.relationshipsCreated) || 0) > 0;
|
|
786
|
+
return {
|
|
787
|
+
success: true,
|
|
788
|
+
executionTime: Date.now() - startTime,
|
|
789
|
+
relationship: result.data.length > 0 ? this.parseRelationship(result.data[0].r) : undefined,
|
|
790
|
+
created,
|
|
791
|
+
matched: !created,
|
|
792
|
+
statistics: result.statistics,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
// ==================== PATH & TRAVERSAL OPERATIONS ====================
|
|
796
|
+
async traverse(connection, options, transaction) {
|
|
797
|
+
const startTime = Date.now();
|
|
798
|
+
const params = {};
|
|
799
|
+
const startNodeMatch = this.buildNodeSelectorMatch(options.startNode, 'start', params);
|
|
800
|
+
// Build relationship pattern
|
|
801
|
+
const minDepth = options.minDepth || 1;
|
|
802
|
+
const maxDepth = options.maxDepth || 10;
|
|
803
|
+
const relPattern = this.buildTraversalRelPattern(options);
|
|
804
|
+
const depthPattern = minDepth === maxDepth ? `*${minDepth}` : `*${minDepth}..${maxDepth}`;
|
|
805
|
+
// Build direction
|
|
806
|
+
const dirStart = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
807
|
+
const dirEnd = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
808
|
+
options.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
809
|
+
let query = `
|
|
810
|
+
MATCH ${startNodeMatch}
|
|
811
|
+
MATCH path = (start)${dirStart}[${relPattern}${depthPattern}]${dirEnd}(end)
|
|
812
|
+
`;
|
|
813
|
+
// Add filters
|
|
814
|
+
if (options.filters) {
|
|
815
|
+
const filterParts = [];
|
|
816
|
+
if (options.filters.nodeLabels && options.filters.nodeLabels.length > 0) {
|
|
817
|
+
const labelConditions = options.filters.nodeLabels
|
|
818
|
+
.map(l => `'${l}' IN labels(end)`)
|
|
819
|
+
.join(' OR ');
|
|
820
|
+
filterParts.push(`(${labelConditions})`);
|
|
821
|
+
}
|
|
822
|
+
if (options.filters.stopAtLabels && options.filters.stopAtLabels.length > 0) {
|
|
823
|
+
const stopConditions = options.filters.stopAtLabels
|
|
824
|
+
.map(l => `NOT '${l}' IN labels(end)`)
|
|
825
|
+
.join(' AND ');
|
|
826
|
+
filterParts.push(`(${stopConditions})`);
|
|
827
|
+
}
|
|
828
|
+
if (filterParts.length > 0) {
|
|
829
|
+
query += ` WHERE ${filterParts.join(' AND ')}`;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
// Return clause
|
|
833
|
+
const returnParts = [];
|
|
834
|
+
if (options.returnPaths) {
|
|
835
|
+
returnParts.push('path');
|
|
836
|
+
}
|
|
837
|
+
if (options.returnNodes) {
|
|
838
|
+
returnParts.push('COLLECT(DISTINCT end) AS nodes');
|
|
839
|
+
}
|
|
840
|
+
if (returnParts.length === 0) {
|
|
841
|
+
returnParts.push('path');
|
|
842
|
+
}
|
|
843
|
+
query += ` RETURN ${returnParts.join(', ')}`;
|
|
844
|
+
if (options.limit) {
|
|
845
|
+
query += ` LIMIT ${options.limit}`;
|
|
846
|
+
}
|
|
847
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
848
|
+
const paths = [];
|
|
849
|
+
const nodesSet = new Map();
|
|
850
|
+
const relsSet = new Map();
|
|
851
|
+
result.data.forEach(row => {
|
|
852
|
+
if (row.path) {
|
|
853
|
+
const parsedPath = this.parsePath(row.path);
|
|
854
|
+
paths.push(parsedPath);
|
|
855
|
+
parsedPath.nodes.forEach(n => nodesSet.set(String(n.id), n));
|
|
856
|
+
parsedPath.relationships.forEach(r => relsSet.set(String(r.id), r));
|
|
857
|
+
}
|
|
858
|
+
if (row.nodes) {
|
|
859
|
+
row.nodes.forEach((n) => {
|
|
860
|
+
const parsed = this.parseNode(n);
|
|
861
|
+
nodesSet.set(String(parsed.id), parsed);
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
return {
|
|
866
|
+
success: true,
|
|
867
|
+
executionTime: Date.now() - startTime,
|
|
868
|
+
paths: options.returnPaths ? paths : undefined,
|
|
869
|
+
nodes: options.returnNodes ? Array.from(nodesSet.values()) : undefined,
|
|
870
|
+
relationships: Array.from(relsSet.values()),
|
|
871
|
+
count: paths.length,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
async shortestPath(connection, options, transaction) {
|
|
875
|
+
const startTime = Date.now();
|
|
876
|
+
const params = {};
|
|
877
|
+
const startNodeMatch = this.buildNodeSelectorMatch(options.startNode, 'start', params);
|
|
878
|
+
const endNodeMatch = this.buildNodeSelectorMatch(options.endNode, 'end', params, 'end_');
|
|
879
|
+
const relPattern = this.buildTraversalRelPattern(options);
|
|
880
|
+
const maxDepth = options.maxDepth || '';
|
|
881
|
+
const depthPattern = maxDepth ? `*..${maxDepth}` : '*';
|
|
882
|
+
const dirStart = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
883
|
+
const dirEnd = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
884
|
+
options.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
885
|
+
let query;
|
|
886
|
+
if (options.weightProperty && options.algorithm === path_types_1.PathAlgorithm.DIJKSTRA) {
|
|
887
|
+
// Use APOC for weighted shortest path
|
|
888
|
+
query = `
|
|
889
|
+
MATCH ${startNodeMatch}, ${endNodeMatch}
|
|
890
|
+
CALL apoc.algo.dijkstra(start, end, '${relPattern}', '${options.weightProperty}')
|
|
891
|
+
YIELD path, weight
|
|
892
|
+
RETURN path, weight
|
|
893
|
+
`;
|
|
894
|
+
}
|
|
895
|
+
else if (options.returnAllShortest) {
|
|
896
|
+
query = `
|
|
897
|
+
MATCH ${startNodeMatch}, ${endNodeMatch}
|
|
898
|
+
MATCH path = allShortestPaths((start)${dirStart}[${relPattern}${depthPattern}]${dirEnd}(end))
|
|
899
|
+
RETURN path
|
|
900
|
+
`;
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
query = `
|
|
904
|
+
MATCH ${startNodeMatch}, ${endNodeMatch}
|
|
905
|
+
MATCH path = shortestPath((start)${dirStart}[${relPattern}${depthPattern}]${dirEnd}(end))
|
|
906
|
+
RETURN path
|
|
907
|
+
`;
|
|
908
|
+
}
|
|
909
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
910
|
+
if (result.data.length === 0) {
|
|
911
|
+
return {
|
|
912
|
+
success: true,
|
|
913
|
+
executionTime: Date.now() - startTime,
|
|
914
|
+
found: false,
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
const paths = result.data.map(row => {
|
|
918
|
+
const parsedPath = this.parsePath(row.path);
|
|
919
|
+
if (row.weight !== undefined) {
|
|
920
|
+
return Object.assign(Object.assign({}, parsedPath), { totalWeight: row.weight, weights: [] });
|
|
921
|
+
}
|
|
922
|
+
return parsedPath;
|
|
923
|
+
});
|
|
924
|
+
return {
|
|
925
|
+
success: true,
|
|
926
|
+
executionTime: Date.now() - startTime,
|
|
927
|
+
path: options.returnAllShortest ? undefined : paths[0],
|
|
928
|
+
paths: options.returnAllShortest ? paths : undefined,
|
|
929
|
+
found: true,
|
|
930
|
+
length: paths[0].length,
|
|
931
|
+
totalWeight: paths[0].totalWeight,
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
async allPaths(connection, options, transaction) {
|
|
935
|
+
const startTime = Date.now();
|
|
936
|
+
const params = {};
|
|
937
|
+
const startNodeMatch = this.buildNodeSelectorMatch(options.startNode, 'start', params);
|
|
938
|
+
const endNodeMatch = this.buildNodeSelectorMatch(options.endNode, 'end', params, 'end_');
|
|
939
|
+
const relPattern = this.buildTraversalRelPattern(options);
|
|
940
|
+
const minDepth = options.minDepth || 1;
|
|
941
|
+
const maxDepth = options.maxDepth || 10;
|
|
942
|
+
const depthPattern = `*${minDepth}..${maxDepth}`;
|
|
943
|
+
const dirStart = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
944
|
+
const dirEnd = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
945
|
+
options.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
946
|
+
let query = `
|
|
947
|
+
MATCH ${startNodeMatch}, ${endNodeMatch}
|
|
948
|
+
MATCH path = (start)${dirStart}[${relPattern}${depthPattern}]${dirEnd}(end)
|
|
949
|
+
RETURN path
|
|
950
|
+
`;
|
|
951
|
+
if (options.limit) {
|
|
952
|
+
query += ` LIMIT ${options.limit}`;
|
|
953
|
+
}
|
|
954
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
955
|
+
const paths = result.data.map(row => this.parsePath(row.path));
|
|
956
|
+
return {
|
|
957
|
+
success: true,
|
|
958
|
+
executionTime: Date.now() - startTime,
|
|
959
|
+
paths,
|
|
960
|
+
count: paths.length,
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
async matchPattern(connection, options, transaction) {
|
|
964
|
+
const startTime = Date.now();
|
|
965
|
+
const params = {};
|
|
966
|
+
const { pattern } = options;
|
|
967
|
+
// Build node patterns
|
|
968
|
+
const nodePatterns = [];
|
|
969
|
+
pattern.nodes.forEach(node => {
|
|
970
|
+
const labels = node.labels
|
|
971
|
+
? ':' + (Array.isArray(node.labels) ? node.labels : [node.labels])
|
|
972
|
+
.map(l => this.escapeIdentifier(l)).join(':')
|
|
973
|
+
: '';
|
|
974
|
+
let props = '';
|
|
975
|
+
if (node.properties && Object.keys(node.properties).length > 0) {
|
|
976
|
+
const { clause, params: nodeParams } = this.buildPropertiesClause(node.properties, `${node.variable}_`);
|
|
977
|
+
props = ` ${clause}`;
|
|
978
|
+
Object.assign(params, nodeParams);
|
|
979
|
+
}
|
|
980
|
+
nodePatterns.push(`(${node.variable}${labels}${props})`);
|
|
981
|
+
});
|
|
982
|
+
// Build relationship patterns
|
|
983
|
+
const matchClauses = [];
|
|
984
|
+
if (pattern.relationships.length === 0) {
|
|
985
|
+
// Just match nodes
|
|
986
|
+
matchClauses.push(`MATCH ${nodePatterns.join(', ')}`);
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
pattern.relationships.forEach(rel => {
|
|
990
|
+
const startVar = rel.startNode;
|
|
991
|
+
const endVar = rel.endNode;
|
|
992
|
+
const relVar = rel.variable || '';
|
|
993
|
+
const types = rel.type
|
|
994
|
+
? ':' + (Array.isArray(rel.type) ? rel.type : [rel.type])
|
|
995
|
+
.map(t => this.escapeIdentifier(t)).join('|')
|
|
996
|
+
: '';
|
|
997
|
+
const dirStart = rel.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
998
|
+
const dirEnd = rel.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
999
|
+
rel.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
1000
|
+
let hops = '';
|
|
1001
|
+
if (rel.minHops !== undefined || rel.maxHops !== undefined) {
|
|
1002
|
+
const min = rel.minHops || '';
|
|
1003
|
+
const max = rel.maxHops || '';
|
|
1004
|
+
hops = `*${min}..${max}`;
|
|
1005
|
+
}
|
|
1006
|
+
matchClauses.push(`MATCH (${startVar})${dirStart}[${relVar}${types}${hops}]${dirEnd}(${endVar})`);
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
let query = matchClauses.join(' ');
|
|
1010
|
+
// WHERE clause
|
|
1011
|
+
if (pattern.where) {
|
|
1012
|
+
const conditions = pattern.where.conditions
|
|
1013
|
+
.map(c => c.expression)
|
|
1014
|
+
.join(pattern.where.operator === 'OR' ? ' OR ' : ' AND ');
|
|
1015
|
+
query += ` WHERE ${conditions}`;
|
|
1016
|
+
}
|
|
1017
|
+
// RETURN clause
|
|
1018
|
+
const returnVars = options.return || pattern.nodes.map(n => n.variable);
|
|
1019
|
+
query += ` RETURN ${returnVars.join(', ')}`;
|
|
1020
|
+
// ORDER BY
|
|
1021
|
+
if (options.orderBy && options.orderBy.length > 0) {
|
|
1022
|
+
const orderParts = options.orderBy.map(o => `${o.expression} ${o.direction}`);
|
|
1023
|
+
query += ` ORDER BY ${orderParts.join(', ')}`;
|
|
1024
|
+
}
|
|
1025
|
+
// SKIP and LIMIT
|
|
1026
|
+
if (options.skip !== undefined) {
|
|
1027
|
+
query += ` SKIP ${options.skip}`;
|
|
1028
|
+
}
|
|
1029
|
+
if (options.limit !== undefined) {
|
|
1030
|
+
query += ` LIMIT ${options.limit}`;
|
|
1031
|
+
}
|
|
1032
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1033
|
+
return {
|
|
1034
|
+
success: true,
|
|
1035
|
+
executionTime: Date.now() - startTime,
|
|
1036
|
+
matches: result.data,
|
|
1037
|
+
count: result.data.length,
|
|
1038
|
+
hasMore: options.limit !== undefined && result.data.length === options.limit,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
async extractSubgraph(connection, options, transaction) {
|
|
1042
|
+
const startTime = Date.now();
|
|
1043
|
+
const params = {};
|
|
1044
|
+
const startNodes = Array.isArray(options.startNodes) ? options.startNodes : [options.startNodes];
|
|
1045
|
+
const startMatches = startNodes.map((node, idx) => this.buildNodeSelectorMatch(node, `start${idx}`, params, `start${idx}_`));
|
|
1046
|
+
const maxDepth = options.maxDepth || 3;
|
|
1047
|
+
const relPattern = this.buildTraversalRelPattern(options);
|
|
1048
|
+
const dirStart = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
1049
|
+
const dirEnd = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
1050
|
+
options.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
1051
|
+
const startVars = startNodes.map((_, idx) => `start${idx}`).join(', ');
|
|
1052
|
+
const matchPaths = startNodes.map((_, idx) => `(start${idx})${dirStart}[${relPattern}*0..${maxDepth}]${dirEnd}(n)`).join(' OR ');
|
|
1053
|
+
let query = `
|
|
1054
|
+
MATCH ${startMatches.join(', ')}
|
|
1055
|
+
WITH ${startVars}
|
|
1056
|
+
MATCH path = ${matchPaths.length > 0 ? matchPaths.replace(' OR ', '), (') : `(start0)${dirStart}[${relPattern}*0..${maxDepth}]${dirEnd}(n)`}
|
|
1057
|
+
WITH COLLECT(DISTINCT n) AS nodes, COLLECT(DISTINCT relationships(path)) AS rels
|
|
1058
|
+
UNWIND rels AS relList
|
|
1059
|
+
UNWIND relList AS r
|
|
1060
|
+
RETURN nodes, COLLECT(DISTINCT r) AS relationships
|
|
1061
|
+
`;
|
|
1062
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1063
|
+
const nodes = [];
|
|
1064
|
+
const relationships = [];
|
|
1065
|
+
if (result.data.length > 0) {
|
|
1066
|
+
const row = result.data[0];
|
|
1067
|
+
if (row.nodes) {
|
|
1068
|
+
row.nodes.forEach((n) => nodes.push(this.parseNode(n)));
|
|
1069
|
+
}
|
|
1070
|
+
if (row.relationships) {
|
|
1071
|
+
row.relationships.forEach((r) => relationships.push(this.parseRelationship(r)));
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
success: true,
|
|
1076
|
+
executionTime: Date.now() - startTime,
|
|
1077
|
+
subgraph: {
|
|
1078
|
+
nodes,
|
|
1079
|
+
relationships,
|
|
1080
|
+
nodeCount: nodes.length,
|
|
1081
|
+
relationshipCount: relationships.length,
|
|
1082
|
+
},
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
async getNeighborhood(connection, options, transaction) {
|
|
1086
|
+
const startTime = Date.now();
|
|
1087
|
+
const params = {};
|
|
1088
|
+
const centerMatch = this.buildNodeSelectorMatch(options.node, 'center', params);
|
|
1089
|
+
const depth = options.depth || 1;
|
|
1090
|
+
let relTypes = '';
|
|
1091
|
+
if (options.relationshipTypes && options.relationshipTypes.length > 0) {
|
|
1092
|
+
relTypes = ':' + options.relationshipTypes.map(t => this.escapeIdentifier(t)).join('|');
|
|
1093
|
+
}
|
|
1094
|
+
const dirStart = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '<-' : '-';
|
|
1095
|
+
const dirEnd = options.direction === relationship_types_1.RelationshipDirection.INCOMING ? '-' :
|
|
1096
|
+
options.direction === relationship_types_1.RelationshipDirection.OUTGOING ? '->' : '-';
|
|
1097
|
+
const query = `
|
|
1098
|
+
MATCH ${centerMatch}
|
|
1099
|
+
CALL apoc.path.subgraphAll(center, {
|
|
1100
|
+
relationshipFilter: '${relTypes.replace(':', '')}',
|
|
1101
|
+
minLevel: ${options.includeCenter ? 0 : 1},
|
|
1102
|
+
maxLevel: ${depth}
|
|
1103
|
+
})
|
|
1104
|
+
YIELD nodes, relationships
|
|
1105
|
+
RETURN nodes, relationships
|
|
1106
|
+
`;
|
|
1107
|
+
// Fallback query without APOC
|
|
1108
|
+
const fallbackQuery = `
|
|
1109
|
+
MATCH ${centerMatch}
|
|
1110
|
+
MATCH path = (center)${dirStart}[${relTypes}*0..${depth}]${dirEnd}(n)
|
|
1111
|
+
WITH COLLECT(DISTINCT n) AS nodes, COLLECT(DISTINCT relationships(path)) AS rels
|
|
1112
|
+
UNWIND rels AS relList
|
|
1113
|
+
UNWIND relList AS r
|
|
1114
|
+
RETURN nodes, COLLECT(DISTINCT r) AS relationships
|
|
1115
|
+
`;
|
|
1116
|
+
let result;
|
|
1117
|
+
try {
|
|
1118
|
+
result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1119
|
+
}
|
|
1120
|
+
catch (_a) {
|
|
1121
|
+
// Fall back to non-APOC query
|
|
1122
|
+
result = await this.executeRaw(connection, { query: fallbackQuery, params }, transaction);
|
|
1123
|
+
}
|
|
1124
|
+
const nodes = [];
|
|
1125
|
+
const relationships = [];
|
|
1126
|
+
const levels = new Map();
|
|
1127
|
+
if (result.data.length > 0) {
|
|
1128
|
+
const row = result.data[0];
|
|
1129
|
+
if (row.nodes) {
|
|
1130
|
+
row.nodes.forEach((n) => {
|
|
1131
|
+
const parsed = this.parseNode(n);
|
|
1132
|
+
nodes.push(parsed);
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
if (row.relationships) {
|
|
1136
|
+
row.relationships.forEach((r) => relationships.push(this.parseRelationship(r)));
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
success: true,
|
|
1141
|
+
executionTime: Date.now() - startTime,
|
|
1142
|
+
nodes,
|
|
1143
|
+
relationships,
|
|
1144
|
+
levels,
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
async findConnectedComponents(connection, options, transaction) {
|
|
1148
|
+
const startTime = Date.now();
|
|
1149
|
+
// Build label filter
|
|
1150
|
+
let labelFilter = '';
|
|
1151
|
+
if (options.labels && options.labels.length > 0) {
|
|
1152
|
+
labelFilter = ':' + options.labels.map(l => this.escapeIdentifier(l)).join(':');
|
|
1153
|
+
}
|
|
1154
|
+
// Build relationship type filter
|
|
1155
|
+
let relFilter = '';
|
|
1156
|
+
if (options.relationshipTypes && options.relationshipTypes.length > 0) {
|
|
1157
|
+
relFilter = ':' + options.relationshipTypes.map(t => this.escapeIdentifier(t)).join('|');
|
|
1158
|
+
}
|
|
1159
|
+
// Use GDS (Graph Data Science) library for connected components
|
|
1160
|
+
const query = `
|
|
1161
|
+
CALL gds.wcc.stream({
|
|
1162
|
+
nodeQuery: 'MATCH (n${labelFilter}) RETURN id(n) AS id',
|
|
1163
|
+
relationshipQuery: 'MATCH (n${labelFilter})-[r${relFilter}]-(m${labelFilter}) RETURN id(n) AS source, id(m) AS target'
|
|
1164
|
+
})
|
|
1165
|
+
YIELD nodeId, componentId
|
|
1166
|
+
WITH componentId, COLLECT(nodeId) AS nodeIds
|
|
1167
|
+
${options.minSize ? `WHERE size(nodeIds) >= ${options.minSize}` : ''}
|
|
1168
|
+
RETURN componentId, nodeIds
|
|
1169
|
+
ORDER BY size(nodeIds) DESC
|
|
1170
|
+
`;
|
|
1171
|
+
// Fallback query without GDS
|
|
1172
|
+
const fallbackQuery = `
|
|
1173
|
+
MATCH (n${labelFilter})
|
|
1174
|
+
WITH COLLECT(n) AS allNodes
|
|
1175
|
+
UNWIND allNodes AS node
|
|
1176
|
+
MATCH path = (node)-[${relFilter}*]-(connected)
|
|
1177
|
+
WITH node, COLLECT(DISTINCT connected) + node AS component
|
|
1178
|
+
WITH COLLECT(DISTINCT component) AS allComponents
|
|
1179
|
+
UNWIND range(0, size(allComponents)-1) AS idx
|
|
1180
|
+
WITH idx AS componentId, allComponents[idx] AS component
|
|
1181
|
+
${options.minSize ? `WHERE size(component) >= ${options.minSize}` : ''}
|
|
1182
|
+
RETURN componentId, component AS nodes
|
|
1183
|
+
`;
|
|
1184
|
+
let result;
|
|
1185
|
+
try {
|
|
1186
|
+
result = await this.executeRaw(connection, { query }, transaction);
|
|
1187
|
+
}
|
|
1188
|
+
catch (_a) {
|
|
1189
|
+
result = await this.executeRaw(connection, { query: fallbackQuery }, transaction);
|
|
1190
|
+
}
|
|
1191
|
+
const components = result.data.map((row, idx) => ({
|
|
1192
|
+
id: row.componentId || idx,
|
|
1193
|
+
nodes: (row.nodes || []).map((n) => this.parseNode(n)),
|
|
1194
|
+
size: row.nodeIds ? row.nodeIds.length : (row.nodes || []).length,
|
|
1195
|
+
}));
|
|
1196
|
+
return {
|
|
1197
|
+
success: true,
|
|
1198
|
+
executionTime: Date.now() - startTime,
|
|
1199
|
+
components,
|
|
1200
|
+
count: components.length,
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
// ==================== AGGREGATION OPERATIONS ====================
|
|
1204
|
+
async countNodes(connection, labels, where, transaction) {
|
|
1205
|
+
var _a;
|
|
1206
|
+
const startTime = Date.now();
|
|
1207
|
+
const params = {};
|
|
1208
|
+
let labelStr = '';
|
|
1209
|
+
if (labels) {
|
|
1210
|
+
const labelArr = Array.isArray(labels) ? labels : [labels];
|
|
1211
|
+
labelStr = ':' + labelArr.map(l => this.escapeIdentifier(l)).join(':');
|
|
1212
|
+
}
|
|
1213
|
+
let query = `MATCH (n${labelStr})`;
|
|
1214
|
+
if (where && Object.keys(where).length > 0) {
|
|
1215
|
+
const whereParts = [];
|
|
1216
|
+
Object.entries(where).forEach(([key, value], index) => {
|
|
1217
|
+
const paramName = `where_${index}`;
|
|
1218
|
+
whereParts.push(`n.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
1219
|
+
params[paramName] = value;
|
|
1220
|
+
});
|
|
1221
|
+
query += ` WHERE ${whereParts.join(' AND ')}`;
|
|
1222
|
+
}
|
|
1223
|
+
query += ' RETURN count(n) AS count';
|
|
1224
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1225
|
+
return {
|
|
1226
|
+
success: true,
|
|
1227
|
+
executionTime: Date.now() - startTime,
|
|
1228
|
+
count: this.toNumber(((_a = result.data[0]) === null || _a === void 0 ? void 0 : _a.count) || 0),
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
async countRelationships(connection, type, where, transaction) {
|
|
1232
|
+
var _a;
|
|
1233
|
+
const startTime = Date.now();
|
|
1234
|
+
const params = {};
|
|
1235
|
+
let typeStr = '';
|
|
1236
|
+
if (type) {
|
|
1237
|
+
const typeArr = Array.isArray(type) ? type : [type];
|
|
1238
|
+
typeStr = ':' + typeArr.map(t => this.escapeIdentifier(t)).join('|');
|
|
1239
|
+
}
|
|
1240
|
+
let query = `MATCH ()-[r${typeStr}]->()`;
|
|
1241
|
+
if (where && Object.keys(where).length > 0) {
|
|
1242
|
+
const whereParts = [];
|
|
1243
|
+
Object.entries(where).forEach(([key, value], index) => {
|
|
1244
|
+
const paramName = `where_${index}`;
|
|
1245
|
+
whereParts.push(`r.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
1246
|
+
params[paramName] = value;
|
|
1247
|
+
});
|
|
1248
|
+
query += ` WHERE ${whereParts.join(' AND ')}`;
|
|
1249
|
+
}
|
|
1250
|
+
query += ' RETURN count(r) AS count';
|
|
1251
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1252
|
+
return {
|
|
1253
|
+
success: true,
|
|
1254
|
+
executionTime: Date.now() - startTime,
|
|
1255
|
+
count: this.toNumber(((_a = result.data[0]) === null || _a === void 0 ? void 0 : _a.count) || 0),
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
async getStatistics(connection, transaction) {
|
|
1259
|
+
const startTime = Date.now();
|
|
1260
|
+
const query = `
|
|
1261
|
+
CALL apoc.meta.stats() YIELD labels, relTypesCount, nodeCount, relCount
|
|
1262
|
+
RETURN labels, relTypesCount, nodeCount, relCount
|
|
1263
|
+
`;
|
|
1264
|
+
// Fallback without APOC
|
|
1265
|
+
const fallbackQuery = `
|
|
1266
|
+
MATCH (n)
|
|
1267
|
+
WITH count(n) AS nodeCount
|
|
1268
|
+
MATCH ()-[r]->()
|
|
1269
|
+
WITH nodeCount, count(r) AS relCount
|
|
1270
|
+
RETURN nodeCount, relCount
|
|
1271
|
+
`;
|
|
1272
|
+
let result;
|
|
1273
|
+
try {
|
|
1274
|
+
result = await this.executeRaw(connection, { query }, transaction);
|
|
1275
|
+
}
|
|
1276
|
+
catch (_a) {
|
|
1277
|
+
result = await this.executeRaw(connection, { query: fallbackQuery }, transaction);
|
|
1278
|
+
}
|
|
1279
|
+
const row = result.data[0] || {};
|
|
1280
|
+
return {
|
|
1281
|
+
success: true,
|
|
1282
|
+
executionTime: Date.now() - startTime,
|
|
1283
|
+
nodeCount: this.toNumber(row.nodeCount || 0),
|
|
1284
|
+
relationshipCount: this.toNumber(row.relCount || 0),
|
|
1285
|
+
labelCounts: row.labels || {},
|
|
1286
|
+
relationshipTypeCounts: row.relTypesCount || {},
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
// ==================== SEARCH OPERATIONS ====================
|
|
1290
|
+
async fullTextSearch(connection, options, transaction) {
|
|
1291
|
+
const startTime = Date.now();
|
|
1292
|
+
let query = `
|
|
1293
|
+
CALL db.index.fulltext.queryNodes($indexName, $searchQuery)
|
|
1294
|
+
YIELD node, score
|
|
1295
|
+
`;
|
|
1296
|
+
if (options.labels && options.labels.length > 0) {
|
|
1297
|
+
const labelConditions = options.labels.map(l => `'${l}' IN labels(node)`).join(' OR ');
|
|
1298
|
+
query += ` WHERE ${labelConditions}`;
|
|
1299
|
+
}
|
|
1300
|
+
query += ' RETURN node';
|
|
1301
|
+
if (options.score) {
|
|
1302
|
+
query += ', score';
|
|
1303
|
+
}
|
|
1304
|
+
if (options.limit) {
|
|
1305
|
+
query += ` LIMIT ${options.limit}`;
|
|
1306
|
+
}
|
|
1307
|
+
const params = {
|
|
1308
|
+
indexName: options.index,
|
|
1309
|
+
searchQuery: options.query,
|
|
1310
|
+
};
|
|
1311
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1312
|
+
const nodes = result.data.map(row => {
|
|
1313
|
+
const node = this.parseNode(row.node);
|
|
1314
|
+
if (options.score && row.score !== undefined) {
|
|
1315
|
+
return Object.assign(Object.assign({}, node), { score: row.score });
|
|
1316
|
+
}
|
|
1317
|
+
return node;
|
|
1318
|
+
});
|
|
1319
|
+
return {
|
|
1320
|
+
success: true,
|
|
1321
|
+
executionTime: Date.now() - startTime,
|
|
1322
|
+
nodes,
|
|
1323
|
+
count: nodes.length,
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
async vectorSearch(connection, options, transaction) {
|
|
1327
|
+
const startTime = Date.now();
|
|
1328
|
+
const query = `
|
|
1329
|
+
CALL db.index.vector.queryNodes($indexName, $k, $vector)
|
|
1330
|
+
YIELD node, score
|
|
1331
|
+
${options.minScore ? `WHERE score >= ${options.minScore}` : ''}
|
|
1332
|
+
RETURN node, score
|
|
1333
|
+
`;
|
|
1334
|
+
const params = {
|
|
1335
|
+
indexName: options.index,
|
|
1336
|
+
k: options.topK,
|
|
1337
|
+
vector: options.vector,
|
|
1338
|
+
};
|
|
1339
|
+
const result = await this.executeRaw(connection, { query, params }, transaction);
|
|
1340
|
+
const nodes = result.data.map(row => (Object.assign(Object.assign({}, this.parseNode(row.node)), { similarity: row.score })));
|
|
1341
|
+
return {
|
|
1342
|
+
success: true,
|
|
1343
|
+
executionTime: Date.now() - startTime,
|
|
1344
|
+
nodes,
|
|
1345
|
+
count: nodes.length,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
// ==================== SCHEMA OPERATIONS ====================
|
|
1349
|
+
async createNodeConstraint(connection, constraint, transaction) {
|
|
1350
|
+
const startTime = Date.now();
|
|
1351
|
+
let query;
|
|
1352
|
+
const props = constraint.properties.map(p => `n.${this.escapeIdentifier(p)}`).join(', ');
|
|
1353
|
+
switch (constraint.type) {
|
|
1354
|
+
case node_types_1.NodeConstraintType.UNIQUE:
|
|
1355
|
+
query = `CREATE CONSTRAINT ${this.escapeIdentifier(constraint.name)} FOR (n:${this.escapeIdentifier(constraint.label)}) REQUIRE (${props}) IS UNIQUE`;
|
|
1356
|
+
break;
|
|
1357
|
+
case node_types_1.NodeConstraintType.EXISTS:
|
|
1358
|
+
query = `CREATE CONSTRAINT ${this.escapeIdentifier(constraint.name)} FOR (n:${this.escapeIdentifier(constraint.label)}) REQUIRE ${props} IS NOT NULL`;
|
|
1359
|
+
break;
|
|
1360
|
+
case node_types_1.NodeConstraintType.NODE_KEY:
|
|
1361
|
+
query = `CREATE CONSTRAINT ${this.escapeIdentifier(constraint.name)} FOR (n:${this.escapeIdentifier(constraint.label)}) REQUIRE (${props}) IS NODE KEY`;
|
|
1362
|
+
break;
|
|
1363
|
+
default:
|
|
1364
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.VALIDATION_ERROR, `Unknown constraint type: ${constraint.type}`);
|
|
1365
|
+
}
|
|
1366
|
+
await this.executeRaw(connection, { query }, transaction);
|
|
1367
|
+
return {
|
|
1368
|
+
success: true,
|
|
1369
|
+
executionTime: Date.now() - startTime,
|
|
1370
|
+
name: constraint.name,
|
|
1371
|
+
created: true,
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
async createRelationshipConstraint(connection, constraint, transaction) {
|
|
1375
|
+
const startTime = Date.now();
|
|
1376
|
+
const props = constraint.properties.map(p => `r.${this.escapeIdentifier(p)}`).join(', ');
|
|
1377
|
+
const query = `CREATE CONSTRAINT ${this.escapeIdentifier(constraint.name)} FOR ()-[r:${this.escapeIdentifier(constraint.relationshipType)}]-() REQUIRE ${props} IS NOT NULL`;
|
|
1378
|
+
await this.executeRaw(connection, { query }, transaction);
|
|
1379
|
+
return {
|
|
1380
|
+
success: true,
|
|
1381
|
+
executionTime: Date.now() - startTime,
|
|
1382
|
+
name: constraint.name,
|
|
1383
|
+
created: true,
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
async dropConstraint(connection, name, transaction) {
|
|
1387
|
+
const startTime = Date.now();
|
|
1388
|
+
const query = `DROP CONSTRAINT ${this.escapeIdentifier(name)}`;
|
|
1389
|
+
await this.executeRaw(connection, { query }, transaction);
|
|
1390
|
+
return {
|
|
1391
|
+
success: true,
|
|
1392
|
+
executionTime: Date.now() - startTime,
|
|
1393
|
+
name,
|
|
1394
|
+
dropped: true,
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
async listConstraints(connection, transaction) {
|
|
1398
|
+
const startTime = Date.now();
|
|
1399
|
+
const query = 'SHOW CONSTRAINTS';
|
|
1400
|
+
const result = await this.executeRaw(connection, { query }, transaction);
|
|
1401
|
+
const constraints = result.data.map(row => ({
|
|
1402
|
+
name: row.name,
|
|
1403
|
+
type: row.type,
|
|
1404
|
+
entityType: row.entityType,
|
|
1405
|
+
labelsOrTypes: row.labelsOrTypes || [],
|
|
1406
|
+
properties: row.properties || [],
|
|
1407
|
+
ownedIndex: row.ownedIndex,
|
|
1408
|
+
}));
|
|
1409
|
+
return {
|
|
1410
|
+
success: true,
|
|
1411
|
+
executionTime: Date.now() - startTime,
|
|
1412
|
+
constraints,
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
async createNodeIndex(connection, index, transaction) {
|
|
1416
|
+
const startTime = Date.now();
|
|
1417
|
+
const props = index.properties.map(p => `n.${this.escapeIdentifier(p)}`).join(', ');
|
|
1418
|
+
let query;
|
|
1419
|
+
switch (index.type) {
|
|
1420
|
+
case node_types_1.NodeIndexType.BTREE:
|
|
1421
|
+
case node_types_1.NodeIndexType.RANGE:
|
|
1422
|
+
query = `CREATE INDEX ${this.escapeIdentifier(index.name)} FOR (n:${this.escapeIdentifier(index.label)}) ON (${props})`;
|
|
1423
|
+
break;
|
|
1424
|
+
case node_types_1.NodeIndexType.FULLTEXT:
|
|
1425
|
+
query = `CREATE FULLTEXT INDEX ${this.escapeIdentifier(index.name)} FOR (n:${this.escapeIdentifier(index.label)}) ON EACH [${props}]`;
|
|
1426
|
+
break;
|
|
1427
|
+
case node_types_1.NodeIndexType.POINT:
|
|
1428
|
+
query = `CREATE POINT INDEX ${this.escapeIdentifier(index.name)} FOR (n:${this.escapeIdentifier(index.label)}) ON (${props})`;
|
|
1429
|
+
break;
|
|
1430
|
+
case node_types_1.NodeIndexType.TEXT:
|
|
1431
|
+
query = `CREATE TEXT INDEX ${this.escapeIdentifier(index.name)} FOR (n:${this.escapeIdentifier(index.label)}) ON (${props})`;
|
|
1432
|
+
break;
|
|
1433
|
+
case node_types_1.NodeIndexType.VECTOR:
|
|
1434
|
+
const vectorOptions = index.options || {};
|
|
1435
|
+
query = `CREATE VECTOR INDEX ${this.escapeIdentifier(index.name)} FOR (n:${this.escapeIdentifier(index.label)}) ON (${props}) OPTIONS {indexConfig: {vector.dimensions: ${vectorOptions.dimensions || 256}, vector.similarity_function: '${vectorOptions.similarityFunction || 'cosine'}'}}`;
|
|
1436
|
+
break;
|
|
1437
|
+
default:
|
|
1438
|
+
query = `CREATE INDEX ${this.escapeIdentifier(index.name)} FOR (n:${this.escapeIdentifier(index.label)}) ON (${props})`;
|
|
1439
|
+
}
|
|
1440
|
+
await this.executeRaw(connection, { query }, transaction);
|
|
1441
|
+
return {
|
|
1442
|
+
success: true,
|
|
1443
|
+
executionTime: Date.now() - startTime,
|
|
1444
|
+
name: index.name,
|
|
1445
|
+
created: true,
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
async createRelationshipIndex(connection, index, transaction) {
|
|
1449
|
+
const startTime = Date.now();
|
|
1450
|
+
const props = index.properties.map(p => `r.${this.escapeIdentifier(p)}`).join(', ');
|
|
1451
|
+
const query = `CREATE INDEX ${this.escapeIdentifier(index.name)} FOR ()-[r:${this.escapeIdentifier(index.relationshipType)}]-() ON (${props})`;
|
|
1452
|
+
await this.executeRaw(connection, { query }, transaction);
|
|
1453
|
+
return {
|
|
1454
|
+
success: true,
|
|
1455
|
+
executionTime: Date.now() - startTime,
|
|
1456
|
+
name: index.name,
|
|
1457
|
+
created: true,
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
async dropIndex(connection, name, transaction) {
|
|
1461
|
+
const startTime = Date.now();
|
|
1462
|
+
const query = `DROP INDEX ${this.escapeIdentifier(name)}`;
|
|
1463
|
+
await this.executeRaw(connection, { query }, transaction);
|
|
1464
|
+
return {
|
|
1465
|
+
success: true,
|
|
1466
|
+
executionTime: Date.now() - startTime,
|
|
1467
|
+
name,
|
|
1468
|
+
dropped: true,
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
async listIndexes(connection, transaction) {
|
|
1472
|
+
const startTime = Date.now();
|
|
1473
|
+
const query = 'SHOW INDEXES';
|
|
1474
|
+
const result = await this.executeRaw(connection, { query }, transaction);
|
|
1475
|
+
const indexes = result.data.map(row => ({
|
|
1476
|
+
name: row.name,
|
|
1477
|
+
type: row.type,
|
|
1478
|
+
entityType: row.entityType,
|
|
1479
|
+
labelsOrTypes: row.labelsOrTypes || [],
|
|
1480
|
+
properties: row.properties || [],
|
|
1481
|
+
state: row.state,
|
|
1482
|
+
uniqueness: row.uniqueness,
|
|
1483
|
+
provider: row.provider,
|
|
1484
|
+
}));
|
|
1485
|
+
return {
|
|
1486
|
+
success: true,
|
|
1487
|
+
executionTime: Date.now() - startTime,
|
|
1488
|
+
indexes,
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
// ==================== UTILITY METHODS ====================
|
|
1492
|
+
escapeIdentifier(identifier) {
|
|
1493
|
+
// Neo4j identifiers can be backtick-escaped
|
|
1494
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier)) {
|
|
1495
|
+
return identifier;
|
|
1496
|
+
}
|
|
1497
|
+
return `\`${identifier.replace(/`/g, '``')}\``;
|
|
1498
|
+
}
|
|
1499
|
+
escapeValue(value) {
|
|
1500
|
+
if (value === null)
|
|
1501
|
+
return 'null';
|
|
1502
|
+
if (typeof value === 'string')
|
|
1503
|
+
return `'${value.replace(/'/g, "\\'")}'`;
|
|
1504
|
+
if (typeof value === 'number')
|
|
1505
|
+
return String(value);
|
|
1506
|
+
if (typeof value === 'boolean')
|
|
1507
|
+
return value ? 'true' : 'false';
|
|
1508
|
+
if (value instanceof Date)
|
|
1509
|
+
return `datetime('${value.toISOString()}')`;
|
|
1510
|
+
if (Array.isArray(value))
|
|
1511
|
+
return `[${value.map(v => this.escapeValue(v)).join(', ')}]`;
|
|
1512
|
+
if (typeof value === 'object')
|
|
1513
|
+
return `{${Object.entries(value).map(([k, v]) => `${k}: ${this.escapeValue(v)}`).join(', ')}}`;
|
|
1514
|
+
return String(value);
|
|
1515
|
+
}
|
|
1516
|
+
buildWhereClause(where, variableName = 'n') {
|
|
1517
|
+
const params = {};
|
|
1518
|
+
const parts = [];
|
|
1519
|
+
Object.entries(where).forEach(([key, value], index) => {
|
|
1520
|
+
const paramName = `where_${index}`;
|
|
1521
|
+
parts.push(`${variableName}.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
1522
|
+
params[paramName] = value;
|
|
1523
|
+
});
|
|
1524
|
+
return {
|
|
1525
|
+
clause: parts.join(' AND '),
|
|
1526
|
+
params,
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
// ==================== PROTECTED PARSER METHODS ====================
|
|
1530
|
+
parseNode(nativeNode) {
|
|
1531
|
+
if (!nativeNode) {
|
|
1532
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.NODE_ERROR, 'Cannot parse null node');
|
|
1533
|
+
}
|
|
1534
|
+
// Handle Neo4j Node object
|
|
1535
|
+
if (nativeNode.identity !== undefined) {
|
|
1536
|
+
return {
|
|
1537
|
+
id: this.toNumber(nativeNode.identity),
|
|
1538
|
+
labels: nativeNode.labels || [],
|
|
1539
|
+
properties: this.convertProperties(nativeNode.properties),
|
|
1540
|
+
elementId: nativeNode.elementId,
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
// Handle plain object (from raw query results)
|
|
1544
|
+
return {
|
|
1545
|
+
id: nativeNode.id || nativeNode.identity || '',
|
|
1546
|
+
labels: nativeNode.labels || [],
|
|
1547
|
+
properties: (nativeNode.properties || nativeNode),
|
|
1548
|
+
elementId: nativeNode.elementId,
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
parseRelationship(nativeRelationship) {
|
|
1552
|
+
if (!nativeRelationship) {
|
|
1553
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.RELATIONSHIP_ERROR, 'Cannot parse null relationship');
|
|
1554
|
+
}
|
|
1555
|
+
// Handle Neo4j Relationship object
|
|
1556
|
+
if (nativeRelationship.identity !== undefined) {
|
|
1557
|
+
return {
|
|
1558
|
+
id: this.toNumber(nativeRelationship.identity),
|
|
1559
|
+
type: nativeRelationship.type,
|
|
1560
|
+
startNodeId: this.toNumber(nativeRelationship.start),
|
|
1561
|
+
endNodeId: this.toNumber(nativeRelationship.end),
|
|
1562
|
+
properties: this.convertProperties(nativeRelationship.properties),
|
|
1563
|
+
elementId: nativeRelationship.elementId,
|
|
1564
|
+
startNodeElementId: nativeRelationship.startNodeElementId,
|
|
1565
|
+
endNodeElementId: nativeRelationship.endNodeElementId,
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
return {
|
|
1569
|
+
id: nativeRelationship.id || nativeRelationship.identity || '',
|
|
1570
|
+
type: nativeRelationship.type || '',
|
|
1571
|
+
startNodeId: nativeRelationship.startNodeId || nativeRelationship.start || '',
|
|
1572
|
+
endNodeId: nativeRelationship.endNodeId || nativeRelationship.end || '',
|
|
1573
|
+
properties: (nativeRelationship.properties || nativeRelationship),
|
|
1574
|
+
elementId: nativeRelationship.elementId,
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
parsePath(nativePath) {
|
|
1578
|
+
if (!nativePath) {
|
|
1579
|
+
throw new graph_types_1.GraphError(graph_types_1.GraphErrorType.PATH_ERROR, 'Cannot parse null path');
|
|
1580
|
+
}
|
|
1581
|
+
// Handle Neo4j Path object
|
|
1582
|
+
const nodes = [];
|
|
1583
|
+
const relationships = [];
|
|
1584
|
+
if (nativePath.segments) {
|
|
1585
|
+
nativePath.segments.forEach((segment) => {
|
|
1586
|
+
if (nodes.length === 0) {
|
|
1587
|
+
nodes.push(this.parseNode(segment.start));
|
|
1588
|
+
}
|
|
1589
|
+
relationships.push(this.parseRelationship(segment.relationship));
|
|
1590
|
+
nodes.push(this.parseNode(segment.end));
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
else if (nativePath.nodes && nativePath.relationships) {
|
|
1594
|
+
nativePath.nodes.forEach((n) => nodes.push(this.parseNode(n)));
|
|
1595
|
+
nativePath.relationships.forEach((r) => relationships.push(this.parseRelationship(r)));
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
nodes,
|
|
1599
|
+
relationships,
|
|
1600
|
+
length: relationships.length,
|
|
1601
|
+
start: nodes[0],
|
|
1602
|
+
end: nodes[nodes.length - 1],
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
// ==================== PRIVATE HELPER METHODS ====================
|
|
1606
|
+
convertNeo4jValue(value) {
|
|
1607
|
+
if (value === null || value === undefined)
|
|
1608
|
+
return value;
|
|
1609
|
+
// Handle Neo4j Integer
|
|
1610
|
+
if (neo4j_driver_1.default.isInt(value)) {
|
|
1611
|
+
return this.toNumber(value);
|
|
1612
|
+
}
|
|
1613
|
+
// Handle Neo4j Node
|
|
1614
|
+
if (value.labels !== undefined && value.properties !== undefined) {
|
|
1615
|
+
return this.parseNode(value);
|
|
1616
|
+
}
|
|
1617
|
+
// Handle Neo4j Relationship
|
|
1618
|
+
if (value.type !== undefined && value.start !== undefined && value.end !== undefined) {
|
|
1619
|
+
return this.parseRelationship(value);
|
|
1620
|
+
}
|
|
1621
|
+
// Handle Neo4j Path
|
|
1622
|
+
if (value.segments !== undefined) {
|
|
1623
|
+
return this.parsePath(value);
|
|
1624
|
+
}
|
|
1625
|
+
// Handle arrays
|
|
1626
|
+
if (Array.isArray(value)) {
|
|
1627
|
+
return value.map(v => this.convertNeo4jValue(v));
|
|
1628
|
+
}
|
|
1629
|
+
// Handle objects
|
|
1630
|
+
if (typeof value === 'object') {
|
|
1631
|
+
const converted = {};
|
|
1632
|
+
Object.entries(value).forEach(([key, val]) => {
|
|
1633
|
+
converted[key] = this.convertNeo4jValue(val);
|
|
1634
|
+
});
|
|
1635
|
+
return converted;
|
|
1636
|
+
}
|
|
1637
|
+
return value;
|
|
1638
|
+
}
|
|
1639
|
+
convertProperties(properties) {
|
|
1640
|
+
if (!properties)
|
|
1641
|
+
return {};
|
|
1642
|
+
const converted = {};
|
|
1643
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
1644
|
+
converted[key] = this.convertNeo4jValue(value);
|
|
1645
|
+
});
|
|
1646
|
+
return converted;
|
|
1647
|
+
}
|
|
1648
|
+
toNumber(value) {
|
|
1649
|
+
if (neo4j_driver_1.default.isInt(value)) {
|
|
1650
|
+
return value.toNumber();
|
|
1651
|
+
}
|
|
1652
|
+
if (typeof value === 'number') {
|
|
1653
|
+
return value;
|
|
1654
|
+
}
|
|
1655
|
+
return parseInt(value, 10) || 0;
|
|
1656
|
+
}
|
|
1657
|
+
normalizeLabels(labels) {
|
|
1658
|
+
return Array.isArray(labels) ? labels : [labels];
|
|
1659
|
+
}
|
|
1660
|
+
buildPropertiesClause(properties, prefix = '') {
|
|
1661
|
+
const params = {};
|
|
1662
|
+
const propParts = [];
|
|
1663
|
+
Object.entries(properties).forEach(([key, value], index) => {
|
|
1664
|
+
const paramName = `${prefix}prop_${index}`;
|
|
1665
|
+
propParts.push(`${this.escapeIdentifier(key)}: $${paramName}`);
|
|
1666
|
+
params[paramName] = value;
|
|
1667
|
+
});
|
|
1668
|
+
return {
|
|
1669
|
+
clause: `{${propParts.join(', ')}}`,
|
|
1670
|
+
params,
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
buildNodeReference(ref, alias, params, prefix = '') {
|
|
1674
|
+
let pattern = `(${alias}`;
|
|
1675
|
+
if (ref.labels) {
|
|
1676
|
+
const labels = this.normalizeLabels(ref.labels);
|
|
1677
|
+
pattern += ':' + labels.map(l => this.escapeIdentifier(l)).join(':');
|
|
1678
|
+
}
|
|
1679
|
+
if (ref.id !== undefined) {
|
|
1680
|
+
const paramName = `${prefix}id`;
|
|
1681
|
+
params[paramName] = ref.id;
|
|
1682
|
+
pattern += ` {id: $${paramName}}`;
|
|
1683
|
+
}
|
|
1684
|
+
else if (ref.properties && Object.keys(ref.properties).length > 0) {
|
|
1685
|
+
const { clause, params: propParams } = this.buildPropertiesClause(ref.properties, prefix);
|
|
1686
|
+
pattern += ` ${clause}`;
|
|
1687
|
+
Object.assign(params, propParams);
|
|
1688
|
+
}
|
|
1689
|
+
pattern += ')';
|
|
1690
|
+
return pattern;
|
|
1691
|
+
}
|
|
1692
|
+
buildNodeReferenceWhere(ref, alias, params, prefix = '') {
|
|
1693
|
+
const parts = [];
|
|
1694
|
+
if (ref.id !== undefined) {
|
|
1695
|
+
const paramName = `${prefix}id`;
|
|
1696
|
+
params[paramName] = ref.id;
|
|
1697
|
+
if (typeof ref.id === 'string') {
|
|
1698
|
+
parts.push(`elementId(${alias}) = $${paramName}`);
|
|
1699
|
+
}
|
|
1700
|
+
else {
|
|
1701
|
+
parts.push(`id(${alias}) = $${paramName}`);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (ref.labels) {
|
|
1705
|
+
const labels = this.normalizeLabels(ref.labels);
|
|
1706
|
+
labels.forEach(label => {
|
|
1707
|
+
parts.push(`'${label}' IN labels(${alias})`);
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
if (ref.properties) {
|
|
1711
|
+
Object.entries(ref.properties).forEach(([key, value], index) => {
|
|
1712
|
+
const paramName = `${prefix}prop_${index}`;
|
|
1713
|
+
parts.push(`${alias}.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
1714
|
+
params[paramName] = value;
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
return parts.join(' AND ');
|
|
1718
|
+
}
|
|
1719
|
+
buildWhereFromClause(where, alias, prefix = '') {
|
|
1720
|
+
const params = {};
|
|
1721
|
+
const clause = this.buildWhereCondition(where, alias, params, prefix);
|
|
1722
|
+
return { clause, params };
|
|
1723
|
+
}
|
|
1724
|
+
buildWhereCondition(where, alias, params, prefix, index = 0) {
|
|
1725
|
+
const paramName = `${prefix}where_${index}`;
|
|
1726
|
+
let condition;
|
|
1727
|
+
switch (where.operator) {
|
|
1728
|
+
case node_types_1.NodeComparisonOperator.IS_NULL:
|
|
1729
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} IS NULL`;
|
|
1730
|
+
break;
|
|
1731
|
+
case node_types_1.NodeComparisonOperator.IS_NOT_NULL:
|
|
1732
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} IS NOT NULL`;
|
|
1733
|
+
break;
|
|
1734
|
+
case node_types_1.NodeComparisonOperator.IN:
|
|
1735
|
+
case node_types_1.NodeComparisonOperator.NOT_IN:
|
|
1736
|
+
params[paramName] = where.value;
|
|
1737
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} ${where.operator} $${paramName}`;
|
|
1738
|
+
break;
|
|
1739
|
+
case node_types_1.NodeComparisonOperator.CONTAINS:
|
|
1740
|
+
params[paramName] = where.value;
|
|
1741
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} CONTAINS $${paramName}`;
|
|
1742
|
+
break;
|
|
1743
|
+
case node_types_1.NodeComparisonOperator.STARTS_WITH:
|
|
1744
|
+
params[paramName] = where.value;
|
|
1745
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} STARTS WITH $${paramName}`;
|
|
1746
|
+
break;
|
|
1747
|
+
case node_types_1.NodeComparisonOperator.ENDS_WITH:
|
|
1748
|
+
params[paramName] = where.value;
|
|
1749
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} ENDS WITH $${paramName}`;
|
|
1750
|
+
break;
|
|
1751
|
+
case node_types_1.NodeComparisonOperator.REGEX:
|
|
1752
|
+
params[paramName] = where.value;
|
|
1753
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} =~ $${paramName}`;
|
|
1754
|
+
break;
|
|
1755
|
+
default:
|
|
1756
|
+
params[paramName] = where.value;
|
|
1757
|
+
condition = `${alias}.${this.escapeIdentifier(where.property)} ${where.operator} $${paramName}`;
|
|
1758
|
+
}
|
|
1759
|
+
// Handle AND conditions
|
|
1760
|
+
if (where.and && where.and.length > 0) {
|
|
1761
|
+
const andClauses = where.and.map((w, i) => this.buildWhereCondition(w, alias, params, prefix, index + i + 1));
|
|
1762
|
+
condition = `(${condition} AND ${andClauses.join(' AND ')})`;
|
|
1763
|
+
}
|
|
1764
|
+
// Handle OR conditions
|
|
1765
|
+
if (where.or && where.or.length > 0) {
|
|
1766
|
+
const orClauses = where.or.map((w, i) => this.buildWhereCondition(w, alias, params, prefix, index + i + 100));
|
|
1767
|
+
condition = `(${condition} OR ${orClauses.join(' OR ')})`;
|
|
1768
|
+
}
|
|
1769
|
+
return condition;
|
|
1770
|
+
}
|
|
1771
|
+
buildRelationshipWhereClause(where, alias, prefix = '') {
|
|
1772
|
+
const params = {};
|
|
1773
|
+
const clause = this.buildRelationshipWhereCondition(where, alias, params, prefix);
|
|
1774
|
+
return { clause, params };
|
|
1775
|
+
}
|
|
1776
|
+
buildRelationshipWhereCondition(where, alias, params, prefix, index = 0) {
|
|
1777
|
+
const paramName = `${prefix}relwhere_${index}`;
|
|
1778
|
+
params[paramName] = where.value;
|
|
1779
|
+
let condition = `${alias}.${this.escapeIdentifier(where.property)} ${where.operator} $${paramName}`;
|
|
1780
|
+
if (where.and && where.and.length > 0) {
|
|
1781
|
+
const andClauses = where.and.map((w, i) => this.buildRelationshipWhereCondition(w, alias, params, prefix, index + i + 1));
|
|
1782
|
+
condition = `(${condition} AND ${andClauses.join(' AND ')})`;
|
|
1783
|
+
}
|
|
1784
|
+
if (where.or && where.or.length > 0) {
|
|
1785
|
+
const orClauses = where.or.map((w, i) => this.buildRelationshipWhereCondition(w, alias, params, prefix, index + i + 100));
|
|
1786
|
+
condition = `(${condition} OR ${orClauses.join(' OR ')})`;
|
|
1787
|
+
}
|
|
1788
|
+
return condition;
|
|
1789
|
+
}
|
|
1790
|
+
buildNodeSelectorMatch(selector, alias, params, prefix = '') {
|
|
1791
|
+
let pattern = `(${alias}`;
|
|
1792
|
+
if (selector.labels) {
|
|
1793
|
+
const labels = this.normalizeLabels(selector.labels);
|
|
1794
|
+
pattern += ':' + labels.map((l) => this.escapeIdentifier(l)).join(':');
|
|
1795
|
+
}
|
|
1796
|
+
pattern += ')';
|
|
1797
|
+
const whereParts = [];
|
|
1798
|
+
if (selector.id !== undefined) {
|
|
1799
|
+
const paramName = `${prefix}id`;
|
|
1800
|
+
params[paramName] = selector.id;
|
|
1801
|
+
if (typeof selector.id === 'string') {
|
|
1802
|
+
whereParts.push(`elementId(${alias}) = $${paramName}`);
|
|
1803
|
+
}
|
|
1804
|
+
else {
|
|
1805
|
+
whereParts.push(`id(${alias}) = $${paramName}`);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
if (selector.properties && Object.keys(selector.properties).length > 0) {
|
|
1809
|
+
Object.entries(selector.properties).forEach(([key, value], index) => {
|
|
1810
|
+
const paramName = `${prefix}prop_${index}`;
|
|
1811
|
+
whereParts.push(`${alias}.${this.escapeIdentifier(key)} = $${paramName}`);
|
|
1812
|
+
params[paramName] = value;
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
if (selector.where) {
|
|
1816
|
+
const { clause, params: whereParams } = this.buildWhereFromClause(selector.where, alias, prefix);
|
|
1817
|
+
whereParts.push(clause);
|
|
1818
|
+
Object.assign(params, whereParams);
|
|
1819
|
+
}
|
|
1820
|
+
if (whereParts.length > 0) {
|
|
1821
|
+
return `${pattern} WHERE ${whereParts.join(' AND ')}`;
|
|
1822
|
+
}
|
|
1823
|
+
return pattern;
|
|
1824
|
+
}
|
|
1825
|
+
buildTraversalRelPattern(options) {
|
|
1826
|
+
if (!options.relationships)
|
|
1827
|
+
return '';
|
|
1828
|
+
const patterns = Array.isArray(options.relationships)
|
|
1829
|
+
? options.relationships
|
|
1830
|
+
: [options.relationships];
|
|
1831
|
+
const types = patterns
|
|
1832
|
+
.filter((p) => p.type)
|
|
1833
|
+
.map((p) => {
|
|
1834
|
+
const types = Array.isArray(p.type) ? p.type : [p.type];
|
|
1835
|
+
return types.map((t) => this.escapeIdentifier(t));
|
|
1836
|
+
})
|
|
1837
|
+
.flat();
|
|
1838
|
+
return types.length > 0 ? ':' + types.join('|') : '';
|
|
1839
|
+
}
|
|
1840
|
+
parseStatistics(counters) {
|
|
1841
|
+
if (!counters)
|
|
1842
|
+
return undefined;
|
|
1843
|
+
return {
|
|
1844
|
+
nodesCreated: counters.nodesCreated ? counters.nodesCreated() : 0,
|
|
1845
|
+
nodesDeleted: counters.nodesDeleted ? counters.nodesDeleted() : 0,
|
|
1846
|
+
relationshipsCreated: counters.relationshipsCreated ? counters.relationshipsCreated() : 0,
|
|
1847
|
+
relationshipsDeleted: counters.relationshipsDeleted ? counters.relationshipsDeleted() : 0,
|
|
1848
|
+
propertiesSet: counters.propertiesSet ? counters.propertiesSet() : 0,
|
|
1849
|
+
labelsAdded: counters.labelsAdded ? counters.labelsAdded() : 0,
|
|
1850
|
+
labelsRemoved: counters.labelsRemoved ? counters.labelsRemoved() : 0,
|
|
1851
|
+
indexesAdded: counters.indexesAdded ? counters.indexesAdded() : 0,
|
|
1852
|
+
indexesRemoved: counters.indexesRemoved ? counters.indexesRemoved() : 0,
|
|
1853
|
+
constraintsAdded: counters.constraintsAdded ? counters.constraintsAdded() : 0,
|
|
1854
|
+
constraintsRemoved: counters.constraintsRemoved ? counters.constraintsRemoved() : 0,
|
|
1855
|
+
containsUpdates: counters.containsUpdates ? counters.containsUpdates() : false,
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
exports.Neo4jAdapter = Neo4jAdapter;
|
|
1860
|
+
exports.default = Neo4jAdapter;
|
|
1861
|
+
//# sourceMappingURL=neo4j.adapter.js.map
|