Skip to content

Commit

Permalink
Merge pull request #34 from jupitern/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
jupitern committed Apr 4, 2024
2 parents a5b83a8 + 9ee9426 commit a257d71
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 84 deletions.
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Include jupitern/cosmosdb in your project, by adding it to your composer.json fi

## Changelog

### v2.7.0
- adding support for PATCH verb (CosmosDb add-on PATCH API) with Add, Set, Replace, Remove, Increment and Move operations, including "getPatchOp[OP]" helper functions

### v2.6.0
- code refactor. min PHP verion supported is now 8.0
- selectCollection no longer creates a colletion if not exist. use createCollection for that
Expand All @@ -43,7 +46,8 @@ Include jupitern/cosmosdb in your project, by adding it to your composer.json fi
This package adds additional functionalities to the [AzureDocumentDB-PHP](https://github.com/cocteau666/AzureDocumentDB-PHP) package. All other functionality exists in this package as well.

## Limitations
Use of `limit()` or `order()` in cross-partition queries is currently not supported.
- Use of `limit()` or `order()` in cross-partition queries is currently not supported.
- Multi-document patch using transaction based requests is currently not supported.

## Usage

Expand Down Expand Up @@ -90,6 +94,52 @@ $rid = \Jupitern\CosmosDb\QueryBuilder::instance()
]);
```

### Patching Records

```php

# Patch operations: ADD | SET | REPLACE | REMOVE | INCR | MOVE
# https://learn.microsoft.com/en-us/azure/cosmos-db/partial-document-update#similarities-and-differences

# Where a PartitionKey is in use, the PartitionValue should be set on the QueryBuilder instance
# prior to making any PATCH operations, as by the nature of PATCH, there is no document body to find the value in,
# and the value is taken from the class property when the request is made. $rid_doc is also required because PATCH is an item-level operation.

# Example starting document (as array)

# [
# "_rid" => $rid,
# 'id' => '2',
# 'name' => 'Jane Doe Something',
# 'age' => 36,
# 'country' => 'Portugal'
# ]

$res = \Jupitern\CosmosDb\QueryBuilder::instance()
->setCollection($collection)
->setPartitionKey('country')
->setPartitionValue('Portugal');

# Patch operations can be batched, so the $operations argument is an array of arrays
# Batch patch operations are limited to 10 operations per request
$operations[] = $res->getPatchOpSet('/age', 38);
$operations[] = $res->getPatchOpAdd('/region' 'Algarve');

$rid_doc = $res->patch($rid_doc, $operations);

# Example patched document (as array)

# [
# "_rid" => $rid,
# 'id' => '2',
# 'name' => 'Jane Doe Something',
# 'age' => 38,
# 'country' => 'Portugal'
# 'region' => 'Algarve'
# ]

