Ответ 1
Последнее обновление
@baba предоставил отличную версию PHP-версии класса, реализующего оценку объекта выражения MongoDB-like, но структура вывода немного отличается, я имею в виду точечную нотацию в выходе вложенного массива ([release.arch] = > x86) вместо обычных массивов ([release] = > Массив ([arch] = > x86)). Я был бы признателен за ваш совет, как сделать класс полностью совместимым с mongoDB в этом порядке, поскольку он, судя по всему, строго связан с реализацией PHP-кода.
=============================================== ========================
Ответ:
То, что вы хотите, очень просто. Все, что вам нужно, это 2 corrections
в текущем цикле ввода и вывода кода, и вы получите новый формат.
Что я имею в виду?
A. Изменено
foreach ( $array as $part ) {
$this->flatten[] = $this->convert($part);
}
To
foreach ( $array as $k => $part ) {
$this->flatten[$k] = $this->convert($part);
}
B. Изменено
foreach ( $this->flatten as $data ) {
$this->check($find, $data, $type) and $f[] = $data;
}
To:
foreach ( $this->flatten as $k => $data ) {
$this->check($find, $data, $type) and $f[] = $this->array[$k];
}
Новый массив для отдыха
$json = '[
{
"name": "Mongo",
"release": {
"arch": "x86",
"version": 22,
"year": 2012
},
"type": "db"
},
{
"name": "Mongo",
"release": {
"arch": "x64",
"version": 21,
"year": 2012
},
"type": "db"
},
{
"name": "Mongo",
"release": {
"arch": "x86",
"version": 23,
"year": 2013
},
"type": "db"
},
{
"name": "MongoBuster",
"release": {
"arch": [
"x86",
"x64"
],
"version": 23,
"year": 2013
},
"type": "db"
},
{
"children": {
"dance": [
"one",
"two",
{
"three": {
"a": "apple",
"b": 700000,
"c": 8.8
}
}
],
"lang": "php",
"tech": "json",
"year": 2013
},
"key": "Diffrent",
"value": "cool"
}
]';
$array = json_decode($json, true);
Простой тест
$s = new ArrayStandard($array);
print_r($s->find(array("release.arch"=>"x86")));
Выход
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 22
[year] => 2012
)
)
[1] => Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 23
[year] => 2013
)
)
)
Если вы также хотите сохранить оригинальный array key position
, вы можете иметь
foreach ( $this->flatten as $k => $data ) {
$this->check($find, $data, $type) and $f[$k] = $this->array[$k];
}
Только для удовольствия
A. Поддержка regex
Просто для удовольствия я добавил поддержку $regex
с псевдонимом $preg
или $match
, что означает, что вы можете иметь
print_r($s->find(array("release.arch" => array('$regex' => "/4$/"))));
или
print_r($s->find(array("release.arch" => array('$regex' => "/4$/"))));
Выход
Array
(
[1] => Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x64
[version] => 21
[year] => 2012
)
)
)
B. Используйте простой массив, например queries
$queryArray = array(
"release" => array(
"arch" => "x86"
)
);
$d = $s->find($s->convert($queryArray));
$s->convert($queryArray)
преобразовал
Array
(
[release] => Array
(
[arch] => x86
)
)
Для
Array
(
[release.arch] => x86
)
C. Модуль $mod
print_r($s->find(array(
"release.version" => array(
'$mod' => array(
23 => 0
)
)
)));
//Checks release.version % 23 == 0 ;
D. Считать элементы с помощью $size
print_r($s->find(array(
"release.arch" => array(
'$size' => 2
)
)));
// returns count(release.arch) == 2;
E. Проверьте, соответствует ли он всем элементу массива $all
print_r($s->find(array(
"release.arch" => array(
'$all' => array(
"x86",
"x64"
)
)
)));
Выход
Array
(
[3] => Array
(
[name] => MongoBuster
[release] => Array
(
[arch] => Array
(
[0] => x86
[1] => x64
)
[version] => 23
[year] => 2013
)
[type] => db
)
)
F. Если вы не уверены в имени ключа элемента, вы можете использовать $has
его как opposite
из $in
print_r($s->find(array(
"release" => array(
'$has' => "x86"
)
)));
=============================================== ========================
Старое обновление
@Baba предоставил отличный класс, который написан с использованием SPL. Интересно, как переписать этот код без SPL. Причина в том, что вызов этого класса несколько раз даст накладные расходы функций, которых можно избежать, переписывая его в необработанный PHP и, возможно, используя инструкцию goto в финальной версии, чтобы избежать рекурсивных вызовов функций.
=============================================== ========================
Поскольку вы не хотите SPL
и функции.. это заняло некоторое время, но я смог придумать альтернативный класс, который также является гибким и простым в использовании
Чтобы избежать загрузки массива несколько раз, вы объявляете его один раз:
$array = json_decode($json, true);
$s = new ArrayStandard($array);
A. Найти, где release.year
есть 2013
$d = $s->find(array(
"release.year" => "2013"
));
print_r($d);
Выход
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
B. В первый раз вы можете запустить сложный оператор $and
или $or
, например find, где release.arch
= x86
и release.year
= 2012
$d = $s->find(array(
"release.arch" => "x86",
"release.year" => "2012"
), ArrayStandard::COMPLEX_AND);
print_r($d);
Выход
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 22
[release.year] => 2012
)
)
C. Представьте гораздо более сложный запрос
$d = $s->find(array(
"release.year" => array(
'$in' => array(
"2012",
"2013"
)
),
"release.version" => array(
'$gt' => 22
),
"release.arch" => array(
'$func' => function ($a) {
return $a == "x86";
}
)
), ArrayStandard::COMPLEX_AND);
print_r($d);
Выход
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
Новый модифицированный класс
class ArrayStandard {
const COMPLEX_OR = 1;
const COMPLEX_AND = 2;
private $array;
private $tokens;
private $found;
function __construct(array $array) {
$this->array = $array;
foreach ( $array as $k => $item ) {
$this->tokens[$k] = $this->tokenize($item);
}
}
public function getTokens() {
return $this->tokens;
}
public function convert($part) {
return $this->tokenize($part, null, false);
}
public function find(array $find, $type = 1) {
$f = array();
foreach ( $this->tokens as $k => $data ) {
$this->check($find, $data, $type) and $f[$k] = $this->array[$k];
}
return $f;
}
private function check($find, $data, $type) {
$o = $r = 0; // Obigation & Requirement
foreach ( $data as $key => $value ) {
if (isset($find[$key])) {
$r ++;
$options = $find[$key];
if (is_array($options)) {
reset($options);
$eK = key($options);
$eValue = current($options);
if (strpos($eK, '$') === 0) {
$this->evaluate($eK, $value, $eValue) and $o ++;
} else {
throw new InvalidArgumentException('Missing "$" in expession key');
}
} else {
$this->evaluate('$eq', $value, $options) and $o ++;
}
}
}
if ($o === 0)
return false;
if ($type == self::COMPLEX_AND and $o !== $r)
return false;
return true;
}
private function getValue(array $path) {
return count($path) > 1 ? $this->getValue(array_slice($path, 1), $this->array[$path[0]]) : $this->array[$path[0]];
}
private function tokenize($array, $prefix = '', $addParent = true) {
$paths = array();
$px = empty($prefix) ? null : $prefix . ".";
foreach ( $array as $key => $items ) {
if (is_array($items)) {
$addParent && $paths[$px . $key] = json_encode($items);
foreach ( $this->tokenize($items, $px . $key) as $k => $path ) {
$paths[$k] = $path;
}
} else {
$paths[$px . $key] = $items;
}
}
return $paths;
}
private function evaluate($func, $a, $b) {
$r = false;
switch ($func) {
case '$eq' :
$r = $a == $b;
break;
case '$not' :
$r = $a != $b;
break;
case '$gte' :
case '$gt' :
if ($this->checkType($a, $b)) {
$r = $a > $b;
}
break;
case '$lte' :
case '$lt' :
if ($this->checkType($a, $b)) {
$r = $a < $b;
}
break;
case '$in' :
if (! is_array($b))
throw new InvalidArgumentException('Invalid argument for $in option must be array');
$r = in_array($a, $b);
break;
case '$has' :
if (is_array($b))
throw new InvalidArgumentException('Invalid argument for $has array not supported');
$a = @json_decode($a, true) ? : array();
$r = in_array($b, $a);
break;
case '$all' :
$a = @json_decode($a, true) ? : array();
if (! is_array($b))
throw new InvalidArgumentException('Invalid argument for $all option must be array');
$r = count(array_intersect_key($a, $b)) == count($b);
break;
case '$regex' :
case '$preg' :
case '$match' :
$r = (boolean) preg_match($b, $a, $match);
break;
case '$size' :
$a = @json_decode($a, true) ? : array();
$r = (int) $b == count($a);
break;
case '$mod' :
if (! is_array($b))
throw new InvalidArgumentException('Invalid argument for $mod option must be array');
list($x, $y) = each($b);
$r = $a % $x == 0;
break;
case '$func' :
case '$fn' :
case '$f' :
if (! is_callable($b))
throw new InvalidArgumentException('Function should be callable');
$r = $b($a);
break;
default :
throw new ErrorException("Condition not valid ... Use \$fn for custom operations");
break;
}
return $r;
}
private function checkType($a, $b) {
if (is_numeric($a) && is_numeric($b)) {
$a = filter_var($a, FILTER_SANITIZE_NUMBER_FLOAT);
$b = filter_var($b, FILTER_SANITIZE_NUMBER_FLOAT);
}
if (gettype($a) != gettype($b)) {
return false;
}
return true;
}
}