Skip to content

Commit 5657f19

Browse files
committed
Query Profile test
1 parent d5585ac commit 5657f19

File tree

5 files changed

+98
-48
lines changed

5 files changed

+98
-48
lines changed

src/Neo4jQueryAPI.php

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use GuzzleHttp\Client;
77
use GuzzleHttp\Exception\GuzzleException;
88
use GuzzleHttp\Exception\RequestException;
9+
use Neo4j\QueryAPI\Objects\ChildQueryPlan;
910
use Neo4j\QueryAPI\Objects\QueryArguments;
1011
use Neo4j\QueryAPI\Objects\ResultCounters;
1112
use Neo4j\QueryAPI\Objects\ProfiledQueryPlan;
@@ -16,6 +17,9 @@
1617
use RuntimeException;
1718
use stdClass;
1819

20+
/**
21+
* @method parseChildren(mixed $children)
22+
*/
1923
class Neo4jQueryAPI
2024
{
2125
private Client $client;
@@ -75,43 +79,12 @@ public function run(string $cypher, array $parameters = [], string $database = '
7579
return new ResultRow($data);
7680
}, $values);
7781

78-
// Extract profile data, if available
79-
$profiledQueryPlan = null;
82+
8083
if (isset($data['profiledQueryPlan'])) {
81-
$profiledQueryPlan = new ProfiledQueryPlan(
82-
$data['profile']['dbHits'],
83-
$data['profile']['records'],
84-
$data['profile']['hasPageCacheStats'],
85-
$data['profile']['pageCacheHits'],
86-
$data['profile']['pageCacheMisses'],
87-
$data['profile']['pageCacheHitRatio'],
88-
$data['profile']['time'],
89-
$data['profile']['operatorType'],
90-
$data['profile']['arguments']
91-
);
92-
}
93-
$queryArguments = null;
94-
if (isset($data['profiledQueryPlan']['arguments'])) {
95-
$queryArguments = new QueryArguments(
96-
$data['profile']['globalMemory'] ?? 0,
97-
$data['profile']['plannerImpl'] ?? '',
98-
$data['profile']['memory'] ?? 0,
99-
$data['profile']['stringRepresentation'] ?? '',
100-
$data['profile']['runtime'] ?? '',
101-
$data['profile']['runtimeImpl'] ?? '',
102-
$data['profile']['dbHits'] ?? 0,
103-
$data['profile']['batchSize'] ?? 0,
104-
$data['profile']['details'] ?? '',
105-
$data['profile']['plannerVersion'] ?? '',
106-
$data['profile']['pipelineInfo'] ?? '',
107-
$data['profile']['runtimeVersion'] ?? '',
108-
$data['profile']['id'] ?? 0,
109-
$data['profile']['estimatedRows'] ?? 0.0,
110-
$data['profile']['planner'] ?? '',
111-
$data['profile']['rows'] ?? 0
112-
);
84+
$profile = $this->createProfileData($data['profiledQueryPlan']);
11385
}
11486

87+
11588
// Return a ResultSet containing rows, counters, and the profiled query plan
11689
return new ResultSet(
11790
$rows,
@@ -131,7 +104,7 @@ public function run(string $cypher, array $parameters = [], string $database = '
131104
containsSystemUpdates: $data['counters']['containsSystemUpdates'],
132105
systemUpdates: $data['counters']['systemUpdates']
133106
),
134-
$profiledQueryPlan // Pass the profiled query plan here
107+
$profile
135108
);
136109
} catch (RequestExceptionInterface $e) {
137110
$response = $e->getResponse();
@@ -156,4 +129,50 @@ public function beginTransaction(string $database = 'neo4j'): Transaction
156129

157130
return new Transaction($this->client, $clusterAffinity, $transactionId);
158131
}
132+
133+
private function createProfileData(array $data): ProfiledQueryPlan
134+
{
135+
$arguments = $data['arguments'];
136+
137+
$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
154+
);
155+
156+
$profiledQueryPlan = new ProfiledQueryPlan(
157+
$data['dbHits'],
158+
$data['records'],
159+
$data['hasPageCacheStats'],
160+
$data['pageCacheHits'],
161+
$data['pageCacheMisses'],
162+
$data['pageCacheHitRatio'],
163+
$data['time'],
164+
$data['operatorType'],
165+
$queryArguments
166+
);
167+
168+
foreach($data['children'] as $child) {
169+
$childQueryPlan = $this->createProfileData($child);
170+
171+
$profiledQueryPlan->addChild($childQueryPlan);
172+
}
173+
174+
return $profiledQueryPlan;
175+
}
176+
177+
159178
}

src/Objects/ProfiledQueryPlan.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ class ProfiledQueryPlan
1212
private float $pageCacheHitRatio;
1313
private int $time;
1414
private string $operatorType;
15-
private array $arguments;
15+
private QueryArguments $arguments;
16+
17+
/**
18+
* @var list<ProfiledQueryPlan>
19+
*/
20+
private array $children;
1621