```

### Querying Records

```php
Expand Down
91 changes: 59 additions & 32 deletions src/CosmosDb.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function setHttpClientOptions(array $options = [])
*
* @link http://msdn.microsoft.com/en-us/library/azure/dn783368.aspx
* @access private
* @param string $verb Request Method (GET, POST, PUT, DELETE)
* @param string $verb Request Method (GET, POST, PUT, DELETE, PATCH)
* @param string $resource_type Resource Type
* @param string $resource_id Resource ID
* @return array of Request Headers
Expand Down Expand Up @@ -151,20 +151,20 @@ public function getInfo(): string
* @return array JSON response
* @throws GuzzleException
*/
public function query(string $rid_id, string $rid_col, string $query, bool $isCrossPartition = false, $partitionValue = null): array
{
public function query(string $rid_id, string $rid_col, string $query, bool $isCrossPartition = false, $partitionValue = null): array
{
$headers = $this->getAuthHeaders('POST', 'docs', $rid_col);
$headers['Content-Length'] = strlen($query);
$headers['Content-Type'] = 'application/query+json';
$headers['x-ms-max-item-count'] = -1;
$headers['x-ms-documentdb-isquery'] = 'True';

if ($isCrossPartition) {
$headers['x-ms-documentdb-query-enablecrosspartition'] = 'True';
}

if ($partitionValue) {
$headers['x-ms-documentdb-partitionkey'] = '["'.$partitionValue.'"]';
$headers['x-ms-documentdb-partitionkey'] = '["' . $partitionValue . '"]';
}
/*
* Fix for https://github.com/jupitern/cosmosdb/issues/21 (credits to https://github.com/ElvenSpellmaker).
Expand All @@ -186,8 +186,8 @@ public function query(string $rid_id, string $rid_col, string $query, bool $isCr
$result = $this->request("/dbs/{$rid_id}/colls/{$rid_col}/docs", "POST", $headers, $query);
$results[] = $result->getBody()->getContents();
}
}
catch (\GuzzleHttp\Exception\ClientException $e) {
}
catch (\GuzzleHttp\Exception\ClientException $e) {
$responseError = \json_decode($e->getResponse()->getBody()->getContents());

// -- Retry the request with PK Ranges --
Expand Down Expand Up @@ -220,14 +220,14 @@ public function query(string $rid_id, string $rid_col, string $query, bool $isCr
* @return mixed
* @throws GuzzleException
*/
public function getPkRanges(string $rid_id, string $rid_col): mixed
public function getPkRanges(string $rid_id, string $rid_col): mixed
{
$headers = $this->getAuthHeaders('GET', 'pkranges', $rid_col);
$headers['Accept'] = 'application/json';
$headers['x-ms-max-item-count'] = -1;
$result = $this->request("/dbs/{$rid_id}/colls/{$rid_col}/pkranges", "GET", $headers);
return json_decode($result->getBody()->getContents());
}
$headers = $this->getAuthHeaders('GET', 'pkranges', $rid_col);
$headers['Accept'] = 'application/json';
$headers['x-ms-max-item-count'] = -1;
$result = $this->request("/dbs/{$rid_id}/colls/{$rid_col}/pkranges", "GET", $headers);
return json_decode($result->getBody()->getContents());
}

/**
* getPkFullRange
Expand All @@ -238,12 +238,12 @@ public function getPkRanges(string $rid_id, string $rid_col): mixed
* @return string
* @throws GuzzleException
*/
public function getPkFullRange($rid_id, $rid_col): string
public function getPkFullRange($rid_id, $rid_col): string
{
$result = $this->getPkRanges($rid_id, $rid_col);
$ids = \array_column($result->PartitionKeyRanges, "id");
return $result->_rid . "," . \implode(",", $ids);
}
$result = $this->getPkRanges($rid_id, $rid_col);
$ids = \array_column($result->PartitionKeyRanges, "id");
return $result->_rid . "," . \implode(",", $ids);
}

/**
* listDatabases
Expand Down Expand Up @@ -534,7 +534,7 @@ public function createDocument(string $rid_id, string $rid_col, string $json, st
$headers = \array_merge($headers, $authHeaders);
$headers['Content-Length'] = strlen($json);
if ($partitionKey !== null) {
$headers['x-ms-documentdb-partitionkey'] = '["'.$partitionKey.'"]';
$headers['x-ms-documentdb-partitionkey'] = '["' . $partitionKey . '"]';
}

return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/docs", "POST", $headers, $json)->getBody()->getContents();
Expand All @@ -560,12 +560,39 @@ public function replaceDocument(string $rid_id, string $rid_col, string $rid_doc
$headers = \array_merge($headers, $authHeaders);
$headers['Content-Length'] = strlen($json);
if ($partitionKey !== null) {
$headers['x-ms-documentdb-partitionkey'] = '["'.$partitionKey.'"]';
$headers['x-ms-documentdb-partitionkey'] = '["' . $partitionKey . '"]';
}

return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/docs/{$rid_doc}", "PUT", $headers, $json)->getBody()->getContents();
}

/**
* patchDocument
*
* @link https://learn.microsoft.com/en-us/azure/cosmos-db/partial-document-update#rest-api-reference-for-partial-document-update
* @access public
* @param string $rid_id Resource ID
* @param string $rid_col Resource Collection ID
* @param string $rid_doc Resource Doc ID
* @param string $json JSON request
* @param string|null $partitionKey
* @param array $headers Optional headers to send along with the request
* @return string JSON response
* @throws GuzzleException
*/
public function patchDocument(string $rid_id, string $rid_col, string $rid_doc, string $json, string $partitionKey = null, array $headers = []): string
{
$authHeaders = $this->getAuthHeaders('PATCH', 'docs', $rid_doc);
$headers = \array_merge($headers, $authHeaders);
$headers['Content-Length'] = strlen($json);
$headers['Content-Type'] = 'application/json_patch+json';
if ($partitionKey !== null) {
$headers['x-ms-documentdb-partitionkey'] = '["' . $partitionKey . '"]';
}

return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/docs/{$rid_doc}", "PATCH", $headers, $json)->getBody()->getContents();
}

