namespace Ice\Db\Driver;
use Ice\Arr;
use Ice\Exception;
use Ice\Db\DbInterface;
/**
* MongoDB driver.
*
* @package Ice/Db
* @category Component
* @author Ice Team
* @copyright (c) 2014-2023 Ice Team
* @license http://iceframework.org/license
*/
class Mongodb implements DbInterface
{
protected id = "_id" { get };
protected type = "NOSQL" { get };
protected error;
protected client { get };
protected lastInsertId { get };
/**
* Instantiate mongo connection.
*
* @param string dsn
* @param string dbname
* @param array options
*/
public function __construct(string dsn, string dbname = NULL, array options = [])
{
var client;
if version_compare(phpversion("mongodb"), "1.2.0alpha1", ">=") {
let client = "MongoDB\\Client",
this->client = (new {client}(dsn, options))->selectDatabase(dbname);
} else {
throw new Exception("Version of `mongodb` extension must be 1.2.0alpha1 or higher");
}
}
/**
* Get the id value.
*
* @param string id
* @return object
*/
public function getIdValue(string id) -> object
{
return new \MongoDB\BSON\ObjectID(id);
}
/**
* Get a date time object.
*
* @param mixed value
* @param boolean model
* @return object
*/
public function getDateTime(value = null, model = false)
{
var date;
if typeof value == "object" && value instanceof \MongoDB\BSON\UTCDateTime {
let date = value;
} else {
long tmp;
switch typeof value {
case "NULL":
let tmp = time() * 1000,
value = tmp;
break;
case "integer":
let tmp = value * 1000,
value = tmp;
break;
default:
let tmp = strtotime(value) * 1000,
value = tmp;
break;
}
let date = new \MongoDB\BSON\UTCDateTime(value);
}
if model {
return date;
}
return date->toDateTime();
}
/**
* Find one document that match criteria.
*
* @param string from Collection name
* @param mixed filters Criteria
* @param array options Options to limit/group results
* @param array fields Fields to retrieve, if not specified get all
* @return Arr
*/
public function findOne(string! from, var filters = [], array options = [], array fields = [])
{
var result;
let options["limit"] = 1,
result = this->select(from, filters, options, fields);
return count(result) ? new Arr(current(result)) : false;
}
/**
* Find all documents that match criteria.
*
*
* $db->find("users", array("a" => 1, "b" => "q"));
* $db->find("users", array("age" => array(">" => 33)));
* $db->find("users", array("OR" => array(array("a" => 1), array("b" => 2))));
*
*
* @param string from Collection name
* @param mixed filters Criteria
* @param array options Options to limit/group results
* @param array fields Fields to retrieve, if not specified get all
* @return Arr
*/
public function find(string! from, var filters = [], array options = [], array fields = [])
{
var result;
let result = this->select(from, filters, options, fields);
return new Arr(result);
}
/**
* Count rows that match criteria.
*
*
* $db->count("users", ["a" => 1]);
*
*
* @param string from Collection name
* @param mixed filters Criteria
* @return int
*/
public function count(string! from, var filters = []) -> int
{
var filtered, collection, result;
switch typeof filters {
case "object":
// Find by ObjectId
if filters instanceof \MongoDB\BSON\ObjectID {
let filtered = [this->id: filters];
} else {
throw new Exception("Object must be an ObjectID instance");
}
break;
case "array":
// Find by filters
let filtered = filters;
break;
case "integer":
case "string":
// Find by id
let filtered = [this->id: this->getIdValue(filters)];
break;
default:
// Find all
let filtered = [];
break;
}
let collection = this->client->selectCollection(from),
result = collection->count(filtered);
return result;
}
/**
* SELECT document(s) that match criteria.
*
* @param string from Collection name
* @param mixed filters Criteria
* @param array options Options to limit/group results
* @param array fields Fields to retrieve, if not specified get all
*/
public function select(string! from, var filters = [], array options = [], array fields = [])
{
var filtered, collection, result;
switch typeof filters {
case "object":
// Find by ObjectId
if filters instanceof \MongoDB\BSON\ObjectID {
let filtered = [this->id: filters];
} else {
throw new Exception("Object must be an ObjectID instance");
}
break;
case "array":
// Find by filters
let filtered = filters;
// @TODO Support multiple filters
if count(filtered) == 1 {
var key = key(filtered);
var value = current(filtered);
// Case insensitive
if typeof value == "string" && isset options["insensitive"] {
let filtered[key] = new \MongoDB\BSON\Regex("^" . value . "$", "i");
}
}
break;
case "integer":
case "string":
// Find by id
let filtered = [this->id: this->getIdValue(filters)];
break;
default:
// Find all
let filtered = [];
break;
}
if isset options["order"] {
let options["sort"] = options["order"];
unset options["order"];
}
if isset options["offset"] {
let options["skip"] = options["offset"];
unset options["offset"];
}
if count(fields) {
let options["projection"] = array_fill_keys(array_flip(fields), 1);
}
let collection = this->client->selectCollection(from),
result = collection->find(filtered, options);
result->setTypeMap(["root": "array", "document": "array", "array": "array"]);
return iterator_to_array(result);
}
/**
* INSERT document into collection.
*
* @param string from Collection name
* @param array fields Fields to insert, keys are the column names
*/
public function insert(string! from, array fields = [])
{
var collection, result;
let collection = this->client->selectCollection(from),
result = collection->insertOne(fields),
this->lastInsertId = result->getInsertedId();
return result->getInsertedCount() ? true : false;
}
/**
* UPDATE documents in the collection.
*
* @param string from Collection name
* @param mixed filters Criteria
* @param array fields Fields to update, keys are the column names
*/
public function update(string! from, var filters = [], array fields = [])
{
var collection, result;
let collection = this->client->selectCollection(from),
result = collection->updateMany(filters, ["$set": fields]);
return result->getMatchedCount() ? true : false;
}
/**
* Remove documents from the collection.
*
* @param string from Collection name
* @param mixed filters Criteria
*/
public function delete(string! from, var filters = [])
{
var collection, result;
let collection = this->client->selectCollection(from),
result = collection->deleteMany(filters);
return result->getDeletedCount() ? true : false;
}
/**
* Get an error message.
*
* @return mixed
*/
public function getError()
{
var error;
fetch error, this->error["err"];
return error;
}
}