1722
public function __construct(
1823
?int $dbHits = 0, // Default to 0 if null
@@ -23,7 +28,7 @@ public function __construct(
2328
?float $pageCacheHitRatio = 0.0,
2429
?int $time = 0,
2530
?string $operatorType = '',
26-
?array $arguments = []
31+
QueryArguments $arguments
2732
) {
2833
$this->dbHits = $dbHits ?? 0;
2934
$this->records = $records ?? 0;
@@ -33,7 +38,7 @@ public function __construct(
3338
$this->pageCacheHitRatio = $pageCacheHitRatio ?? 0.0;
3439
$this->time = $time ?? 0;
3540
$this->operatorType = $operatorType ?? '';
36-
$this->arguments = $arguments ?? [];
41+
$this->arguments = $arguments;
3742
}
3843

3944
public function getDbHits(): int
@@ -76,8 +81,21 @@ public function getOperatorType(): string
7681
return $this->operatorType;
7782
}
7883

79-
public function getArguments(): array
84+
public function getArguments(): QueryArguments
8085
{
8186
return $this->arguments;
8287
}
88+
89+
/**
90+
* @return list<ProfiledQueryPlan>
91+
*/
92+
public function getChildren(): array
93+
{
94+
return $this->children;
95+
}
96+
97+
public function addChild(ProfiledQueryPlan $child): void
98+
{
99+
$this->children[] = $child;
100+
}
83101
}

src/Objects/QueryArguments.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function __construct(
2626
?string $plannerImpl = '',
2727
?int $memory = 0,
2828
?string $stringRepresentation = '',
29-
?string $runtime = '',
29+
$runtime = '',
3030
?string $runtimeImpl = '',
3131
?int $dbHits = 0,
3232
?int $batchSize = 0,
@@ -43,7 +43,7 @@ public function __construct(
4343
$this->plannerImpl = $plannerImpl ?? '';
4444
$this->memory = $memory ?? 0;
4545
$this->stringRepresentation = $stringRepresentation ?? '';
46-
$this->runtime = $runtime ?? '';
46+
$this->runtime = is_string($runtime) ? $runtime : json_encode($runtime);
4747
$this->runtimeImpl = $runtimeImpl ?? '';
4848
$this->dbHits = $dbHits ?? 0;
4949
$this->batchSize = $batchSize ?? 0;

src/Results/ResultSet.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use ArrayIterator;
55
use Countable;
66
use IteratorAggregate;
7+
use Neo4j\QueryAPI\Objects\ChildQueryPlan;
78
use Neo4j\QueryAPI\Objects\ProfiledQueryPlan;
89
use Neo4j\QueryAPI\Objects\QueryArguments;
910
use Neo4j\QueryAPI\Objects\ResultCounters;
@@ -14,7 +15,7 @@ class ResultSet implements IteratorAggregate, Countable
1415
/**
1516
* @param list<ResultRow> $rows
1617
*/
17-
public function __construct(private readonly array $rows, private ResultCounters $counters, private ?ProfiledQueryPlan $profiledQueryPlan = null , private ?QueryArguments $queryArguments = null)
18+
public function __construct(private readonly array $rows, private ResultCounters $counters, private ?ProfiledQueryPlan $profiledQueryPlan = null)
1819
{
1920
}
2021

@@ -38,6 +39,11 @@ public function getQueryArguments(): ?QueryArguments
3839
return $this->queryArguments;
3940
}
4041

42+
public function getChildQueryPlan(): ?ChildQueryPlan
43+
{
44+
return $this->childQueryPlan;
45+
}
46+
4147
public function count(): int
4248
{
4349
return count($this->rows);

tests/Integration/Neo4jQueryAPIIntegrationTest.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use GuzzleHttp\Exception\GuzzleException;
66
use Neo4j\QueryAPI\Exception\Neo4jException;
77
use Neo4j\QueryAPI\Neo4jQueryAPI;
8+
use Neo4j\QueryAPI\Objects\ProfiledQueryPlan;
89
use Neo4j\QueryAPI\Results\ResultRow;
910
use Neo4j\QueryAPI\Results\ResultSet;
1011
use PHPUnit\Framework\Attributes\DataProvider;
@@ -50,20 +51,26 @@ public function testProfileExistence(): void
5051
$result = $this->api->run($query);
5152
$this->assertNotNull($result->getProfiledQueryPlan(),"profiled query plan not found");
5253
}
53-
5454
public function testQueryArgumentsExistence(): void
5555
{
5656
$query = "PROFILE MATCH (n:Person {name: 'Alice'}) RETURN n.name";
5757
$result = $this->api->run($query);
58+
$this->assertNotNull($result->getQueryArguments(),"QueryArguments should not be null");
59+
}
60+
public function testChildQueryPlanExistence(): void
61+
{
62+
$result = $this->api->run("PROFILE MATCH (n:Person {name: 'Alice'}) RETURN n.name");
5863

59-
// Assert that the QueryArguments are not null
60-
$this->assertNotNull($result->getQueryArguments(), "QueryArguments not found");
64+
$profiledQueryPlan = $result->getProfiledQueryPlan();
65+
$this->assertNotNull($profiledQueryPlan);
66+
$this->assertNotEmpty($profiledQueryPlan->getChildren());
6167

62-
// You can assert on individual arguments if necessary
63-
$queryArguments = $result->getQueryArguments();
64-
$this->assertGreaterThanOrEqual(0, $queryArguments->getDbHits(), "DbHits should be >= 0");
68+
foreach ($profiledQueryPlan->getChildren() as $child) {
69+
$this->assertInstanceOf(ProfiledQueryPlan::class, $child);
70+
}
6571
}
6672

73+
6774
public function testTransactionCommit(): void
6875
{
6976
// Begin a new transaction

0 commit comments

Comments
 (0)