/**
* deleteDocument
*
Expand All @@ -585,7 +612,7 @@ public function deleteDocument(string $rid_id, string $rid_col, string $rid_doc,
$headers = \array_merge($headers, $authHeaders);
$headers['Content-Length'] = '0';
if ($partitionKey !== null) {
$headers['x-ms-documentdb-partitionkey'] = '["'.$partitionKey.'"]';
$headers['x-ms-documentdb-partitionkey'] = '["' . $partitionKey . '"]';
}

return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/docs/{$rid_doc}", "DELETE", $headers)->getBody()->getContents();
Expand Down Expand Up @@ -935,7 +962,7 @@ public function replaceStoredProcedure(string $rid_id, string $rid_col, string $
* @throws GuzzleException
*/
public function deleteStoredProcedure(string $rid_id, string $rid_col, string $rid_sproc): string
{
{
$headers = $this->getAuthHeaders('DELETE', 'sprocs', $rid_sproc);
$headers['Content-Length'] = '0';
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/sprocs/{$rid_sproc}", "DELETE", $headers)->getBody()->getContents();
Expand All @@ -952,7 +979,7 @@ public function deleteStoredProcedure(string $rid_id, string $rid_col, string $r
* @throws GuzzleException
*/
public function listUserDefinedFunctions(string $rid_id, string $rid_col): string
{
{
$headers = $this->getAuthHeaders('GET', 'udfs', $rid_col);
$headers['Content-Length'] = '0';
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/udfs", "GET", $headers)->getBody()->getContents();
Expand All @@ -970,7 +997,7 @@ public function listUserDefinedFunctions(string $rid_id, string $rid_col): strin
* @throws GuzzleException
*/
public function createUserDefinedFunction(string $rid_id, string $rid_col, string $json): string
{
{
$headers = $this->getAuthHeaders('POST', 'udfs', $rid_col);
$headers['Content-Length'] = strlen($json);
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/udfs", "POST", $headers, $json)->getBody()->getContents();
Expand All @@ -989,7 +1016,7 @@ public function createUserDefinedFunction(string $rid_id, string $rid_col, strin
* @throws GuzzleException
*/
public function replaceUserDefinedFunction(string $rid_id, string $rid_col, string $rid_udf, string $json): string
{
{
$headers = $this->getAuthHeaders('PUT', 'udfs', $rid_udf);
$headers['Content-Length'] = strlen($json);
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/udfs/{$rid_udf}", "PUT", $headers, $json)->getBody()->getContents();
Expand All @@ -1007,7 +1034,7 @@ public function replaceUserDefinedFunction(string $rid_id, string $rid_col, stri
* @throws GuzzleException
*/
public function deleteUserDefinedFunction(string $rid_id, string $rid_col, string $rid_udf): string
{
{
$headers = $this->getAuthHeaders('DELETE', 'udfs', $rid_udf);
$headers['Content-Length'] = '0';
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/udfs/{$rid_udf}", "DELETE", $headers)->getBody()->getContents();
Expand All @@ -1024,7 +1051,7 @@ public function deleteUserDefinedFunction(string $rid_id, string $rid_col, strin
* @throws GuzzleException
*/
public function listTriggers(string $rid_id, string $rid_col): string
{
{
$headers = $this->getAuthHeaders('GET', 'triggers', $rid_col);
$headers['Content-Length'] = '0';
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/triggers", "GET", $headers)->getBody()->getContents();
Expand All @@ -1042,7 +1069,7 @@ public function listTriggers(string $rid_id, string $rid_col): string
* @throws GuzzleException
*/
public function createTrigger(string $rid_id, string $rid_col, string $json): string
{
{
$headers = $this->getAuthHeaders('POST', 'triggers', $rid_col);
$headers['Content-Length'] = strlen($json);
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/triggers", "POST", $headers, $json)->getBody()->getContents();
Expand All @@ -1061,7 +1088,7 @@ public function createTrigger(string $rid_id, string $rid_col, string $json): st
* @throws GuzzleException
*/
public function replaceTrigger(string $rid_id, string $rid_col, string $rid_trigger, string $json): string
{
{
$headers = $this->getAuthHeaders('PUT', 'triggers', $rid_trigger);
$headers['Content-Length'] = strlen($json);
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/triggers/{$rid_trigger}", "PUT", $headers, $json)->getBody()->getContents();
Expand All @@ -1079,7 +1106,7 @@ public function replaceTrigger(string $rid_id, string $rid_col, string $rid_trig
* @throws GuzzleException
*/
public function deleteTrigger(string $rid_id, string $rid_col, string $rid_trigger): string
{
{
$headers = $this->getAuthHeaders('DELETE', 'triggers', $rid_trigger);
$headers['Content-Length'] = '0';
return $this->request("/dbs/{$rid_id}/colls/{$rid_col}/triggers/{$rid_trigger}", "DELETE", $headers)->getBody()->getContents();
Expand Down
59 changes: 37 additions & 22 deletions src/CosmosDbCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,35 @@ public function query($query, $params = [], $isCrossPartition = false, $partitio
{
$paramsJson = [];
foreach ($params as $key => $val) {
$val = is_int($val) || is_float($val) ? $val : '"'. str_replace('"', '\\"', $val) .'"';
$val = is_int($val) || is_float($val) ? $val : '"' . str_replace('"', '\\"', $val) . '"';

$paramsJson[] = '{"name": "' . str_replace('"', '\\"', $key) . '", "value": '.$val.'}';
$paramsJson[] = '{"name": "' . str_replace('"', '\\"', $key) . '", "value": ' . $val . '}';
}

$query = '{"query": "' . str_replace('"', '\\"', $query) . '", "parameters": [' . implode(',', $paramsJson) . ']}';

return $this->document_db->query($this->rid_db, $this->rid_col, $query, $isCrossPartition, $partitionValue);
}

/**
* getPkRanges
*
* @return mixed
*/
public function getPkRanges()
{
return $this->document_db->getPkRanges($this->rid_db, $this->rid_col);
}

/**
* getPkFullRange
*
* @return mixed
*/
public function getPkFullRange()
{
return $this->document_db->getPkFullRange($this->rid_db, $this->rid_col);
}
/**
* getPkRanges
*
* @return mixed
*/
public function getPkRanges()
{
return $this->document_db->getPkRanges($this->rid_db, $this->rid_col);
}

/**
* getPkFullRange
*
* @return mixed
*/
public function getPkFullRange()
{
return $this->document_db->getPkFullRange($this->rid_db, $this->rid_col);
}

/**
* createDocument
Expand Down Expand Up @@ -94,6 +94,21 @@ public function replaceDocument($rid, $json, $partitionKey = null, array $header
return $this->document_db->replaceDocument($this->rid_db, $this->rid_col, $rid, $json, $partitionKey, $headers);
}

/**
* patchDocument
*
* @access public
* @param string $rid document ResourceID (_rid)
* @param string $json JSON formatted operations array
* @param string $partitionKey
* @param array $headers Optional headers to send along with the request
* @return string JSON strings
*/
public function patchDocument($rid_doc, $operations, $partitionKey = null, array $headers = [])
{
return $this->document_db->patchDocument($this->rid_db, $this->rid_col, $rid_doc, $operations, $partitionKey, $headers);
}

/**
* deleteDocument
*
Expand Down Expand Up @@ -134,7 +149,7 @@ public function getPermission($uid, $pid)
return $this->document_db->getPermission($this->rid_db, $uid, $pid);
}
*/

public function listStoredProcedures()
{
return $this->document_db->listStoredProcedures($this->rid_db, $this->rid_col);
Expand Down
Loading

0 comments on commit a257d71

Please sign in to comment.