Skip to content

Commit

Permalink
Merge pull request #8 from byjg/4.9
Browse files Browse the repository at this point in the history
Upgrade MongoDB support to version 7.0
Add `KeyValueInterface::rename($oldKey, $newKey)`
Add `KeyValueInterface::has($key)`
  • Loading branch information
byjg committed Jun 4, 2024
2 parents 610b1a4 + 7dd3602 commit 36b5706
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 57 deletions.
1 change: 1 addition & 0 deletions .run/Start Backends for Test.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<configuration default="false" name="Start Backends for Test" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker">
<deployment type="docker-compose.yml">
<settings>
<option name="envFilePath" value="" />
<option name="removeVolumesOnComposeDown" value="true" />
<option name="sourceFilePath" value="docker-compose.yml" />
</settings>
Expand Down
2 changes: 1 addition & 1 deletion .run/Test Project.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<CommandLine>
<envs>
<env name="DYNAMODB_CONNECTION" value="dynamodb://aaa:123456@us-east-1/tablename?endpoint=http://127.0.0.1:8000" />
<env name="MONGODB_CONNECTION" value="mongodb://127.0.0.1/test" />
<env name="MONGODB_CONNECTION" value="mongodb://rootuser:rootpass@127.0.0.1/test" />
<env name="S3_CONNECTION" value="s3://aaa:12345678@us-east-1/mybucket?create=true&amp;endpoint=http://localhost:4566" />
</envs>
</CommandLine>
Expand Down
8 changes: 8 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"ByJG\\AnyDataset\\NoSql\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"prefer-stable": true,
"minimum-stability": "dev",
"require": {
Expand All @@ -25,5 +30,8 @@
"ext-mongodb": "*",
"byjg/cache-engine": "4.9.*"
},
"provide": {
"byjg/anydataset-implementation": "1.0"
},
"license": "MIT"
}
5 changes: 3 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ services:

mongodb:
container_name: anydataset_nosql_mongodb
image: mongo:3
image: mongo:7
environment:
TZ: America/Winnipeg
MONGO_INITDB_ROOT_USERNAME: rootuser
MONGO_INITDB_ROOT_PASSWORD: rootpass
ports:
- "27017:27017"

Expand Down
10 changes: 10 additions & 0 deletions docs/AwsDynamoDbKeyValue.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ $iterator = $dynamodb->getIterator($options);
print_r($iterator->toArray());
```

### Check if a key exists

```php
<?php
$dynamodb = \ByJG\AnyDataset\NoSql\Factory::getInstance('dynamodb://....');
if ($dynamodb->has(1201)) {
echo "exist!";
}
```

## Further reading

- [https://docs.aws.amazon.com/aws-sdk-php/v2/guide/service-dynamodb.html](https://docs.aws.amazon.com/aws-sdk-php/v2/guide/service-dynamodb.html)
Expand Down
17 changes: 17 additions & 0 deletions docs/AwsS3KeyValue.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,22 @@ while (strlen($data) <= $size) {
}
```

## Rename a key

```php
<?php
$s3 = \ByJG\AnyDataset\NoSql\Factory::getInstance('s3://....');
$s3->rename("object_name", "new_object_name");
```

## Check if a key exists

