2
2
3
3
namespace Neo4j \QueryAPI ;
4
4
5
- use Exception ;
6
5
use GuzzleHttp \Client ;
7
- use GuzzleHttp \Exception \GuzzleException ;
8
6
use GuzzleHttp \Exception \RequestException ;
9
- use InvalidArgumentException ;
10
- use Neo4j \QueryAPI \Objects \ChildQueryPlan ;
11
- use Neo4j \QueryAPI \Objects \QueryArguments ;
12
- use Neo4j \QueryAPI \Objects \ResultCounters ;
13
- use Neo4j \QueryAPI \Objects \ProfiledQueryPlan ;
14
- use Neo4j \QueryAPI \Results \ResultRow ;
7
+ use Neo4j \QueryAPI \Enums \AccessMode ;
8
+ use Neo4j \QueryAPI \Objects \Bookmarks ;
15
9
use Neo4j \QueryAPI \Results \ResultSet ;
16
10
use Neo4j \QueryAPI \Exception \Neo4jException ;
17
11
use Psr \Http \Client \RequestExceptionInterface ;
18
- use RuntimeException ;
19
- use stdClass ;
20
- use Neo4j \QueryAPI \Objects \Bookmarks ;
21
- use Neo4j \QueryAPI \Enums \AccessMode ;
22
-
12
+ use Psr \Http \Message \ResponseInterface ;
23
13
24
14
class Neo4jQueryAPI
25
15
{
26
-
27
16
private Client $ client ;
17
+ private Configuration $ config ;
18
+ private ResponseParser $ responseParser ;
28
19
29
- public function __construct (Client $ client )
20
+ public function __construct (Configuration $ config , ResponseParser $ responseParser )
30
21
{
31
- $ this ->client = $ client ;
22
+ $ this ->config = $ config ;
23
+ $ this ->responseParser = $ responseParser ;
24
+
25
+ $ this ->client = new Client ([
26
+ 'base_uri ' => rtrim ($ this ->config ->getBaseUrl (), '/ ' ),
27
+ 'timeout ' => 10.0 ,
28
+ 'headers ' => $ this ->config ->getDefaultHeaders (),
29
+ ]);
32
30
}
31
+
33
32
/**
34
- * @api
33
+ * Static method to create an instance with login details.
35
34
*/
36
35
public static function login (string $ address , string $ username , string $ password ): self
37
36
{
37
+ $ authToken = base64_encode ("$ username: $ password " );
38
+ $ config = (new Configuration ())
39
+ ->setBaseUrl ($ address )
40
+ ->setAuthToken ($ authToken );
38
41
39
-
40
- $ client = new Client ([
41
- 'base_uri ' => rtrim ($ address , '/ ' ),
42
- 'timeout ' => 10.0 ,
43
- 'headers ' => [
44
- 'Authorization ' => 'Basic ' . base64_encode ("$ username: $ password " ),
45
- 'Content-Type ' => 'application/vnd.neo4j.query ' ,
46
- 'Accept ' => 'application/vnd.neo4j.query ' ,
47
- ],
48
- ]);
49
-
50
- return new self ($ client );
42
+ return new self ($ config , new ResponseParser (new OGM ()));
51
43
}
52
44
53
45
/**
54
- * @throws Neo4jException
55
- * @throws RequestExceptionInterface
56
- * @api
46
+ * Executes a Cypher query.
47
+ *
48
+ * @throws Neo4jException|RequestExceptionInterface
57
49
*/
58
50
public function run (string $ cypher , array $ parameters = [], string $ database = 'neo4j ' , Bookmarks $ bookmark = null , ?string $ impersonatedUser = null , AccessMode $ accessMode = AccessMode::WRITE ): ResultSet
59
51
{
60
52
try {
61
53
$ payload = [
62
54
'statement ' => $ cypher ,
63
- 'parameters ' => empty ($ parameters ) ? new stdClass () : $ parameters ,
55
+ 'parameters ' => empty ($ parameters ) ? new \ stdClass () : $ parameters ,
64
56
'includeCounters ' => true ,
65
57
'accessMode ' => $ accessMode ->value ,
66
58
];
67
59
68
-
69
60
if ($ bookmark !== null ) {
70
61
$ payload ['bookmarks ' ] = $ bookmark ->getBookmarks ();
71
62
}
63
+
72
64
if ($ impersonatedUser !== null ) {
73
65
$ payload ['impersonatedUser ' ] = $ impersonatedUser ;
74
66
}
75
67
76
- $ response = $ this ->client ->post ('/db/ ' . $ database . '/query/v2 ' , [
77
- 'json ' => $ payload ,
78
- ]);
68
+ $ response = $ this ->client ->post ("/db/ {$ database }/query/v2 " , ['json ' => $ payload ]);
79
69
80
- $ data = json_decode ($ response ->getBody ()->getContents (), true );
81
-
82
- $ ogm = new OGM ();
83
-
84
- $ keys = $ data ['data ' ]['fields ' ];
85
- $ values = $ data ['data ' ]['values ' ];
86
-
87
- $ rows = array_map (function ($ resultRow ) use ($ ogm , $ keys ) {
88
- $ data = [];
89
- foreach ($ keys as $ index => $ key ) {
90
- $ fieldData = $ resultRow [$ index ] ?? null ;
91
- $ data [$ key ] = $ ogm ->map ($ fieldData );
92
- }
93
- return new ResultRow ($ data );
94
- }, $ values );
95
-
96
- $ profile = null ;
97
- if (isset ($ data ['profiledQueryPlan ' ])) {
98
- $ profile = $ this ->createProfileData ($ data ['profiledQueryPlan ' ]);
99
- }
100
-
101
- $ resultCounters = new ResultCounters (
102
- containsUpdates: $ data ['counters ' ]['containsUpdates ' ] ?? false ,
103
- nodesCreated: $ data ['counters ' ]['nodesCreated ' ] ?? 0 ,
104
- nodesDeleted: $ data ['counters ' ]['nodesDeleted ' ] ?? 0 ,
105
- propertiesSet: $ data ['counters ' ]['propertiesSet ' ] ?? 0 ,
106
- relationshipsCreated: $ data ['counters ' ]['relationshipsCreated ' ] ?? 0 ,
107
- relationshipsDeleted: $ data ['counters ' ]['relationshipsDeleted ' ] ?? 0 ,
108
- labelsAdded: $ data ['counters ' ]['labelsAdded ' ] ?? 0 ,
109
- labelsRemoved: $ data ['counters ' ]['labelsRemoved ' ] ?? 0 ,
110
- indexesAdded: $ data ['counters ' ]['indexesAdded ' ] ?? 0 ,
111
- indexesRemoved: $ data ['counters ' ]['indexesRemoved ' ] ?? 0 ,
112
- constraintsAdded: $ data ['counters ' ]['constraintsAdded ' ] ?? 0 ,
113
- constraintsRemoved: $ data ['counters ' ]['constraintsRemoved ' ] ?? 0 ,
114
- containsSystemUpdates: $ data ['counters ' ]['containsSystemUpdates ' ] ?? false ,
115
- systemUpdates: $ data ['counters ' ]['systemUpdates ' ] ?? 0
116
- );
117
-
118
- return new ResultSet (
119
- $ rows ,
120
- $ resultCounters ,
121
- new Bookmarks ($ data ['bookmarks ' ] ?? []),
122
- $ profile ,
123
- $ accessMode
124
- );
125
- } catch (RequestExceptionInterface $ e ) {
126
- error_log ("Request Exception: " . $ e ->getMessage ());
127
-
128
- $ response = $ e ->getResponse ();
129
- if ($ response !== null ) {
130
- $ contents = $ response ->getBody ()->getContents ();
131
- $ errorResponse = json_decode ($ contents , true );
132
- throw Neo4jException::fromNeo4jResponse ($ errorResponse , $ e );
133
- }
134
-
135
-
136
- throw new Neo4jException (['message ' => $ e ->getMessage ()], 500 , $ e );
70
+ return $ this ->responseParser ->parseRunQueryResponse ($ response );
71
+ } catch (RequestException $ e ) {
72
+ $ this ->handleRequestException ($ e );
137
73
}
138
74
}
139
75
140
-
141
-
142
76
/**
143
- * @api
77
+ * Starts a transaction.
144
78
*/
145
79
public function beginTransaction (string $ database = 'neo4j ' ): Transaction
146
80
{
147
- unset($ database );
148
- $ response = $ this ->client ->post ("/db/neo4j/query/v2/tx " );
81
+ $ response = $ this ->client ->post ("/db/ {$ database }/query/v2/tx " );
149
82
150
83
$ clusterAffinity = $ response ->getHeaderLine ('neo4j-cluster-affinity ' );
151
84
$ responseData = json_decode ($ response ->getBody (), true );
@@ -154,49 +87,19 @@ public function beginTransaction(string $database = 'neo4j'): Transaction
154
87
return new Transaction ($ this ->client , $ clusterAffinity , $ transactionId );
155
88
}
156
89
157
- private function createProfileData (array $ data ): ProfiledQueryPlan
90
+ /**
91
+ * Handles request exceptions by parsing error details and throwing a Neo4jException.
92
+ *
93
+ * @throws Neo4jException
94
+ */
95
+ private function handleRequestException (RequestExceptionInterface $ e ): void
158
96
{
159
- $ arguments = $ data ['arguments ' ];
160
-
161
- $ queryArguments = new QueryArguments (
162
- $ arguments ['globalMemory ' ] ?? 0 ,
163
- $ arguments ['plannerImpl ' ] ?? '' ,
164
- $ arguments ['memory ' ] ?? 0 ,
165
- $ arguments ['stringRepresentation ' ] ?? '' ,
166
- is_string ($ arguments ['runtime ' ] ?? '' ) ? $ arguments ['runtime ' ] : json_encode ($ arguments ['runtime ' ]),
167
- $ arguments ['runtimeImpl ' ] ?? '' ,
168
- $ arguments ['dbHits ' ] ?? 0 ,
169
- $ arguments ['batchSize ' ] ?? 0 ,
170
- $ arguments ['details ' ] ?? '' ,
171
- $ arguments ['plannerVersion ' ] ?? '' ,
172
- $ arguments ['pipelineInfo ' ] ?? '' ,
173
- $ arguments ['runtimeVersion ' ] ?? '' ,
174
- $ arguments ['id ' ] ?? 0 ,
175
- $ arguments ['estimatedRows ' ] ?? 0.0 ,
176
- is_string ($ arguments ['planner ' ] ?? '' ) ? $ arguments ['planner ' ] : json_encode ($ arguments ['planner ' ]),
177
- $ arguments ['rows ' ] ?? 0
178
- );
179
-
180
- $ profiledQueryPlan = new ProfiledQueryPlan (
181
- $ data ['dbHits ' ],
182
- $ data ['records ' ],
183
- $ data ['hasPageCacheStats ' ],
184
- $ data ['pageCacheHits ' ],
185
- $ data ['pageCacheMisses ' ],
186
- $ data ['pageCacheHitRatio ' ],
187
- $ data ['time ' ],
188
- $ data ['operatorType ' ],
189
- $ queryArguments
190
- );
191
-
192
- foreach ($ data ['children ' ] as $ child ) {
193
- $ childQueryPlan = $ this ->createProfileData ($ child );
194
-
195
- $ profiledQueryPlan ->addChild ($ childQueryPlan );
97
+ $ response = $ e ->getResponse ();
98
+ if ($ response instanceof ResponseInterface) {
99
+ $ errorResponse = json_decode ((string )$ response ->getBody (), true );
100
+ throw Neo4jException::fromNeo4jResponse ($ errorResponse , $ e );
196
101
}
197
102
198
- return $ profiledQueryPlan ;
103
+ throw new Neo4jException ([ ' message ' => $ e -> getMessage ()], 500 , $ e ) ;
199
104
}
200
-
201
-
202
105
}
0 commit comments