1 <?php
2
3 /**
4 * ArangoDB PHP client: export
5 *
6 * @package triagens\ArangoDb
7 * @author Jan Steemann
8 * @copyright Copyright 2015, triagens GmbH, Cologne, Germany
9 */
10
11 namespace triagens\ArangoDb;
12
13 /**
14 * Collection export
15 *
16 * @package triagens\ArangoDb
17 * @since 2.6
18 */
19 class Export
20 {
21 /**
22 * The connection object
23 *
24 * @var Connection
25 */
26 private $_connection;
27
28 /**
29 * The collection name or collection object
30 *
31 * @var mixed
32 */
33 private $_collection;
34
35 /**
36 * The current batch size (number of result documents retrieved per round-trip)
37 *
38 * @var mixed
39 */
40 private $_batchSize;
41
42 /**
43 * "flat" flag (if set, the query results will be treated as a simple array, not documents)
44 *
45 * @var bool
46 */
47 private $_flat = false;
48
49 /**
50 * Flush flag (if set, then all documents from the collection that are currently only
51 * in the write-ahead log (WAL) will be moved to the collection's datafiles. This may cause
52 * an initial delay in the export, but will lead to the documents in the WAL not being
53 * excluded from the export run. If the flush flag is set to false, the documents still
54 * in the WAL may be missing in the export result.
55 *
56 * @var bool
57 */
58 private $_flush = true;
59
60 /**
61 * The underlying collection type
62 */
63 private $_type;
64
65 /**
66 * export restrictions - either null for no restrictions or an array with a "type" and a "fields" index
67 *
68 * @var mixed
69 */
70 private $_restrictions;
71
72 /**
73 * optional limit for export - if specified and positive, will cap the amount of documents in the cursor to
74 * the specified value
75 *
76 * @var int
77 */
78 private $_limit = 0;
79
80 /**
81 * Count option index
82 */
83 const ENTRY_COUNT = 'count';
84
85 /**
86 * Batch size option index
87 */
88 const ENTRY_BATCHSIZE = 'batchSize';
89
90 /**
91 * Flush option index
92 */
93 const ENTRY_FLUSH = 'flush';
94
95 /**
96 * Export restrictions
97 */
98 const ENTRY_RESTRICT = 'restrict';
99
100 /**
101 * Optional limit for the number of documents
102 */
103 const ENTRY_LIMIT = 'limit';
104
105 /**
106 * Initialize the export
107 *
108 * @throws Exception
109 *
110 * @param Connection $connection - the connection to be used
111 * @param string $collection - the collection to export
112 * @param array $data - export options
113 */
114 public function __construct(Connection $connection, $collection, array $data = [])
115 {
116 $this->_connection = $connection;
117
118 if (!($collection instanceof Collection)) {
119 $collectionHandler = new CollectionHandler($this->_connection);
120 $collection = $collectionHandler->get($collection);
121 }
122 $this->_collection = $collection;
123
124 // check if we're working with an edge collection or not
125 $this->_type = $this->_collection->getType();
126
127 if (isset($data[self::ENTRY_FLUSH])) {
128 // set a default value
129 $this->_flush = $data[self::ENTRY_FLUSH];
130 }
131
132 if (isset($data[self::ENTRY_BATCHSIZE])) {
133 $this->setBatchSize($data[self::ENTRY_BATCHSIZE]);
134 }
135
136 if (isset($data[self::ENTRY_LIMIT])) {
137 $this->_limit = (int) $data[self::ENTRY_LIMIT];
138 }
139
140 if (isset($data[self::ENTRY_RESTRICT]) &&
141 is_array($data[self::ENTRY_RESTRICT])
142 ) {
143 $restrictions = $data[self::ENTRY_RESTRICT];
144
145 if (!isset($restrictions['type']) ||
146 !in_array($restrictions['type'], ['include', 'exclude'], true)
147 ) {
148 // validate restrictions.type
149 throw new ClientException('Invalid restrictions type definition');
150 }
151
152 if (!isset($restrictions['fields']) ||
153 !is_array($restrictions['fields'])
154 ) {
155 // validate restrictions.fields
156 throw new ClientException('Invalid restrictions fields definition');
157 }
158
159 // all valid
160 $this->_restrictions = $restrictions;
161 }
162
163 if (isset($data[ExportCursor::ENTRY_FLAT])) {
164 $this->_flat = (bool) $data[ExportCursor::ENTRY_FLAT];
165 }
166 }
167
168 /**
169 * Return the connection object
170 *
171 * @return Connection - the connection object
172 */
173 protected function getConnection()
174 {
175 return $this->_connection;
176 }
177
178 /**
179 * Execute the export
180 *
181 * This will return the results as a Cursor. The cursor can then be used to iterate the results.
182 *
183 * @throws Exception
184 * @return ExportCursor
185 */
186 public function execute()
187 {
188 $data = [
189 self::ENTRY_FLUSH => $this->_flush,
190 self::ENTRY_COUNT => true
191 ];
192
193 if ($this->_batchSize > 0) {
194 $data[self::ENTRY_BATCHSIZE] = $this->_batchSize;
195 }
196
197 if ($this->_limit > 0) {
198 $data[self::ENTRY_LIMIT] = $this->_limit;
199 }
200
201 if (is_array($this->_restrictions)) {
202 $data[self::ENTRY_RESTRICT] = $this->_restrictions;
203 }
204
205 $collection = $this->_collection;
206 if ($collection instanceof Collection) {
207 $collection = $collection->getName();
208 }
209
210 $url = UrlHelper::appendParamsUrl(Urls::URL_EXPORT, ['collection' => $collection]);
211 $response = $this->_connection->post($url, $this->getConnection()->json_encode_wrapper($data));
212
213 return new ExportCursor($this->_connection, $response->getJson(), $this->getCursorOptions());
214 }
215
216 /**
217 * Set the batch size for the export
218 *
219 * The batch size is the number of results to be transferred
220 * in one server round-trip. If an export produces more documents
221 * than the batch size, it creates a server-side cursor that
222 * provides the additional results.
223 *
224 * The server-side cursor can be accessed by the client with subsequent HTTP requests.
225 *
226 * @throws ClientException
227 *
228 * @param int $value - batch size value
229 *
230 * @return void
231 */
232 public function setBatchSize($value)
233 {
234 if (!is_int($value) || (int) $value <= 0) {
235 throw new ClientException('Batch size should be a positive integer');
236 }
237
238 $this->_batchSize = (int) $value;
239 }
240
241 /**
242 * Get the batch size for the export
243 *
244 * @return int - current batch size value
245 */
246 public function getBatchSize()
247 {
248 return $this->_batchSize;
249 }
250
251 /**
252 * Return an array of cursor options
253 *
254 * @return array - array of options
255 */
256 private function getCursorOptions()
257 {
258 $result = [
259 ExportCursor::ENTRY_FLAT => (bool) $this->_flat,
260 ExportCursor::ENTRY_BASEURL => Urls::URL_EXPORT,
261 ExportCursor::ENTRY_TYPE => $this->_type
262 ];
263
264 return $result;
265 }
266 }
267