22
33namespace Neo4j \QueryAPI ;
44
5- use Exception ;
65use GuzzleHttp \Client ;
7- use Neo4j \QueryAPI \Objects \Auth ;
6+ use GuzzleHttp \Psr7 \Request ;
7+ use GuzzleHttp \Psr7 \Utils ;
8+ use Neo4j \QueryAPI \Exception \Neo4jException ;
89use Neo4j \QueryAPI \Objects \Authentication ;
10+ use Neo4j \QueryAPI \Objects \Bookmarks ;
11+ use Neo4j \QueryAPI \Objects \ProfiledQueryPlan ;
912use Neo4j \QueryAPI \Objects \ProfiledQueryPlanArguments ;
1013use Neo4j \QueryAPI \Objects \ResultCounters ;
11- use Neo4j \QueryAPI \Objects \ProfiledQueryPlan ;
14+ use Neo4j \QueryAPI \Objects \ResultSet ;
1215use Neo4j \QueryAPI \Results \ResultRow ;
13- use Neo4j \QueryAPI \Results \ResultSet ;
14- use Neo4j \QueryAPI \Exception \Neo4jException ;
16+ use Psr \Http \Client \ClientInterface ;
1517use Psr \Http \Client \RequestExceptionInterface ;
18+ use Psr \Http \Message \RequestInterface ;
1619use RuntimeException ;
1720use stdClass ;
18- use Neo4j \QueryAPI \Objects \Bookmarks ;
1921
2022class Neo4jQueryAPI
2123{
22- private Client $ client ;
24+ private ClientInterface $ client ;
25+ private AuthenticateInterface $ auth ;
2326
24- public function __construct (Client $ client )
27+ public function __construct (ClientInterface $ client, AuthenticateInterface $ auth )
2528 {
2629 $ this ->client = $ client ;
30+ $ this ->auth = $ auth ;
2731 }
2832
29- public static function login (string $ address , Authentication $ auth ): self
33+ /**
34+ * @throws \Exception
35+ */
36+ public static function login (string $ address , AuthenticateInterface $ auth = null ): self
3037 {
3138 $ client = new Client ([
3239 'base_uri ' => rtrim ($ address , '/ ' ),
3340 'timeout ' => 10.0 ,
3441 'headers ' => [
35- 'Authorization ' => $ auth ->getHeader (),
3642 'Content-Type ' => 'application/vnd.neo4j.query ' ,
3743 'Accept ' => 'application/vnd.neo4j.query ' ,
3844 ],
3945 ]);
4046
41- return new self ($ client );
47+ return new self ($ client, $ auth ?? Authentication:: fromEnvironment () );
4248 }
4349
44-
4550 /**
51+ * Executes a Cypher query on the Neo4j database.
52+ *
4653 * @throws Neo4jException
4754 * @throws RequestExceptionInterface
4855 */
@@ -59,68 +66,88 @@ public function run(string $cypher, array $parameters = [], string $database = '
5966 $ payload ['bookmarks ' ] = $ bookmark ->getBookmarks ();
6067 }
6168
62- $ response = $ this ->client ->request ('POST ' , '/db/ ' . $ database . '/query/v2 ' , [
63- 'json ' => $ payload ,
64- ]);
69+
70+ $ request = new Request ('POST ' , '/db/ ' . $ database . '/query/v2 ' );
71+
72+ $ request = $ this ->auth ->authenticate ($ request );
73+
74+ $ request = $ request ->withHeader ('Content-Type ' , 'application/json ' );
75+
76+ $ request = $ request ->withBody (Utils::streamFor (json_encode ($ payload )));
77+
78+ $ response = $ this ->client ->sendRequest ($ request );
79+
6580
6681 $ contents = $ response ->getBody ()->getContents ();
6782 $ data = json_decode ($ contents , true , flags: JSON_THROW_ON_ERROR );
68- $ ogm = new OGM ();
6983
70- $ keys = $ data ['data ' ]['fields ' ] ?? [];
71- $ values = $ data ['data ' ]['values ' ] ?? []; // Ensure $values is an array
84+ return $ this ->parseResultSet ($ data );
85+ } catch (RequestExceptionInterface $ e ) {
86+ $ this ->handleException ($ e );
87+ }
88+ }
89+
90+ private function parseResultSet (array $ data ): ResultSet
91+ {
92+ $ ogm = new OGM ();
7293
73- if (!is_array ($ values )) {
74- throw new RuntimeException ('Unexpected response format: values is not an array. ' );
75- }
94+ $ keys = $ data ['data ' ]['fields ' ] ?? [];
95+ $ values = $ data ['data ' ]['values ' ] ?? [];
7696
77- $ rows = array_map (function ($ resultRow ) use ($ ogm , $ keys ) {
78- $ data = [];
79- foreach ($ keys as $ index => $ key ) {
80- $ fieldData = $ resultRow [$ index ] ?? null ;
81- $ data [$ key ] = $ ogm ->map ($ fieldData );
82- }
83- return new ResultRow ($ data );
84- }, $ values );
85- $ profile = isset ($ data ['profiledQueryPlan ' ]) ? $ this ->createProfileData ($ data ['profiledQueryPlan ' ]) : null ;
86-
87- $ resultCounters = new ResultCounters (
88- containsUpdates: $ data ['counters ' ]['containsUpdates ' ] ?? false ,
89- nodesCreated: $ data ['counters ' ]['nodesCreated ' ] ?? 0 ,
90- nodesDeleted: $ data ['counters ' ]['nodesDeleted ' ] ?? 0 ,
91- propertiesSet: $ data ['counters ' ]['propertiesSet ' ] ?? 0 ,
92- relationshipsCreated: $ data ['counters ' ]['relationshipsCreated ' ] ?? 0 ,
93- relationshipsDeleted: $ data ['counters ' ]['relationshipsDeleted ' ] ?? 0 ,
94- labelsAdded: $ data ['counters ' ]['labelsAdded ' ] ?? 0 ,
95- labelsRemoved: $ data ['counters ' ]['labelsRemoved ' ] ?? 0 ,
96- indexesAdded: $ data ['counters ' ]['indexesAdded ' ] ?? 0 ,
97- indexesRemoved: $ data ['counters ' ]['indexesRemoved ' ] ?? 0 ,
98- constraintsAdded: $ data ['counters ' ]['constraintsAdded ' ] ?? 0 ,
99- constraintsRemoved: $ data ['counters ' ]['constraintsRemoved ' ] ?? 0 ,
100- containsSystemUpdates: $ data ['counters ' ]['containsSystemUpdates ' ] ?? false ,
101- systemUpdates: $ data ['counters ' ]['systemUpdates ' ] ?? 0
102- );
103-
104- return new ResultSet (
105- $ rows ,
106- $ resultCounters ,
107- new Bookmarks ($ data ['bookmarks ' ] ?? []),
108- $ profile
109- );
110- } catch (RequestExceptionInterface $ e ) {
111- $ response = $ e ->getResponse ();
112- if ($ response !== null ) {
113- $ contents = $ response ->getBody ()->getContents ();
114- $ errorResponse = json_decode ($ contents , true );
115- throw Neo4jException::fromNeo4jResponse ($ errorResponse , $ e );
97+ if (!is_array ($ values )) {
98+ throw new RuntimeException ('Unexpected response format: values is not an array. ' );
99+ }
100+
101+ $ rows = array_map (function ($ resultRow ) use ($ ogm , $ keys ) {
102+ $ row = [];
103+ foreach ($ keys as $ index => $ key ) {
104+ $ fieldData = $ resultRow [$ index ] ?? null ;
105+ $ row [$ key ] = $ ogm ->map ($ fieldData );
116106 }
117- throw $ e ;
107+ return new ResultRow ($ row );
108+ }, $ values );
109+
110+ $ resultCounters = new ResultCounters (
111+ containsUpdates: $ data ['counters ' ]['containsUpdates ' ] ?? false ,
112+ nodesCreated: $ data ['counters ' ]['nodesCreated ' ] ?? 0 ,
113+ nodesDeleted: $ data ['counters ' ]['nodesDeleted ' ] ?? 0 ,
114+ propertiesSet: $ data ['counters ' ]['propertiesSet ' ] ?? 0 ,
115+ relationshipsCreated: $ data ['counters ' ]['relationshipsCreated ' ] ?? 0 ,
116+ relationshipsDeleted: $ data ['counters ' ]['relationshipsDeleted ' ] ?? 0 ,
117+ labelsAdded: $ data ['counters ' ]['labelsAdded ' ] ?? 0 ,
118+ labelsRemoved: $ data ['counters ' ]['labelsRemoved ' ] ?? 0 ,
119+ indexesAdded: $ data ['counters ' ]['indexesAdded ' ] ?? 0 ,
120+ indexesRemoved: $ data ['counters ' ]['indexesRemoved ' ] ?? 0 ,
121+ constraintsAdded: $ data ['counters ' ]['constraintsAdded ' ] ?? 0 ,
122+ constraintsRemoved: $ data ['counters ' ]['constraintsRemoved ' ] ?? 0 ,
123+ containsSystemUpdates: $ data ['counters ' ]['containsSystemUpdates ' ] ?? false ,
124+ systemUpdates: $ data ['counters ' ]['systemUpdates ' ] ?? 0
125+ );
126+
127+ $ profile = isset ($ data ['profiledQueryPlan ' ]) ? $ this ->createProfileData ($ data ['profiledQueryPlan ' ]) : null ;
128+
129+ return new ResultSet (
130+ $ rows ,
131+ $ resultCounters ,
132+ new Bookmarks ($ data ['bookmarks ' ] ?? []),
133+ $ profile
134+ );
135+ }
136+
137+ private function handleException (RequestExceptionInterface $ e ): void
138+ {
139+ $ response = $ e ->getResponse ();
140+ if ($ response !== null ) {
141+ $ contents = $ response ->getBody ()->getContents ();
142+ $ errorResponse = json_decode ($ contents , true );
143+ throw Neo4jException::fromNeo4jResponse ($ errorResponse , $ e );
118144 }
145+ throw $ e ;
119146 }
120147
121148 public function beginTransaction (string $ database = 'neo4j ' ): Transaction
122149 {
123- $ response = $ this ->client ->post ( " /db/neo4j/query/v2/tx " );
150+ $ response = $ this ->client ->sendRequest ( new Request ( ' POST ' , ' /db/neo4j/query/v2/tx ' ) );
124151
125152 $ clusterAffinity = $ response ->getHeaderLine ('neo4j-cluster-affinity ' );
126153 $ responseData = json_decode ($ response ->getBody (), true );
@@ -133,16 +160,12 @@ private function createProfileData(array $data): ProfiledQueryPlan
133160 {
134161 $ ogm = new OGM ();
135162
136- // Map arguments using OGM
137- $ arguments = $ data ['arguments ' ];
138- $ mappedArguments = [];
139- foreach ($ arguments as $ key => $ value ) {
163+ $ mappedArguments = array_map (function ($ value ) use ($ ogm ) {
140164 if (is_array ($ value ) && array_key_exists ('$type ' , $ value ) && array_key_exists ('_value ' , $ value )) {
141- $ mappedArguments [$ key ] = $ ogm ->map ($ value );
142- } else {
143- $ mappedArguments [$ key ] = $ value ;
165+ return $ ogm ->map ($ value );
144166 }
145- }
167+ return $ value ;
168+ }, $ data ['arguments ' ] ?? []);
146169
147170 $ queryArguments = new ProfiledQueryPlanArguments (
148171 globalMemory: $ mappedArguments ['GlobalMemory ' ] ?? null ,
@@ -164,10 +187,9 @@ private function createProfileData(array $data): ProfiledQueryPlan
164187 id: $ mappedArguments ['Id ' ] ?? null ,
165188 estimatedRows: $ mappedArguments ['EstimatedRows ' ] ?? null ,
166189 planner: $ mappedArguments ['planner ' ] ?? null ,
167- rows: $ mappedArguments ['Rows ' ?? null ]
190+ rows: $ mappedArguments ['Rows ' ] ?? null
168191 );
169192
170- $ identifiers = $ data ['identifiers ' ] ?? [];
171193 $ profiledQueryPlan = new ProfiledQueryPlan (
172194 $ data ['dbHits ' ],
173195 $ data ['records ' ],
@@ -179,15 +201,13 @@ private function createProfileData(array $data): ProfiledQueryPlan
179201 $ data ['operatorType ' ],
180202 $ queryArguments ,
181203 children: [],
182- identifiers: $ identifiers
204+ identifiers: $ data [ ' identifiers ' ] ?? []
183205 );
184- // Process children recursively
185- foreach ($ data ['children ' ] as $ child ) {
186- $ childQueryPlan = $ this ->createProfileData ($ child );
187- $ profiledQueryPlan ->addChild ($ childQueryPlan );
206+
207+ foreach ($ data ['children ' ] ?? [] as $ child ) {
208+ $ profiledQueryPlan ->addChild ($ this ->createProfileData ($ child ));
188209 }
189210
190211 return $ profiledQueryPlan ;
191212 }
192-
193213}
0 commit comments