diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache new file mode 100644 index 00000000..378cd40f --- /dev/null +++ b/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.3.6","version":"3.68.0:v3.68.0#73f78d8b2b34a0dd65fedb434a602ee4c2c8ad4c","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":{"default":"at_least_single_space"},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"attribute_placement":"ignore","on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"strict_param":true},"hashes":{"tests\/Unit\/Neo4jExceptionUnitTest.php":"0d7842780eeb9729d501831ee55df0d8","tests\/Unit\/ResultRowTest.php":"f5ee9f21d2439793a290e8ab946a7f32","tests\/Unit\/Neo4jQueryAPIUnitTest.php":"54be7f7b0f9dcf13d62c4912127583b9","tests\/Integration\/Neo4jQueryAPIIntegrationTest.php":"44977ffd6c09c505b00c8ef4857b8bfd","tests\/Integration\/Neo4jOGMTest.php":"73136b2d28fbb4fa298467d1ab3e18c8","src\/OGM.php":"93aae9c7afc8dbfd5aa00bc1d264ad19","src\/Results\/ResultRow.php":"ad55ec1bd999a8f6ad6b18874c4017b5","src\/Results\/ResultSet.php":"5f7748a356bf0fb30403e3c5a411bd24","src\/Exception\/Neo4jException.php":"dfb0f6933b9d3913c5495ba6d801d5f1","src\/Objects\/Path.php":"88c95962a6316ba7aa2fa3f0f6e31627","src\/Objects\/Node.php":"4a8ab7b8bd1981ee4d35d8c52b81c7c3","src\/Objects\/ProfiledQueryPlanArguments.php":"1be7b230a034a72c13349a5670a34a2f","src\/Objects\/Person.php":"f2f469937660f5454761e4f31154e081","src\/Objects\/Point.php":"169715b2157e08482e420374e6ca4cc3","src\/Objects\/Bookmarks.php":"50f89ca88b2df817433ce8237ccc0f18","src\/Objects\/ResultCounters.php":"a9372c98fe7bede10cb004af30ea502f","src\/Objects\/Relationship.php":"f6347c0260780d4f5d2dc407dc97e25e","src\/Transaction.php":"e456922858b31d87b17ca47d25d58474","tests\/resources\/expected\/complex-query-profile.php":"cc2b1e7e731c30a8855d9fa368cd55f3","src\/Neo4jQueryAPI.php":"8bffb787a834b58523e89fc9e5c19fe3","src\/Objects\/ProfiledQueryPlan.php":"d9ba608f3177426ea34d73276d75f20b"}} \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 00000000..28bf20e3 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,18 @@ +setRiskyAllowed(true) // Allow risky fixers + ->setRules([ + '@PSR12' => true, + 'strict_param' => true, // This is a risky rule + ]) + ->setFinder( + Finder::create() + ->in(__DIR__) + ->exclude([ + 'vendor', + ]) + ); diff --git a/src/Neo4jQueryAPI.php b/src/Neo4jQueryAPI.php index df40cc6c..cc315979 100644 --- a/src/Neo4jQueryAPI.php +++ b/src/Neo4jQueryAPI.php @@ -4,10 +4,7 @@ use Exception; use GuzzleHttp\Client; -use GuzzleHttp\Exception\GuzzleException; -use GuzzleHttp\Exception\RequestException; -use Neo4j\QueryAPI\Objects\ChildQueryPlan; -use Neo4j\QueryAPI\Objects\QueryArguments; +use Neo4j\QueryAPI\Objects\ProfiledQueryPlanArguments; use Neo4j\QueryAPI\Objects\ResultCounters; use Neo4j\QueryAPI\Objects\ProfiledQueryPlan; use Neo4j\QueryAPI\Results\ResultRow; @@ -26,18 +23,9 @@ public function __construct(Client $client) { $this->client = $client; } - /** - * @api - */ + public static function login(string $address, string $username, string $password): self { - if (empty($address)) { - throw new RuntimeException('Address cannot be empty'); - } - if (empty($username) || empty($password)) { - throw new RuntimeException('Missing username or password'); - } - $client = new Client([ 'base_uri' => rtrim($address, '/'), 'timeout' => 10.0, @@ -54,7 +42,6 @@ public static function login(string $address, string $username, string $password /** * @throws Neo4jException * @throws RequestExceptionInterface - * @api */ public function run(string $cypher, array $parameters = [], string $database = 'neo4j', Bookmarks $bookmark = null): ResultSet { @@ -69,15 +56,21 @@ public function run(string $cypher, array $parameters = [], string $database = ' $payload['bookmarks'] = $bookmark->getBookmarks(); } - $response = $this->client->post('/db/' . $database . '/query/v2', [ + $response = $this->client->request('POST', '/db/' . $database . '/query/v2', [ 'json' => $payload, ]); - $data = json_decode($response->getBody()->getContents(), true); + $contents = $response->getBody()->getContents(); + $data = json_decode($contents, true, flags: JSON_THROW_ON_ERROR); $ogm = new OGM(); - $keys = $data['data']['fields']; - $values = $data['data']['values']; + $keys = $data['data']['fields'] ?? []; + $values = $data['data']['values'] ?? []; // Ensure $values is an array + + if (!is_array($values)) { + throw new RuntimeException('Unexpected response format: values is not an array.'); + } + $rows = array_map(function ($resultRow) use ($ogm, $keys) { $data = []; foreach ($keys as $index => $key) { @@ -86,10 +79,7 @@ public function run(string $cypher, array $parameters = [], string $database = ' } return new ResultRow($data); }, $values); - - if (isset($data['profiledQueryPlan'])) { - $profile = $this->createProfileData($data['profiledQueryPlan']); - } + $profile = isset($data['profiledQueryPlan']) ? $this->createProfileData($data['profiledQueryPlan']) : null; $resultCounters = new ResultCounters( containsUpdates: $data['counters']['containsUpdates'] ?? false, @@ -119,19 +109,14 @@ public function run(string $cypher, array $parameters = [], string $database = ' if ($response !== null) { $contents = $response->getBody()->getContents(); $errorResponse = json_decode($contents, true); - throw Neo4jException::fromNeo4jResponse($errorResponse, $e); } + throw $e; } - throw new RuntimeException('Error executing query: ' . $e->getMessage(), 0, $e); } - /** - * @api - */ public function beginTransaction(string $database = 'neo4j'): Transaction { - unset($database); $response = $this->client->post("/db/neo4j/query/v2/tx"); $clusterAffinity = $response->getHeaderLine('neo4j-cluster-affinity'); @@ -143,27 +128,43 @@ public function beginTransaction(string $database = 'neo4j'): Transaction private function createProfileData(array $data): ProfiledQueryPlan { + $ogm = new OGM(); + + // Map arguments using OGM $arguments = $data['arguments']; + $mappedArguments = []; + foreach ($arguments as $key => $value) { + if (is_array($value) && array_key_exists('$type', $value) && array_key_exists('_value', $value)) { + $mappedArguments[$key] = $ogm->map($value); + } else { + $mappedArguments[$key] = $value; + } + } - $queryArguments = new QueryArguments( - $arguments['globalMemory'] ?? 0, - $arguments['plannerImpl'] ?? '', - $arguments['memory'] ?? 0, - $arguments['stringRepresentation'] ?? '', - is_string($arguments['runtime'] ?? '') ? $arguments['runtime'] : json_encode($arguments['runtime']), - $arguments['runtimeImpl'] ?? '', - $arguments['dbHits'] ?? 0, - $arguments['batchSize'] ?? 0, - $arguments['details'] ?? '', - $arguments['plannerVersion'] ?? '', - $arguments['pipelineInfo'] ?? '', - $arguments['runtimeVersion'] ?? '', - $arguments['id'] ?? 0, - $arguments['estimatedRows'] ?? 0.0, - is_string($arguments['planner'] ?? '') ? $arguments['planner'] : json_encode($arguments['planner']), - $arguments['rows'] ?? 0 + $queryArguments = new ProfiledQueryPlanArguments( + globalMemory: $mappedArguments['GlobalMemory'] ?? null, + plannerImpl: $mappedArguments['planner-impl'] ?? null, + memory: $mappedArguments['Memory'] ?? null, + stringRepresentation: $mappedArguments['string-representation'] ?? null, + runtime: $mappedArguments['runtime'] ?? null, + time: $mappedArguments['Time'] ?? null, + pageCacheMisses: $mappedArguments['PageCacheMisses'] ?? null, + pageCacheHits: $mappedArguments['PageCacheHits'] ?? null, + runtimeImpl: $mappedArguments['runtime-impl'] ?? null, + version: $mappedArguments['version'] ?? null, + dbHits: $mappedArguments['DbHits'] ?? null, + batchSize: $mappedArguments['batch-size'] ?? null, + details: $mappedArguments['Details'] ?? null, + plannerVersion: $mappedArguments['planner-version'] ?? null, + pipelineInfo: $mappedArguments['PipelineInfo'] ?? null, + runtimeVersion: $mappedArguments['runtime-version'] ?? null, + id: $mappedArguments['Id'] ?? null, + estimatedRows: $mappedArguments['EstimatedRows'] ?? null, + planner: $mappedArguments['planner'] ?? null, + rows: $mappedArguments['Rows' ?? null] ); + $identifiers = $data['identifiers'] ?? []; $profiledQueryPlan = new ProfiledQueryPlan( $data['dbHits'], $data['records'], @@ -173,17 +174,17 @@ private function createProfileData(array $data): ProfiledQueryPlan $data['pageCacheHitRatio'], $data['time'], $data['operatorType'], - $queryArguments + $queryArguments, + children: [], + identifiers: $identifiers ); - + // Process children recursively foreach ($data['children'] as $child) { $childQueryPlan = $this->createProfileData($child); - $profiledQueryPlan->addChild($childQueryPlan); } return $profiledQueryPlan; } - } diff --git a/src/Objects/ProfiledQueryPlan.php b/src/Objects/ProfiledQueryPlan.php index 268e95d6..a32d314f 100644 --- a/src/Objects/ProfiledQueryPlan.php +++ b/src/Objects/ProfiledQueryPlan.php @@ -12,23 +12,30 @@ class ProfiledQueryPlan private float $pageCacheHitRatio; private int $time; private string $operatorType; - private QueryArguments $arguments; + private ProfiledQueryPlanArguments $arguments; /** - * @var list + * @var list */ private array $children; + /** + * @var string[] + */ + private array $identifiers; + public function __construct( - ?int $dbHits = 0, // Default to 0 if null - ?int $records = 0, - ?bool $hasPageCacheStats = false, - ?int $pageCacheHits = 0, - ?int $pageCacheMisses = 0, - ?float $pageCacheHitRatio = 0.0, - ?int $time = 0, - ?string $operatorType = '', - QueryArguments $arguments + ?int $dbHits, + ?int $records, + ?bool $hasPageCacheStats, + ?int $pageCacheHits, + ?int $pageCacheMisses, + ?float $pageCacheHitRatio, + ?int $time, + ?string $operatorType, + ProfiledQueryPlanArguments $arguments, + ?array $children = [], + array $identifiers = [] // Default to an empty array ) { $this->dbHits = $dbHits ?? 0; $this->records = $records ?? 0; @@ -39,94 +46,86 @@ public function __construct( $this->time = $time ?? 0; $this->operatorType = $operatorType ?? ''; $this->arguments = $arguments; + $this->children = $children ?? []; + $this->identifiers = $identifiers; } - /** - * @api - */ public function getDbHits(): int { return $this->dbHits; } - /** - * @api - */ public function getRecords(): int { return $this->records; } - /** - * @api - */ public function hasPageCacheStats(): bool { return $this->hasPageCacheStats; } - /** - * @api - */ public function getPageCacheHits(): int { return $this->pageCacheHits; } - /** - * @api - */ public function getPageCacheMisses(): int { return $this->pageCacheMisses; } - /** - * @api - */ public function getPageCacheHitRatio(): float { return $this->pageCacheHitRatio; } - /** - * @api - */ public function getTime(): int { return $this->time; } - /** - * @api - */ public function getOperatorType(): string { return $this->operatorType; } - /** - * @api - */ - public function getArguments(): QueryArguments + public function getArguments(): ProfiledQueryPlanArguments { return $this->arguments; } /** - * @api - * @return list + * @return list */ public function getChildren(): array { return $this->children; } + + public function addChild(ProfiledQueryPlan|ProfiledQueryPlanArguments $child): void + { + $this->children[] = $child; + } + /** - * @api + * @return string[] */ + public function getIdentifiers(): array + { + return $this->identifiers; + } + + /** + * @param string[] $identifiers + */ + public function setIdentifiers(array $identifiers): void + { + $this->identifiers = $identifiers; + } - public function addChild(ProfiledQueryPlan $child): void + public function addIdentifier(string $identifier): void { - $this->children[] = $child; + $this->identifiers[] = $identifier; } } diff --git a/src/Objects/ProfiledQueryPlanArguments.php b/src/Objects/ProfiledQueryPlanArguments.php new file mode 100644 index 00000000..6943116c --- /dev/null +++ b/src/Objects/ProfiledQueryPlanArguments.php @@ -0,0 +1,130 @@ +globalMemory; + } + + public function getPlannerImpl(): ?string + { + return $this->plannerImpl; + } + + public function getMemory(): ?int + { + return $this->memory; + } + + public function getStringRepresentation(): ?string + { + return $this->stringRepresentation; + } + + public function getRuntime(): ?string + { + return $this->runtime; + } + + public function getTime(): ?int + { + return $this->time; + } + + public function getPageCacheMisses(): ?int + { + return $this->pageCacheMisses; + } + + private function getPageCacheHits(): ?int + { + return $this->pageCacheHits; + } + + public function getRuntimeImpl(): ?string + { + return $this->runtimeImpl; + } + + public function getVersion(): ?int + { + return $this->version; + } + + public function getDbHits(): ?int + { + return $this->dbHits; + } + + public function getBatchSize(): ?int + { + return $this->batchSize; + } + + public function getDetails(): ?string + { + return $this->details; + } + + public function getPlannerVersion(): ?string + { + return $this->plannerVersion; + } + + public function getPipelineInfo(): ?string + { + return $this->pipelineInfo; + } + + public function getRuntimeVersion(): ?string + { + return $this->runtimeVersion; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getEstimatedRows(): ?float + { + return $this->estimatedRows; + } + + public function getPlanner(): ?string + { + return $this->planner; + } + + public function getRows(): ?int + { + return $this->rows; + } +} diff --git a/src/Objects/QueryArguments.php b/src/Objects/QueryArguments.php deleted file mode 100644 index ff0ccf29..00000000 --- a/src/Objects/QueryArguments.php +++ /dev/null @@ -1,189 +0,0 @@ -globalMemory = $globalMemory ?? 0; - $this->plannerImpl = $plannerImpl ?? ''; - $this->memory = $memory ?? 0; - $this->stringRepresentation = $stringRepresentation ?? ''; - $this->runtime = is_string($runtime) ? $runtime : json_encode($runtime); - $this->runtimeImpl = $runtimeImpl ?? ''; - $this->dbHits = $dbHits ?? 0; - $this->batchSize = $batchSize ?? 0; - $this->details = $details ?? ''; - $this->plannerVersion = $plannerVersion ?? ''; - $this->pipelineInfo = $pipelineInfo ?? ''; - $this->runtimeVersion = $runtimeVersion ?? ''; - $this->id = $id ?? 0; - $this->estimatedRows = $estimatedRows ?? 0.0; - $this->planner = $planner ?? ''; - $this->rows = $rows ?? 0; - } - /** - * @api - */ - - public function getGlobalMemory(): int - { - return $this->globalMemory; - } - /** - * @api - */ - - public function getPlannerImpl(): string - { - return $this->plannerImpl; - } - /** - * @api - */ - - public function getMemory(): int - { - return $this->memory; - } - /** - * @api - */ - - public function getStringRepresentation(): string - { - return $this->stringRepresentation; - } - /** - * @api - */ - - public function getRuntime(): string - { - return $this->runtime; - } - /** - * @api - */ - - public function getRuntimeImpl(): string - { - return $this->runtimeImpl; - } - /** - * @api - */ - - public function getDbHits(): int - { - return $this->dbHits; - } - /** - * @api - */ - - public function getBatchSize(): int - { - return $this->batchSize; - } - /** - * @api - */ - - public function getDetails(): string - { - return $this->details; - } - /** - * @api - */ - - public function getPlannerVersion(): string - { - return $this->plannerVersion; - } - /** - * @api - */ - - public function getPipelineInfo(): string - { - return $this->pipelineInfo; - } - /** - * @api - */ - - public function getRuntimeVersion(): string - { - return $this->runtimeVersion; - } - /** - * @api - */ - - public function getId(): int - { - return $this->id; - } - /** - * @api - */ - - public function getEstimatedRows(): float - { - return $this->estimatedRows; - } - /** - * @api - */ - - public function getPlanner(): string - { - return $this->planner; - } - /** - * @api - */ - public function getRows(): int - { - return $this->rows; - } -} diff --git a/src/Results/ResultSet.php b/src/Results/ResultSet.php index f8bd080b..b822615f 100644 --- a/src/Results/ResultSet.php +++ b/src/Results/ResultSet.php @@ -5,17 +5,13 @@ use ArrayIterator; use Countable; use IteratorAggregate; +use Neo4j\QueryAPI\Objects\ChildQueryPlan; use Neo4j\QueryAPI\Objects\ProfiledQueryPlan; +use Neo4j\QueryAPI\Objects\ProfiledQueryPlanArguments; use Neo4j\QueryAPI\Objects\ResultCounters; -use Neo4j\QueryAPI\Objects\Bookmarks; +use Neo4j\QueryAPI\Objects\Bookmarks; // Make sure to include the Bookmarks class use Traversable; -/** - * @api - * @template TKey of array-key - * @template TValue - * @implements IteratorAggregate - */ class ResultSet implements IteratorAggregate, Countable { /** @@ -25,47 +21,35 @@ public function __construct( private readonly array $rows, private ResultCounters $counters, private Bookmarks $bookmarks, - private ?ProfiledQueryPlan $profiledQueryPlan = null + private ?ProfiledQueryPlan $profiledQueryPlan = null, + private ?ProfiledQueryPlanArguments $profiledQueryPlanArguments = null ) { - - } - /** - * @return Traversable - */ public function getIterator(): Traversable { return new ArrayIterator($this->rows); } - - /** - * @api - */ public function getQueryCounters(): ?ResultCounters { return $this->counters; } - /** - * @api - */ public function getProfiledQueryPlan(): ?ProfiledQueryPlan { return $this->profiledQueryPlan; } - /** - * @api - */ + public function getChildQueryPlan(): ?ChildQueryPlan + { + return $this->childQueryPlan; + } + public function count(): int { return count($this->rows); } - /** - * @api - */ public function getBookmarks(): ?Bookmarks { return $this->bookmarks; diff --git a/tests/Integration/Neo4jQueryAPIIntegrationTest.php b/tests/Integration/Neo4jQueryAPIIntegrationTest.php index c85d9e34..f01bac6f 100644 --- a/tests/Integration/Neo4jQueryAPIIntegrationTest.php +++ b/tests/Integration/Neo4jQueryAPIIntegrationTest.php @@ -2,7 +2,11 @@ namespace Neo4j\QueryAPI\Tests\Integration; +use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Psr7\Response; use Neo4j\QueryAPI\Exception\Neo4jException; use Neo4j\QueryAPI\Neo4jQueryAPI; use Neo4j\QueryAPI\Objects\ProfiledQueryPlan; @@ -13,6 +17,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Neo4j\QueryAPI\Transaction; +use Psr\Http\Client\RequestExceptionInterface; class Neo4jQueryAPIIntegrationTest extends TestCase { @@ -152,18 +157,39 @@ public function testProfileCreateWatchedWithFilters(): void $this->assertNotNull($result->getProfiledQueryPlan(), "profiled query plan not found"); } - public function testProfileCreateKnowsBidirectionalRelationships(): void + /** + * @throws Neo4jException + * @throws RequestExceptionInterface + */ + public function testProfileCreateKnowsBidirectionalRelationshipsMock(): void { $query = " - PROFILE UNWIND range(1, 100) AS i - UNWIND range(1, 100) AS j - MATCH (a:Person {id: i}), (b:Person {id: j}) - WHERE a.id < b.id AND rand() < 0.1 - CREATE (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(a); + PROFILE UNWIND range(1, 100) AS i + UNWIND range(1, 100) AS j + MATCH (a:Person {id: i}), (b:Person {id: j}) + WHERE a.id < b.id AND rand() < 0.1 + CREATE (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(a); "; - $result = $this->api->run($query); - $this->assertNotNull($result->getProfiledQueryPlan(), "profiled query plan not found"); + $body = file_get_contents(__DIR__ . '/../resources/responses/complex-query-profile.json'); + $mockSack = new MockHandler([ + new Response(200, [], $body), + ]); + + $handler = HandlerStack::create($mockSack); + $client = new Client(['handler' => $handler]); + $api = new Neo4jQueryAPI($client); + + $result = $api->run($query); + + $plan = $result->getProfiledQueryPlan(); + $this->assertNotNull($plan, "The result of the query should not be null."); + + // Load expected data + $expected = require __DIR__ . '/../resources/expected/complex-query-profile.php'; + + // Assert the profiled query plan matches the expected result + $this->assertEquals($expected->getProfiledQueryPlan(), $plan, "Profiled query plan does not match the expected value."); } public function testProfileCreateActedInRelationships(): void @@ -179,6 +205,7 @@ public function testProfileCreateActedInRelationships(): void $this->assertNotNull($result->getProfiledQueryPlan(), "profiled query plan not found"); } + public function testChildQueryPlanExistence(): void { $result = $this->api->run("PROFILE MATCH (n:Person {name: 'Alice'}) RETURN n.name"); @@ -192,6 +219,8 @@ public function testChildQueryPlanExistence(): void } } + + public function testTransactionCommit(): void { // Begin a new transaction diff --git a/tests/resources/expected/complex-query-profile.php b/tests/resources/expected/complex-query-profile.php new file mode 100644 index 00000000..858b88cb --- /dev/null +++ b/tests/resources/expected/complex-query-profile.php @@ -0,0 +1,432 @@ +(b), (b)-[anon_1:KNOWS]->(a) | 2 | 0 | 0 | | 0/0 | 0.000 | |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ |\n| +Filter | 3 | cache[a.id] < cache[b.id] | 2 | 0 | 0 | | 0/0 | 0.000 | In Pipeline 3 |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| +Apply | 4 | | 8 | 0 | 0 | | 0/0 | | |\n| |\\ +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| | +CartesianProduct | 5 | | 8 | 0 | 0 | 1392 | | | In Pipeline 3 |\n| | |\\ +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| | | +Filter | 6 | cache[b.id] = j | 50 | 0 | 0 | | | | |\n| | | | +----+--------------------------------------------------+----------------+-------+---------+----------------+ | | |\n| | | +NodeByLabelScan | 7 | b:Person | 1000 | 0 | 0 | 256 | 0/0 | 0.000 | Fused in Pipeline 2 |\n| | | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| | +Filter | 8 | rand() < \$autodouble_4 AND cache[a.id] = i | 15 | 0 | 2027 | | | | |\n| | | +----+--------------------------------------------------+----------------+-------+---------+----------------+ | | |\n| | +NodeByLabelScan | 9 | a:Person | 1000 | 20000 | 30000 | 8488 | 10002/0 | 6.158 | Fused in Pipeline 1 |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| +Unwind | 10 | range(\$autoint_2, \$autoint_3) AS j | 100 | 10000 | 0 | | | | |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+ | | |\n| +Unwind | 11 | range(\$autoint_0, \$autoint_1) AS i | 10 | 100 | 0 | | 0/0 | 0.000 | Fused in Pipeline 0 |\n+----------------------+----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n\nTotal database accesses: 32027, total allocated memory: 10624\n", + runtime: "PIPELINED", + time: 0, + pageCacheMisses: 0, + pageCacheHits: 0, + runtimeImpl: "PIPELINED", + version: 5, + dbHits: 0, + batchSize: 128, + details: "", + plannerVersion: 5.26, + pipelineInfo: "In Pipeline 3", + runtimeVersion: 5.26, + id: 0, + estimatedRows: 2.25, + planner: 'COST', + rows: 0, + ), + children: [ + // CHILD #1: EmptyResult@neo4j + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "EmptyResult@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: 0, + pageCacheMisses: 0, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: null, + details: null, + // Using empty strings or nulls for fields not strictly needed + plannerVersion: null, + pipelineInfo: 'In Pipeline 3', + runtimeVersion: null, + id: 1, + estimatedRows: 2.25, + rows: 0 + ), + children: [ + // CHILD #1.1: Create@neo4j + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Create@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: 0, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "(a)-[anon_0:KNOWS]->(b), (b)-[anon_1:KNOWS]->(a)", + plannerVersion: "", + pipelineInfo: "In Pipeline 3", + runtimeVersion: null, + id: 2, + estimatedRows: 2.25, + rows: 0 + ), + children: [ + // CHILD #1.1.1: Filter@neo4j (id=3) + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Filter@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: 0, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "cache[a.id] < cache[b.id]", + plannerVersion: "", + pipelineInfo: "In Pipeline 3", + runtimeVersion: null, + id: 3, + estimatedRows: 2.25, + rows: 0 + ), + children: [ + // CHILD #1.1.1.1: Apply@neo4j (id=4) + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Apply@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: null, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "", + plannerVersion: "", + pipelineInfo: "", + runtimeVersion: null, + id: 4, + estimatedRows: 7.5, + rows: 0 + ), + children: [ + // CHILD #1.1.1.1.1: Unwind@neo4j (id=10) + new ProfiledQueryPlan( + dbHits: 0, + records: 10000, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Unwind@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: null, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "range(\$autoint_2, \$autoint_3) AS j", + plannerVersion: "", + pipelineInfo: "Fused in Pipeline 0", + runtimeVersion: null, + id: 10, + estimatedRows: 100.0, + rows: 10000 + ), + children: [ + // The second Unwind@neo4j (id=11) + new ProfiledQueryPlan( + dbHits: 0, + records: 100, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Unwind@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: 0, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "range(\$autoint_0, \$autoint_1) AS i", + plannerVersion: "", + pipelineInfo: "Fused in Pipeline 0", + runtimeVersion: null, + id: 11, + estimatedRows: 10.0, + rows: 100 + ), + identifiers: ["i"] + ) + ], + identifiers: ["i","j"] + ), + // CHILD #1.1.1.1.2: CartesianProduct@neo4j (id=5) + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "CartesianProduct@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: 1392, + stringRepresentation: null, + runtime: null, + time: null, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "", + plannerVersion: "", + pipelineInfo: "In Pipeline 3", + runtimeVersion: null, + id: 5, + estimatedRows: 7.5, + rows: 0 + ), + children: [ + // CHILD #1.1.1.1.2.1: Filter@neo4j (id=8) + new ProfiledQueryPlan( + dbHits: 2027, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Filter@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: null, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 2027, + batchSize: 0, + details: "rand() < \$autodouble_4 AND cache[a.id] = i", + plannerVersion: "", + pipelineInfo: "Fused in Pipeline 1", + runtimeVersion: null, + id: 8, + estimatedRows: 15.0, + rows: 0 + ), + children: [ + // NodeByLabelScan@neo4j (id=9) + new ProfiledQueryPlan( + dbHits: 30000, + records: 20000, + hasPageCacheStats: true, + pageCacheHits: 10002, + pageCacheMisses: 0, + pageCacheHitRatio: 1.0, + time: 6157670, + operatorType: "NodeByLabelScan@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: 8488, + stringRepresentation: null, + runtime: null, + time: 6157670, + pageCacheMisses: 0, + pageCacheHits: 10002, + runtimeImpl: null, + version: null, + dbHits: 30000, + batchSize: 0, + details: "a:Person", + plannerVersion: "", + pipelineInfo: "Fused in Pipeline 1", + runtimeVersion: null, + id: 9, + estimatedRows: 1000.0, + rows: 20000, + ), + identifiers: ["i","j","a"] + ) + ], + identifiers: ["i","j","a"] + ), + // CHILD #1.1.1.1.2.2: Filter@neo4j (id=6) + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "Filter@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: null, + stringRepresentation: null, + runtime: null, + time: null, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "cache[b.id] = j", + plannerVersion: "", + pipelineInfo: "Fused in Pipeline 2", + runtimeVersion: null, + id: 6, + estimatedRows: 50.0, + rows: 0 + ), + children: [ + // NodeByLabelScan@neo4j (id=7) + new ProfiledQueryPlan( + dbHits: 0, + records: 0, + hasPageCacheStats: false, + pageCacheHits: 0, + pageCacheMisses: 0, + pageCacheHitRatio: 0.0, + time: 0, + operatorType: "NodeByLabelScan@neo4j", + arguments: new ProfiledQueryPlanArguments( + plannerImpl: null, + memory: 256, + stringRepresentation: null, + runtime: null, + time: 0, + pageCacheMisses: null, + pageCacheHits: 0, + runtimeImpl: null, + version: null, + dbHits: 0, + batchSize: 0, + details: "b:Person", + plannerVersion: "", + pipelineInfo: "Fused in Pipeline 2", + runtimeVersion: null, + id: 7, + estimatedRows: 1000.0, + rows: 0 + ), + identifiers: ["i","j","b"] + ) + ], + identifiers: ["i","j","b"] + ) + ], + identifiers: ["i","j","a","b"] + ) + ], + identifiers: ["i","j","a","b"] + ) + ], + identifiers: ["i","j","a","b"] + ) + ], + identifiers: ["j","a","i","b","anon_0","anon_1"] + ) + ], + identifiers: ["j","a","i","b","anon_0","anon_1"] + ) + ], + identifiers: ["j","a","i","b","anon_0","anon_1"] + ) +); diff --git a/tests/resources/responses/complex-query-profile.json b/tests/resources/responses/complex-query-profile.json new file mode 100644 index 00000000..d4e02145 --- /dev/null +++ b/tests/resources/responses/complex-query-profile.json @@ -0,0 +1,528 @@ +{ + "data" : { + "fields" : [ ], + "values" : [ ] + }, + "notifications" : [ { + "code" : "Neo.ClientNotification.Statement.CartesianProduct", + "description" : "If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (b))", + "severity" : "INFORMATION", + "title" : "This query builds a cartesian product between disconnected patterns.", + "position" : { + "offset" : 73, + "line" : 4, + "column" : 5 + }, + "category" : "PERFORMANCE" + } ], + "counters" : { + "containsUpdates" : false, + "nodesCreated" : 0, + "nodesDeleted" : 0, + "propertiesSet" : 0, + "relationshipsCreated" : 0, + "relationshipsDeleted" : 0, + "labelsAdded" : 0, + "labelsRemoved" : 0, + "indexesAdded" : 0, + "indexesRemoved" : 0, + "constraintsAdded" : 0, + "constraintsRemoved" : 0, + "containsSystemUpdates" : false, + "systemUpdates" : 0 + }, + "profiledQueryPlan" : { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "ProduceResults@neo4j", + "arguments" : { + "GlobalMemory" : 10624, + "planner-impl" : "IDP", + "string-representation" : "Cypher 5\n\nPlanner COST\n\nRuntime PIPELINED\n\nRuntime version 5.26\n\nBatch size 128\n\n+----------------------+----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |\n+----------------------+----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| +ProduceResults | 0 | | 2 | 0 | 0 | | 0/0 | 0.000 | |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ |\n| +EmptyResult | 1 | | 2 | 0 | 0 | | 0/0 | 0.000 | |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ |\n| +Create | 2 | (a)-[anon_0:KNOWS]->(b), (b)-[anon_1:KNOWS]->(a) | 2 | 0 | 0 | | 0/0 | 0.000 | |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ |\n| +Filter | 3 | cache[a.id] < cache[b.id] | 2 | 0 | 0 | | 0/0 | 0.000 | In Pipeline 3 |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| +Apply | 4 | | 8 | 0 | 0 | | 0/0 | | |\n| |\\ +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| | +CartesianProduct | 5 | | 8 | 0 | 0 | 1392 | | | In Pipeline 3 |\n| | |\\ +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| | | +Filter | 6 | cache[b.id] = j | 50 | 0 | 0 | | | | |\n| | | | +----+--------------------------------------------------+----------------+-------+---------+----------------+ | | |\n| | | +NodeByLabelScan | 7 | b:Person | 1000 | 0 | 0 | 256 | 0/0 | 0.000 | Fused in Pipeline 2 |\n| | | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| | +Filter | 8 | rand() < $autodouble_4 AND cache[a.id] = i | 15 | 0 | 2027 | | | | |\n| | | +----+--------------------------------------------------+----------------+-------+---------+----------------+ | | |\n| | +NodeByLabelScan | 9 | a:Person | 1000 | 20000 | 30000 | 8488 | 10002/0 | 6.158 | Fused in Pipeline 1 |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n| +Unwind | 10 | range($autoint_2, $autoint_3) AS j | 100 | 10000 | 0 | | | | |\n| | +----+--------------------------------------------------+----------------+-------+---------+----------------+ | | |\n| +Unwind | 11 | range($autoint_0, $autoint_1) AS i | 10 | 100 | 0 | | 0/0 | 0.000 | Fused in Pipeline 0 |\n+----------------------+----+--------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+\n\nTotal database accesses: 32027, total allocated memory: 10624\n", + "runtime" : "PIPELINED", + "Time" : 0, + "runtime-impl" : "PIPELINED", + "version" : 5, + "DbHits" :0, + "batch-size" : 128, + "Details" : "", + "planner-version" : 5.26, + "PipelineInfo" : "In Pipeline 3", + "runtime-version" :5.26, + "Id" : 0, + "PageCacheMisses" : 0, + "EstimatedRows" : 2.25, + "planner" : "COST", + "Rows" : 0, + "PageCacheHits" : 0 + }, + "identifiers" : [ "j", "a", "i", "b", "anon_0", "anon_1" ], + "children" : [ + { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "EmptyResult@neo4j", + "arguments" : { + "PipelineInfo" : "In Pipeline 3", + "Time" : 0, + "Id" : 1, + "PageCacheMisses" : 0, + "EstimatedRows" : 2.25, + "DbHits" : 0, + "Rows" :0, + "PageCacheHits" :0 + }, + "identifiers" : [ "j", "a", "i", "b", "anon_0", "anon_1" ], + "children" : [ { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Create@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "(a)-[anon_0:KNOWS]->(b), (b)-[anon_1:KNOWS]->(a)" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "In Pipeline 3" + }, + "Time" : { + "$type" : "Integer", + "_value" : "0" + }, + "Id" : { + "$type" : "Integer", + "_value" : "2" + }, + "PageCacheMisses" : { + "$type" : "Integer", + "_value" : "0" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "2.25" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + }, + "PageCacheHits" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "j", "a", "i", "b", "anon_0", "anon_1" ], + "children" : [ { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Filter@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "cache[a.id] < cache[b.id]" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "In Pipeline 3" + }, + "Time" : { + "$type" : "Integer", + "_value" : "0" + }, + "Id" : { + "$type" : "Integer", + "_value" : "3" + }, + "PageCacheMisses" : { + "$type" : "Integer", + "_value" : "0" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "2.25" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + }, + "PageCacheHits" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i", "j", "a", "b" ], + "children" : [ { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Apply@neo4j", + "arguments" : { + "Id" : { + "$type" : "Integer", + "_value" : "4" + }, + "PageCacheMisses" : { + "$type" : "Integer", + "_value" : "0" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "7.5" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + }, + "PageCacheHits" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i", "j", "a", "b" ], + "children" : [ { + "dbHits" : 0, + "records" : 10000, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Unwind@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "range($autoint_2, $autoint_3) AS j" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "Fused in Pipeline 0" + }, + "Id" : { + "$type" : "Integer", + "_value" : "10" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "100.0" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "10000" + } + }, + "identifiers" : [ "i", "j" ], + "children" : [ { + "dbHits" : 0, + "records" : 100, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Unwind@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "range($autoint_0, $autoint_1) AS i" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "Fused in Pipeline 0" + }, + "Time" : { + "$type" : "Integer", + "_value" : "0" + }, + "Id" : { + "$type" : "Integer", + "_value" : "11" + }, + "PageCacheMisses" : { + "$type" : "Integer", + "_value" : "0" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "10.0" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "100" + }, + "PageCacheHits" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i" ], + "children" : [ ] + } ] + }, { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "CartesianProduct@neo4j", + "arguments" : { + "Memory" : { + "$type" : "Integer", + "_value" : "1392" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "In Pipeline 3" + }, + "Id" : { + "$type" : "Integer", + "_value" : "5" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "7.5" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i", "j", "a", "b" ], + "children" : [ { + "dbHits" : 2027, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Filter@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "rand() < $autodouble_4 AND cache[a.id] = i" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "Fused in Pipeline 1" + }, + "Id" : { + "$type" : "Integer", + "_value" : "8" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "15.0" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "2027" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i", "j", "a" ], + "children" : [ { + "dbHits" : 30000, + "records" : 20000, + "hasPageCacheStats" : true, + "pageCacheHits" : 10002, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 1.0, + "time" : 6157670, + "operatorType" : "NodeByLabelScan@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "a:Person" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "Fused in Pipeline 1" + }, + "Memory" : { + "$type" : "Integer", + "_value" : "8488" + }, + "Time" : { + "$type" : "Integer", + "_value" : "6157670" + }, + "Id" : { + "$type" : "Integer", + "_value" : "9" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "1000.0" + }, + "PageCacheMisses" : { + "$type" : "Integer", + "_value" : "0" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "30000" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "20000" + }, + "PageCacheHits" : { + "$type" : "Integer", + "_value" : "10002" + } + }, + "identifiers" : [ "i", "j", "a" ], + "children" : [ ] + } ] + }, { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "Filter@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "cache[b.id] = j" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "Fused in Pipeline 2" + }, + "Id" : { + "$type" : "Integer", + "_value" : "6" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "50.0" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i", "j", "b" ], + "children" : [ { + "dbHits" : 0, + "records" : 0, + "hasPageCacheStats" : false, + "pageCacheHits" : 0, + "pageCacheMisses" : 0, + "pageCacheHitRatio" : 0.0, + "time" : 0, + "operatorType" : "NodeByLabelScan@neo4j", + "arguments" : { + "Details" : { + "$type" : "String", + "_value" : "b:Person" + }, + "PipelineInfo" : { + "$type" : "String", + "_value" : "Fused in Pipeline 2" + }, + "Memory" : { + "$type" : "Integer", + "_value" : "256" + }, + "Time" : { + "$type" : "Integer", + "_value" : "0" + }, + "Id" : { + "$type" : "Integer", + "_value" : "7" + }, + "EstimatedRows" : { + "$type" : "Float", + "_value" : "1000.0" + }, + "PageCacheMisses" : { + "$type" : "Integer", + "_value" : "0" + }, + "DbHits" : { + "$type" : "Integer", + "_value" : "0" + }, + "Rows" : { + "$type" : "Integer", + "_value" : "0" + }, + "PageCacheHits" : { + "$type" : "Integer", + "_value" : "0" + } + }, + "identifiers" : [ "i", "j", "b" ], + "children" : [ ] + } ] + } ] + } ] + } ] + } ] + } ] + } ] + }, + "bookmarks" : [ "FB:kcwQMSScZToiRKKW8P2Tlr362soAAQWHkA==" ] +} \ No newline at end of file