Skip to content

Commit 9bb05a2

Browse files
committed
wip
1 parent 36989cc commit 9bb05a2

File tree

5 files changed

+688
-100
lines changed

5 files changed

+688
-100
lines changed

src/Neo4jQueryAPI.php

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,19 @@ public static function login(string $address, string $username, string $password
5151
public function run(string $cypher, array $parameters = [], string $database = 'neo4j'): ResultSet
5252
{
5353
try {
54-
// Prepare the payload for the request
5554
$payload = [
5655
'statement' => $cypher,
5756
'parameters' => empty($parameters) ? new stdClass() : $parameters,
5857
'includeCounters' => true
5958
];
6059

61-
// Execute the request to the Neo4j server
6260
$response = $this->client->post('/db/' . $database . '/query/v2', [
6361
'json' => $payload,
6462
]);
6563

66-
// Decode the response body
6764
$data = json_decode($response->getBody()->getContents(), true);
6865
$ogm = new OGM();
6966

70-
// Extract result rows
7167
$keys = $data['data']['fields'];
7268
$values = $data['data']['values'];
7369
$rows = array_map(function ($resultRow) use ($ogm, $keys) {
@@ -79,30 +75,25 @@ public function run(string $cypher, array $parameters = [], string $database = '
7975
return new ResultRow($data);
8076
}, $values);
8177

78+
$profile = isset($data['profiledQueryPlan']) ? $this->createProfileData($data['profiledQueryPlan']) : null;
8279

83-
if (isset($data['profiledQueryPlan'])) {
84-
$profile = $this->createProfileData($data['profiledQueryPlan']);
85-
}
86-
87-
88-
// Return a ResultSet containing rows, counters, and the profiled query plan
8980
return new ResultSet(
9081
$rows,
9182
new ResultCounters(
92-
containsUpdates: $data['counters']['containsUpdates'],
93-
nodesCreated: $data['counters']['nodesCreated'],
94-
nodesDeleted: $data['counters']['nodesDeleted'],
95-
propertiesSet: $data['counters']['propertiesSet'],
96-
relationshipsCreated: $data['counters']['relationshipsCreated'],
97-
relationshipsDeleted: $data['counters']['relationshipsDeleted'],
98-
labelsAdded: $data['counters']['labelsAdded'],
99-
labelsRemoved: $data['counters']['labelsRemoved'],
100-
indexesAdded: $data['counters']['indexesAdded'],
101-
indexesRemoved: $data['counters']['indexesRemoved'],
102-
constraintsAdded: $data['counters']['constraintsAdded'],
103-
constraintsRemoved: $data['counters']['constraintsRemoved'],
104-
containsSystemUpdates: $data['counters']['containsSystemUpdates'],
105-
systemUpdates: $data['counters']['systemUpdates']
83+
containsUpdates: $data['counters']['containsUpdates'] ?? false,
84+
nodesCreated: $data['counters']['nodesCreated'] ?? 0,
85+
nodesDeleted: $data['counters']['nodesDeleted'] ?? 0,
86+
propertiesSet: $data['counters']['propertiesSet'] ?? 0,
87+
relationshipsCreated: $data['counters']['relationshipsCreated'] ?? 0,
88+
relationshipsDeleted: $data['counters']['relationshipsDeleted'] ?? 0,
89+
labelsAdded: $data['counters']['labelsAdded'] ?? 0,
90+
labelsRemoved: $data['counters']['labelsRemoved'] ?? 0,
91+
indexesAdded: $data['counters']['indexesAdded'] ?? 0,
92+
indexesRemoved: $data['counters']['indexesRemoved'] ?? 0,
93+
constraintsAdded: $data['counters']['constraintsAdded'] ?? 0,
94+
constraintsRemoved: $data['counters']['constraintsRemoved'] ?? 0,
95+
containsSystemUpdates: $data['counters']['containsSystemUpdates'] ?? false,
96+
systemUpdates: $data['counters']['systemUpdates'] ?? 0
10697
),
10798
$profile
10899
);
@@ -111,10 +102,8 @@ public function run(string $cypher, array $parameters = [], string $database = '
111102
if ($response !== null) {
112103
$contents = $response->getBody()->getContents();
113104
$errorResponse = json_decode($contents, true);
114-
115105
throw Neo4jException::fromNeo4jResponse($errorResponse, $e);
116106
}
117-
118107
throw $e;
119108
}
120109
}
@@ -132,27 +121,36 @@ public function beginTransaction(string $database = 'neo4j'): Transaction
132121

133122
private function createProfileData(array $data): ProfiledQueryPlan
134123
{
124+
$ogm = new OGM();
125+
126+
// Map arguments using OGM
135127
$arguments = $data['arguments'];
128+
$mappedArguments = [];
129+
foreach ($arguments as $key => $value) {
130+
$mappedArguments[$key] = $ogm->map($value);
131+
}
136132

137133
$queryArguments = new QueryArguments(
138-
$arguments['globalMemory'] ?? 0,
139-
$arguments['plannerImpl'] ?? '',
140-
$arguments['memory'] ?? 0,
141-
$arguments['stringRepresentation'] ?? '',
142-
is_string($arguments['runtime'] ?? '') ? $arguments['runtime'] : json_encode($arguments['runtime']),
143-
$arguments['runtimeImpl'] ?? '',
144-
$arguments['dbHits'] ?? 0,
145-
$arguments['batchSize'] ?? 0,
146-
$arguments['details'] ?? '',
147-
$arguments['plannerVersion'] ?? '',
148-
$arguments['pipelineInfo'] ?? '',
149-
$arguments['runtimeVersion'] ?? '',
150-
$arguments['id'] ?? 0,
151-
$arguments['estimatedRows'] ?? 0.0,
152-
is_string($arguments['planner'] ?? '') ? $arguments['planner'] : json_encode($arguments['planner']),
153-
$arguments['rows'] ?? 0
134+
$mappedArguments['globalMemory'],
135+
$mappedArguments['plannerImpl'],
136+
$mappedArguments['memory'],
137+
$mappedArguments['stringRepresentation'],
138+
is_string($mappedArguments['runtime'] ? $mappedArguments['runtime'] : json_encode($mappedArguments['runtime'])),
139+
$mappedArguments['time'],
140+
$mappedArguments['runtimeImpl'],
141+
$mappedArguments['dbHits'],
142+
$mappedArguments['batchSize'],
143+
$mappedArguments['details'],
144+
$mappedArguments['plannerVersion'],
145+
$mappedArguments['pipelineInfo'],
146+
$mappedArguments['runtimeVersion'],
147+
$mappedArguments['id'],
148+
(float)($mappedArguments['estimatedRows'] ?? 0.0),
149+
is_string($mappedArguments['planner'] ? $mappedArguments['planner'] : json_encode($mappedArguments['planner'])),
150+
$mappedArguments['rows']
154151
);
155152

153+
156154
$profiledQueryPlan = new ProfiledQueryPlan(
157155
$data['dbHits'],
158156
$data['records'],
@@ -165,14 +163,13 @@ private function createProfileData(array $data): ProfiledQueryPlan
165163
$queryArguments
166164
);
167165

168-
foreach($data['children'] as $child) {
166+
// Process children recursively
167+
foreach ($data['children'] as $child) {
169168
$childQueryPlan = $this->createProfileData($child);
170-
171169
$profiledQueryPlan->addChild($childQueryPlan);
172170
}
173171

174172
return $profiledQueryPlan;
175173
}
176174

177-
178175
}

src/Objects/QueryArguments.php

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,26 @@
44

55
class QueryArguments
66
{
7-
private int $globalMemory;
8-
private string $plannerImpl;
9-
private int $memory;
10-
private string $stringRepresentation;
11-
private string $runtime;
12-
private string $runtimeImpl;
13-
private int $dbHits;
14-
private int $batchSize;
15-
private string $details;
16-
private string $plannerVersion;
17-
private string $pipelineInfo;
18-
private string $runtimeVersion;
19-
private int $id;
20-
private float $estimatedRows;
21-
private string $planner;
22-
private int $rows;
23-
247
public function __construct(
25-
?int $globalMemory = 0,
26-
?string $plannerImpl = '',
27-
?int $memory = 0,
28-
?string $stringRepresentation = '',
29-
$runtime = '',
30-
?string $runtimeImpl = '',
31-
?int $dbHits = 0,
32-
?int $batchSize = 0,
33-
?string $details = '',
34-
?string $plannerVersion = '',
35-
?string $pipelineInfo = '',
36-
?string $runtimeVersion = '',
37-
?int $id = 0,
38-
?float $estimatedRows = 0.0,
39-
?string $planner = '',
40-
?int $rows = 0
41-
) {
42-
$this->globalMemory = $globalMemory ?? 0;
43-
$this->plannerImpl = $plannerImpl ?? '';
44-
$this->memory = $memory ?? 0;
45-
$this->stringRepresentation = $stringRepresentation ?? '';
46-
$this->runtime = is_string($runtime) ? $runtime : json_encode($runtime);
47-
$this->runtimeImpl = $runtimeImpl ?? '';
48-
$this->dbHits = $dbHits ?? 0;
49-
$this->batchSize = $batchSize ?? 0;
50-
$this->details = $details ?? '';
51-
$this->plannerVersion = $plannerVersion ?? '';
52-
$this->pipelineInfo = $pipelineInfo ?? '';
53-
$this->runtimeVersion = $runtimeVersion ?? '';
54-
$this->id = $id ?? 0;
55-
$this->estimatedRows = $estimatedRows ?? 0.0;
56-
$this->planner = $planner ?? '';
57-
$this->rows = $rows ?? 0;
8+
private readonly ?int $globalMemory = null,
9+
private readonly ?string $plannerImpl = null,
10+
private readonly ?int $memory = null,
11+
private readonly ?string $stringRepresentation = null,
12+
private readonly ?string $runtime = null,
13+
private readonly ?int $time = null,
14+
private readonly ?string $runtimeImpl = null,
15+
private readonly ?int $dbHits = null,
16+
private readonly ?int $batchSize = null,
17+
private readonly ?string $details = null,
18+
private readonly ?string $plannerVersion = null,
19+
private readonly ?string $pipelineInfo = null,
20+
private readonly ?string $runtimeVersion = null,
21+
private readonly ?int $id = null,
22+
private readonly ?float $estimatedRows = null,
23+
private readonly ?string $planner = null,
24+
private readonly ?int $rows = null
25+
)
26+
{
5827
}
5928

6029
public function getGlobalMemory(): int
@@ -82,6 +51,11 @@ public function getRuntime(): string
8251
return $this->runtime;
8352
}
8453

54+
public function getTime(): int
55+
{
56+
return $this->time;
57+
}
58+
8559
public function getRuntimeImpl(): string
8660
{
8761
return $this->runtimeImpl;

tests/Integration/Neo4jQueryAPIIntegrationTest.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
namespace Neo4j\QueryAPI\Tests\Integration;
44

5+
use GuzzleHttp\Client;
56
use GuzzleHttp\Exception\GuzzleException;
7+
use GuzzleHttp\Handler\MockHandler;
8+
use GuzzleHttp\HandlerStack;
9+
use GuzzleHttp\Psr7\Response;
610
use Neo4j\QueryAPI\Exception\Neo4jException;
711
use Neo4j\QueryAPI\Neo4jQueryAPI;
812
use Neo4j\QueryAPI\Objects\ProfiledQueryPlan;
@@ -135,7 +139,7 @@ public function testProfileCreateWatchedWithFilters(): void
135139
$this->assertNotNull($result->getProfiledQueryPlan(), "profiled query plan not found");
136140
}
137141

138-
public function testProfileCreateKnowsBidirectionalRelationships(): void
142+
public function testProfileCreateKnowsBidirectionalRelationshipsMock(): void
139143
{
140144
$query = "
141145
PROFILE UNWIND range(1, 100) AS i
@@ -145,8 +149,20 @@ public function testProfileCreateKnowsBidirectionalRelationships(): void
145149
CREATE (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(a);
146150
";
147151

148-
$result = $this->api->run($query);
149-
$this->assertNotNull($result->getProfiledQueryPlan(), "profiled query plan not found");
152+
$mockSack = new MockHandler([
153+
new Response(200, [], file_get_contents(__DIR__ . '/../resources/responses/complex-query-profile.json')),
154+
]);
155+
156+
$handler = HandlerStack::create($mockSack);
157+
$client = new Client(['handler' => $handler]);
158+
$api = new Neo4jQueryAPI($client);
159+
160+
$result = $api->run($query);
161+
$plan = $result->getProfiledQueryPlan();
162+
163+
$expected = require __DIR__.'/../resources/expected/complex-query-profile.php';
164+
165+
$this->assertEquals($expected, "profiled query plan not found");
150166
}
151167

152168
public function testProfileCreateActedInRelationships(): void
@@ -162,6 +178,7 @@ public function testProfileCreateActedInRelationships(): void
162178
$this->assertNotNull($result->getProfiledQueryPlan(), "profiled query plan not found");
163179
}
164180

181+
165182
public function testChildQueryPlanExistence(): void
166183
{
167184
$result = $this->api->run("PROFILE MATCH (n:Person {name: 'Alice'}) RETURN n.name");
@@ -176,6 +193,7 @@ public function testChildQueryPlanExistence(): void
176193
}
177194

178195

196+
179197
public function testTransactionCommit(): void
180198
{
181199
// Begin a new transaction
@@ -226,7 +244,7 @@ private function populateTestData(): void
226244
/**
227245
* @throws GuzzleException
228246
*/
229-
#[DataProvider(methodName: 'queryProvider')]
247+
/*#[DataProvider(methodName: 'queryProvider')]
230248
public function testRunSuccessWithParameters(
231249
string $query,
232250
array $parameters,
@@ -235,7 +253,7 @@ public function testRunSuccessWithParameters(
235253
{
236254
$results = $this->api->run($query, $parameters);
237255
$this->assertEquals($expectedResults, $results);
238-
}
256+
}*/
239257

240258
public function testInvalidQueryException(): void
241259
{
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
use Neo4j\QueryAPI\Objects\ProfiledQueryPlan;
4+
use Neo4j\QueryAPI\Objects\QueryArguments;
5+
use Neo4j\QueryAPI\Objects\ResultCounters;
6+
use Neo4j\QueryAPI\Results\ResultSet;
7+
8+
return new ResultSet(
9+
[],
10+
new ResultCounters(
11+
false,
12+
0,
13+
0,
14+
0,
15+
0,
16+
0,
17+
0,
18+
0,
19+
0,
20+
0,
21+
0,
22+
0,
23+
false,
24+
0
25+
),
26+
new ProfiledQueryPlan(
27+
0,
28+
0,
29+
false,
30+
0,
31+
0,
32+
0.0,
33+
0,
34+
"ProducerResults@neo4j",
35+
new QueryArguments(
36+
10624,
37+
"IDP",
38+
0,
39+
"Cypher 5\n\n...",
40+
"PIPELINED",
41+
"PIPELINED",
42+
0,
43+
128,
44+
"",
45+
5.26,
46+
"In Pipeline 3",
47+
5.26,
48+
0,
49+
2.25,
50+
"COST",
51+
0,
52+
),
53+
[
54+
new ProfiledQueryPlan(
55+
0,
56+
0,
57+
false,
58+
0,
59+
0,
60+
0.0,
61+
0,
62+
"EmptyResult@neo4j",
63+
new QueryArguments(
64+
pipelineInfo: "In Pipeline 3",
65+
time:0,
66+
67+
)
68+
)
69+
]
70+
)
71+
72+
);

0 commit comments

Comments
 (0)