```php
<?php
$s3 = \ByJG\AnyDataset\NoSql\Factory::getInstance('s3://....');
if ($s3->has("object_name")) {
echo "exist!";
}
```
----
[Open source ByJG](http://opensource.byjg.com)
14 changes: 14 additions & 0 deletions docs/MongoDB.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,19 @@ foreach ($result as $document)
}
```


### Full Connection String

```text
mongodb://username:password@server:27017/dbname?uri.option1=value1&driver.option2=value2
```

The list of parameters can be found in the [PHP MongoDB Driver documentation](https://www.php.net/manual/en/mongodb-driver-manager.construct.php).

Parameters started with `uri.` are passed to the MongoDB URI connection string.
Parameters started with `driver.` are passed to the MongoDB driver connection string.
Any other parameters will throw an exception.


----
[Open source ByJG](http://opensource.byjg.com)
16 changes: 16 additions & 0 deletions src/AwsDynamoDbDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Aws\DynamoDb\DynamoDbClient;
use Aws\Result;
use ByJG\AnyDataset\Core\Exception\NotImplementedException;
use ByJG\AnyDataset\Core\GenericIterator;
use ByJG\AnyDataset\Lists\ArrayDataset;
use ByJG\Serializer\SerializerObject;
Expand Down Expand Up @@ -115,6 +116,10 @@ protected function extractRecord($awsResult) {
$raw = $awsResult["Item"];
}

if (empty($raw)) {
return null;
}

array_walk($raw, function($val, $key) use (&$result) {
$value = null;
if (isset($val["N"])) {
Expand Down Expand Up @@ -233,4 +238,15 @@ public static function schema()
{
return ["dynamo", "dynamodb"];
}

public function rename($oldKey, $newKey)
{
throw new NotImplementedException("DynamoDB cannot rename");
}

public function has($key, $options = [])
{
$value = $this->get($key, $options);
return !empty($value);
}
}
17 changes: 17 additions & 0 deletions src/AwsS3Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,21 @@ public static function schema()
{
return "s3";
}

public function rename($oldKey, $newKey)
{
$data = [
'Bucket' => $this->bucket,
'Key' => $newKey,
'CopySource' => "{$this->bucket}/{$oldKey}",
];

$this->s3Client->copyObject($data);
$this->remove($oldKey);
}

public function has($key, $options = [])
{
return $this->s3Client->doesObjectExistV2($this->bucket, $key, false, $options);
}
}
10 changes: 10 additions & 0 deletions src/CloudflareKV.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,14 @@ public static function schema()
{
return "kv";
}

public function rename($oldKey, $newKey)
{
// TODO: Implement rename() method.
}

public function has($key, $options = [])
{
// TODO: Implement has() method.
}
}
4 changes: 4 additions & 0 deletions src/KeyValueInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface KeyValueInterface
*/
public function getIterator($options = []);

public function has($key, $options = []);

public function get($key, $options = []);

public function put($key, $value, $options = []);
Expand All @@ -35,4 +37,6 @@ public function removeBatch($keys, $options = []);

public function getDbConnection();

public function rename($oldKey, $newKey);

}
115 changes: 77 additions & 38 deletions src/MongoDbDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class MongoDbDriver implements NoSqlInterface, RegistrableInterface

protected $database;

protected $idField;

/**
* Creates a new MongoDB connection.
*
Expand All @@ -68,15 +70,27 @@ public function __construct(Uri $connUri)
$database = $path;
$username = $this->connectionUri->getUsername();
$password = $this->connectionUri->getPassword();
parse_str($this->connectionUri->getQuery(), $options);
$uriOptions = [];
$driverOptions = [];
foreach ($options as $key => $value) {
if (strpos($key, 'uri.') === 0) {
$options[$key] = $value;
} elseif (strpos($key, 'driver.') === 0) {
$driverOptions[$key] = $value;
} else {
throw new InvalidArgumentException("Invalid option '$key'. Need start with 'uri.' or 'driver.'. ");
}
}

if ($username != '' && $password != '') {
$auth = array('username' => $username, 'password' => $password, 'connect' => 'true');
if (!empty($username) && !empty($password)) {
$auth = "$username:$password@";
} else {
$auth = array('connect' => 'true');
$auth = "";
}

$connectString = sprintf('mongodb://%s:%d', $hosts, $port);
$this->mongoManager = new Manager($connectString, $auth);
$connectString = sprintf('mongodb://%s%s:%d', $auth, $hosts, $port);
$this->mongoManager = new Manager($connectString, $uriOptions, $driverOptions);
$this->database = $database;
}

Expand Down Expand Up @@ -131,7 +145,7 @@ public function getDocuments(IteratorFilter $filter, $collection = null)

$dataCursor = $this->mongoManager->executeQuery(
$this->database . '.' . $collection,
$this->getMongoFilterArray($filter)
new Query($this->getMongoFilterArray($filter))
);

if (empty($dataCursor)) {
Expand All @@ -154,10 +168,44 @@ public function getDocuments(IteratorFilter $filter, $collection = null)
return $result;
}

protected function getMongoFilterArray(IteratorFilter $filter)
protected function getMongoFilterArray(IteratorFilter $filter): array
{
$result = [];

$data = [
Relation::EQUAL => function ($value) {
return $value;
},
Relation::GREATER_THAN => function ($value) {
return [ '$gt' => $value ];
},
Relation::LESS_THAN => function ($value) {
return [ '$lt' => $value ];
},
Relation::GREATER_OR_EQUAL_THAN => function ($value) {
return [ '$gte' => $value ];
},
Relation::LESS_OR_EQUAL_THAN => function ($value) {
return [ '$lte' => $value ];
},
Relation::NOT_EQUAL => function ($value) {
return [ '$ne' => $value ];
},
Relation::STARTS_WITH => function ($value) {
return [ '$regex' => "^$value" ];
},
Relation::CONTAINS => function ($value) {
return [ '$regex' => "$value" ];
},
Relation::IN => function ($value) {
return [ '$in' => $value ];
},
Relation::NOT_IN => function ($value) {
return [ '$nin' => $value ];
},
];


foreach ($filter->getRawFilters() as $itemFilter) {
$name = $itemFilter[1];
$relation = $itemFilter[2];
Expand All @@ -171,37 +219,10 @@ protected function getMongoFilterArray(IteratorFilter $filter)
throw new InvalidArgumentException('MongoDBDriver does not support filtering the same field twice');
}

$data = [
Relation::EQUAL => function ($value) {
return $value;
},
Relation::GREATER_THAN => function ($value) {
return [ '$gt' => $value ];
},
Relation::LESS_THAN => function ($value) {
return [ '$lt' => $value ];
},
Relation::GREATER_OR_EQUAL_THAN => function ($value) {
return [ '$gte' => $value ];
},
Relation::LESS_OR_EQUAL_THAN => function ($value) {
return [ '$lte' => $value ];
},
Relation::NOT_EQUAL => function ($value) {
return [ '$ne' => $value ];
},
Relation::STARTS_WITH => function ($value) {
return [ '$regex' => "^$value" ];
},
Relation::CONTAINS => function ($value) {
return [ '$regex' => "$value" ];
},
];

$result[$name] = $data[$relation]($value);
}

return new Query($result);
return $result;
}

public function deleteDocumentById($idDocument, $collection = null)
Expand All @@ -219,9 +240,27 @@ public function deleteDocuments(IteratorFilter $filter, $collection = null)
}

$writeConcern = new WriteConcern(WriteConcern::MAJORITY, 100);

$query = $this->getMongoFilterArray($filter);
$bulkWrite = new BulkWrite();
$bulkWrite->delete($query);
$this->mongoManager->executeBulkWrite(
$this->database . '.' . $collection,
$bulkWrite,
$writeConcern
);
}

public function updateDocuments(IteratorFilter $filter, $data, $collection = null)
{
if (empty($collection)) {
throw new InvalidArgumentException('Collection is mandatory for MongoDB');
}

$bulkWrite->delete($this->getMongoFilterArray($filter));
$query = $this->getMongoFilterArray($filter);
$writeConcern = new WriteConcern(WriteConcern::MAJORITY, 100);
$bulkWrite = new BulkWrite();
$bulkWrite->update($query, ["\$set" => $data], ["multi" => 1]);
$this->mongoManager->executeBulkWrite(
$this->database . '.' . $collection,
$bulkWrite,
Expand Down Expand Up @@ -251,10 +290,10 @@ public function save(NoSqlDocument $document)
$idDocument = $data['_id'] ?? null;
}

$data['updated'] = new UTCDateTime((new DateTime())->getTimestamp()*1000);
$data['updatedAt'] = new UTCDateTime((new DateTime())->getTimestamp()*1000);
if (empty($idDocument)) {
$data['_id'] = $idDocument = new ObjectID();
$data['created'] = new UTCDateTime((new DateTime())->getTimestamp()*1000);
$data['createdAt'] = new UTCDateTime((new DateTime())->getTimestamp()*1000);
$bulkWrite->insert($data);
} else {
$data['_id'] = $idDocument;
Expand Down
Loading

0 comments on commit 36b5706

Please sign in to comment.