Внедрение оценки объектов выражения типа goMongoDB

Я искал объект MongoDb-like ( http://docs.mongodb.org/manual/applications/read/#find , docs.mongodb.org/manual/reference/operators/) реализацию функции оценки объекта выражения запроса или класс. Он может охватывать не все расширенные функции и иметь расширяемую архитектуру.

Объекты выражения MongoDB-like легко понимают и используют , обеспечивая возможность писать чистый, самоочевидный код, поскольку как запрос, так и объекты для поиска, являются ассоциативными массивами.

В основном говоря, его удобная функция для извлечения информации из php-массивов. Зная структуру массива (arrayPath), он позволит выполнять операции над данными многомерных массивов без необходимости в нескольких вложенных циклах.

Если вы не знакомы с MongoDb, посмотрите на объект выражения и массив для поиска.

Я написал его как строку JSON для простоты. Содержимое объекта не имеет смысла, просто показано синтаксис запроса MongoDb.

Объект выражения запроса MongoDb

{ "name": "Mongo", "type": "db", "arch": { "$in": [ "x86", "x64" ] }, "version": { "$gte": 22 }, "released": { "$or": { "$lt": 2013, "$gt": 2012 } } } 

Массив для поиска в

 [ { "name": "Mongo", "type": "db", "release": { "arch": "x86", "version": 22, "year": 2012 } }, { "name": "Mongo", "type": "db", "release": { "arch": "x64", "version": 21, "year": 2012 } }, { "name": "Mongo", "type": "db", "release": { "arch": "x86", "version": 23, "year": 2013 } } ] 

Найти использование выражений запросов, подобных Mongo

Таким образом, с помощью функции мы должны иметь возможность выдавать следующий запрос в целевой массив.

 $found=findLikeMongo($array, $queryExpr); //resulting in a $array[0] value; //@return found array 

Получить массив с использованием выражений запросов, подобных Mongo

 $arrayPath=getPathFromMongo($array, $queryExpr);// resulting in array("0") //@return array path, represented as an array where entries are consecutive keys. 

Домашнее задание

  • Я обнаружил, что goessner.net/articles/JsonPath/ может удовлетворить мои потребности (не будучи точным совпадением, потому что он использует выражения, подобные Xpath), оговорка заключается в том, что она в значительной степени зависит от регулярных выражений и синтаксического анализа строк, что определенно замедляется это по сравнению с реализацией только массива (JSON).

  • Также я нашел аналогичный вопрос здесь, @stackoverflow. Оценивая запросы JSON , подобные MongoDB, в PHP . В результате ответ был использован некоторые функции SPL, которые я использую, чтобы избежать большую часть времени.
    Интересно, создал ли автор функцию, которую он пытался развить.

  • Возможная реализация arrayPath была найдена на thereisamoduleforthat.com/content/dealing-deep-arrays-php, поэтому отсутствие этой реализации состоит в том, что она опирается на указатели.

Я знаю, что это не тривиальный вопрос с ответом oneliner, вот почему я спрашиваю об этом, прежде чем начинать разработку своего собственного класса.

Я ценю советы по архитектуре, родственный или похожий код, который может быть хорошим примером практики для создания выражений php «if..else» на лету. подчеркнутый текст

Как написать версию, отличную от SPL?

@Baba предоставил отличный класс, который написан с использованием SPL. Интересно, как переписать этот код без SPL.

Для этого есть две причины.

  • вызов класса несколько раз даст функцию накладных расходов , чего можно избежать при перезаписи в необработанном PHP.
  • он был бы легко переносимым в исходный Javascript, где SPL недоступен, что упрощает обслуживание кода на обеих платформах.

Результаты

Созданный класс ArrayQuery публикуется в Github, подумайте о том, чтобы проверить репозиторий на наличие обновлений.

SPL, необработанная версия PHP и выход профилей Chequer2 FORP

Вкратце-

  1. исходная версия PHP работает в 10 раз быстрее, чем SPL, потребляя на 20% меньше памяти.
  2. Класс Chequer2 выполняет на 40% медленнее, чем PHP SPL, и почти в 20 раз медленнее, чем исходная версия PHP.
  3. MongoDb является самым быстрым (в 10 раз быстрее, чем необработанная реализация PHP и потребляет 5 раз меньше памяти), не используйте эти классы, если вы не уверены, что хотите избежать взаимодействия с MongoDb.

Версия MongoDb

Результаты профилирования ссылок MongoDb

Версия SPL

PHP с результатами профилирования класса SPL

Необработанный PHP (последний класс ArrayQuery)

необработанные результаты профилирования класса ArrayQuery

Версия Chequer2

Результаты профилирования PHP класса Chequer2

Справочный тестовый код MongoDb

 $m = new MongoClient(); // connect $db = $m->testmongo; // select a database $collection = $db->data; $loops=100; for ($i=0; $i<$loops; $i++) { $d = $collection->find(array("release.year" => 2013)); } print_r( iterator_to_array($d) ); 

PHP с кодом профилирования класса SPL

 include('data.php'); include('phpmongo-spl.php'); $s = new ArrayCollection($array, array("release.year" => 2013),false); $loops=100; for ($i=0; $i<$loops; $i++) { $d = $s->parse(); } print_r( $d ); в include('data.php'); include('phpmongo-spl.php'); $s = new ArrayCollection($array, array("release.year" => 2013),false); $loops=100; for ($i=0; $i<$loops; $i++) { $d = $s->parse(); } print_r( $d ); в include('data.php'); include('phpmongo-spl.php'); $s = new ArrayCollection($array, array("release.year" => 2013),false); $loops=100; for ($i=0; $i<$loops; $i++) { $d = $s->parse(); } print_r( $d ); 

Функция parse () SPL-класса была слегка изменена, чтобы вернуть значение после выполнения, его также можно было бы изменить, чтобы принять выражение, но это не важно для целей профилирования, поскольку это выражение переоценивается каждый раз.

необработанный PHP (последний класс ArrayQuery) код профилирования

 include('data.php'); include('phpmongo-raw.php'); $s = new ArrayStandard($array); $loops=100; for ($i=0; $i<$loops; $i++) { $d = $s->find(array("release.year" => 2013)); } print_r( $d ); в include('data.php'); include('phpmongo-raw.php'); $s = new ArrayStandard($array); $loops=100; for ($i=0; $i<$loops; $i++) { $d = $s->find(array("release.year" => 2013)); } print_r( $d ); в include('data.php'); include('phpmongo-raw.php'); $s = new ArrayStandard($array); $loops=100; for ($i=0; $i<$loops; $i++) { $d = $s->find(array("release.year" => 2013)); } print_r( $d ); 

chequer2 PHP-код профилирования

 <?php include('data.php'); include('../chequer2/Chequer.php'); $query=array("release.year" => 2013); $loops=100; for ($i=0; $i<$loops; $i++) { $result=Chequer::shorthand('(.release.year > 2012) ? (.) : NULL') ->walk($array); } print_r($result); ?> в <?php include('data.php'); include('../chequer2/Chequer.php'); $query=array("release.year" => 2013); $loops=100; for ($i=0; $i<$loops; $i++) { $result=Chequer::shorthand('(.release.year > 2012) ? (.) : NULL') ->walk($array); } print_r($result); ?> 

используемые данные (такие же, как @baba, предоставленные в его ответе)

 $json = '[{ "name":"Mongo", "type":"db", "release":{ "arch":"x86", "version":22, "year":2012 } }, { "name":"Mongo", "type":"db", "release":{ "arch":"x64", "version":21, "year":2012 } }, { "name":"Mongo", "type":"db", "release":{ "arch":"x86", "version":23, "year":2013 } }, { "key":"Diffrent", "value":"cool", "children":{ "tech":"json", "lang":"php", "year":2013 } } ]'; $array = json_decode($json, true); 

forp-ui слегка измененный образец ui-загрузчика (для вызова с профилем? = FILE_TO_PROFILE)

 <!doctype html> <html> <head> <style> body {margin : 0px} </style> </head> <body> <div class="forp"></div> <?php register_shutdown_function( function() { // next code can be append to PHP scripts in dev mode ?> <script src="../forp-ui/js/forp.min.js"></script> <script> (function(f) { f.find(".forp") .each( function(el) { el.css('margin:50px;height:300px;border:1px solid #333'); } ) .forp({ stack : <?php echo json_encode(forp_dump()); ?>, //mode : "fixed" }) })(forp); </script> <?php } ); // start forp forp_start(); // our PHP script to profile include($_GET['profile']); // stop forp forp_end(); ?> </body> </html> 

Solutions Collecting From Web of "Внедрение оценки объектов выражения типа goMongoDB"

Последнее обновление

@baba дала отличную сырую версию PHP класса, реализующего оценку объекта выражения MongoDB-like, но структура вывода немного отличается, я имею в виду точечную нотацию в выходе вложенного массива ([release.arch] => x86), вместо регулярных массивов ([release] => Массив ([arch] => x86)). Я был бы признателен за ваш совет, как сделать класс полностью совместимым с mongoDB в этом порядке, поскольку он, похоже, строго связан с реализацией PHP-класса.

================================================== =====================

Ответ:

То, что вы хотите, очень просто. Все, что вам нужно, это 2 corrections в текущем коде ввода и вывода кода, и вы получите новый формат.

Что я имею в виду ?

A. Изменено

  foreach ( $array as $part ) { $this->flatten[] = $this->convert($part); } 

к

  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; } 

Для того, чтобы:

  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.year2013

 $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 ) ) 

Новый класс Modified

 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; } } 

Введение

Я думаю, что оценка запросов в JSON от MongoDB в PHP дала всю необходимую вам информацию. все, что вам нужно, это быть творческим с решением, и вы достигаете того, что хотите

Массив

Предположим, что мы имеем следующий json преобразованный в массив

 $json = '[{ "name":"Mongo", "type":"db", "release":{ "arch":"x86", "version":22, "year":2012 } }, { "name":"Mongo", "type":"db", "release":{ "arch":"x64", "version":21, "year":2012 } }, { "name":"Mongo", "type":"db", "release":{ "arch":"x86", "version":23, "year":2013 } }, { "key":"Diffrent", "value":"cool", "children":{ "tech":"json", "lang":"php", "year":2013 } } ]'; $array = json_decode($json, true); 

Пример 1

проверьте, будет ли keyDifferent будут такими же простыми, как

 echo new ArrayCollection($array, array("key" => "Diffrent")); 

Вывод

 {"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}} 

Пример 2. Проверьте, release year ли год release year в 2013

 echo new ArrayCollection($array, array("release.year" => 2013)); 

Вывод

 {"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}} 

Пример 3.

Граф, где Year 2012

 $c = new ArrayCollection($array, array("release.year" => 2012)); echo count($c); // output 2 

Пример 4.

Давайте возьмем из вашего примера, где вы хотите проверить version больше, grater than 22

 $c = new ArrayCollection($array, array("release.version" => array('$gt'=>22))); echo $c; 

Вывод

 {"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}} 

Пример 5.

Убедитесь, что значение release.arch равно IN например [x86,x100] (пример)

 $c = new ArrayCollection($array, array("release.arch" => array('$in'=>array("x86","x100")))); foreach($c as $var) { print_r($var); } 

Вывод

 Array ( [name] => Mongo [type] => db [release] => Array ( [arch] => x86 [version] => 22 [year] => 2012 ) ) Array ( [name] => Mongo [type] => db [release] => Array ( [arch] => x86 [version] => 23 [year] => 2013 ) ) 

Пример 6.

Использование Callable

 $year = 2013; $expression = array("release.year" => array('$func' => function ($value) use($year) { return $value === 2013; })); $c = new ArrayCollection($array, $expression); foreach ( $c as $var ) { print_r($var); } 

Вывод

 Array ( [name] => Mongo [type] => db [release] => Array ( [arch] => x86 [version] => 23 [year] => 2013 ) ) 

Пример 7.

Зарегистрируйте свое собственное выражение

 $c = new ArrayCollection($array, array("release.year" => array('$baba' => 3)), false); $c->register('$baba', function ($a, $b) { return substr($a, - 1) == $b; }); $c->parse(); echo $c; 

Вывод

 {"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}} 

Используемый класс

 class ArrayCollection implements IteratorAggregate, Countable, JsonSerializable { private $array; private $found = array(); private $log; private $expression; private $register; function __construct(array $array, array $expression, $parse = true) { $this->array = $array; $this->expression = $expression; $this->registerDefault(); $parse === true and $this->parse(); } public function __toString() { return $this->jsonSerialize(); } public function jsonSerialize() { return json_encode($this->found); } public function getIterator() { return new ArrayIterator($this->found); } public function count() { return count($this->found); } public function getLog() { return $this->log; } public function register($offset, $value) { if (strpos($offset, '$') !== 0) throw new InvalidArgumentException('Expresiion name must always start with "$" sign'); if (isset($this->register[$offset])) throw new InvalidArgumentException(sprintf('Expression %s already registred .. Please unregister It first')); if (! is_callable($value)) { throw new InvalidArgumentException(sprintf('Only callable value can be registred')); } $this->register[$offset] = $value; } public function unRegister($offset) { unset($this->register[$offset]); } public function parse() { $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->array)); foreach ( $it as $k => $items ) { if ($this->evaluate($this->getPath($it), $items)) { $this->found[$it->getSubIterator(0)->key()] = $this->array[$it->getSubIterator(0)->key()]; } } } private function registerDefault() { $this->register['$eq'] = array($this,"evaluateEqal"); $this->register['$not'] = array($this,"evaluateNotEqual"); $this->register['$gte'] = array($this,"evaluateGreater"); $this->register['$gt'] = array($this,"evaluateGreater"); $this->register['$lte'] = array($this,"evaluateLess"); $this->register['$lt'] = array($this,"evaluateLess"); $this->register['$in'] = array($this,"evalueateInset"); $this->register['$func'] = array($this,"evalueateFunction"); $this->register['$fn'] = array($this,"evalueateFunction"); $this->register['$f'] = array($this,"evalueateFunction"); } private function log($log) { $this->log[] = $log; } private function getPath(RecursiveIteratorIterator $it) { $keyPath = array(); foreach ( range(1, $it->getDepth()) as $depth ) { $keyPath[] = $it->getSubIterator($depth)->key(); } return implode(".", $keyPath); } private function checkType($a, $b) { if (gettype($a) != gettype($b)) { $this->log(sprintf("%s - %s is not same type of %s - %s", json_encode($a), gettype($a), json_encode($b), gettype($b))); return false; } return true; } private function evaluate($key, $value) { $o = $r = 0; // Obigation & Requirement foreach ( $this->expression as $k => $options ) { if ($k !== $key) continue; if (is_array($options)) { foreach ( $options as $eK => $eValue ) { if (strpos($eK, '$') === 0) { $r ++; $callable = $this->register[$eK]; $callable($value, $eValue) and $o ++; } else { throw new InvalidArgumentException('Missing "$" in expession key'); } } } else { $r ++; $this->evaluateEqal($value, $options) and $o ++; } } return $r > 0 && $o === $r; } private function evaluateEqal($a, $b) { return $a == $b; } private function evaluateNotEqual($a, $b) { return $a != $b; } private function evaluateLess($a, $b) { return $this->checkType($a, $b) and $a < $b; } private function evaluateGreater($a, $b) { return $this->checkType($a, $b) and $a > $b; } private function evalueateInset($a, array $b) { return in_array($a, $b); } private function evalueateFunction($a, callable $b) { return $b($a); } } с class ArrayCollection implements IteratorAggregate, Countable, JsonSerializable { private $array; private $found = array(); private $log; private $expression; private $register; function __construct(array $array, array $expression, $parse = true) { $this->array = $array; $this->expression = $expression; $this->registerDefault(); $parse === true and $this->parse(); } public function __toString() { return $this->jsonSerialize(); } public function jsonSerialize() { return json_encode($this->found); } public function getIterator() { return new ArrayIterator($this->found); } public function count() { return count($this->found); } public function getLog() { return $this->log; } public function register($offset, $value) { if (strpos($offset, '$') !== 0) throw new InvalidArgumentException('Expresiion name must always start with "$" sign'); if (isset($this->register[$offset])) throw new InvalidArgumentException(sprintf('Expression %s already registred .. Please unregister It first')); if (! is_callable($value)) { throw new InvalidArgumentException(sprintf('Only callable value can be registred')); } $this->register[$offset] = $value; } public function unRegister($offset) { unset($this->register[$offset]); } public function parse() { $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->array)); foreach ( $it as $k => $items ) { if ($this->evaluate($this->getPath($it), $items)) { $this->found[$it->getSubIterator(0)->key()] = $this->array[$it->getSubIterator(0)->key()]; } } } private function registerDefault() { $this->register['$eq'] = array($this,"evaluateEqal"); $this->register['$not'] = array($this,"evaluateNotEqual"); $this->register['$gte'] = array($this,"evaluateGreater"); $this->register['$gt'] = array($this,"evaluateGreater"); $this->register['$lte'] = array($this,"evaluateLess"); $this->register['$lt'] = array($this,"evaluateLess"); $this->register['$in'] = array($this,"evalueateInset"); $this->register['$func'] = array($this,"evalueateFunction"); $this->register['$fn'] = array($this,"evalueateFunction"); $this->register['$f'] = array($this,"evalueateFunction"); } private function log($log) { $this->log[] = $log; } private function getPath(RecursiveIteratorIterator $it) { $keyPath = array(); foreach ( range(1, $it->getDepth()) as $depth ) { $keyPath[] = $it->getSubIterator($depth)->key(); } return implode(".", $keyPath); } private function checkType($a, $b) { if (gettype($a) != gettype($b)) { $this->log(sprintf("%s - %s is not same type of %s - %s", json_encode($a), gettype($a), json_encode($b), gettype($b))); return false; } return true; } private function evaluate($key, $value) { $o = $r = 0; // Obigation & Requirement foreach ( $this->expression as $k => $options ) { if ($k !== $key) continue; if (is_array($options)) { foreach ( $options as $eK => $eValue ) { if (strpos($eK, '$') === 0) { $r ++; $callable = $this->register[$eK]; $callable($value, $eValue) and $o ++; } else { throw new InvalidArgumentException('Missing "$" in expession key'); } } } else { $r ++; $this->evaluateEqal($value, $options) and $o ++; } } return $r > 0 && $o === $r; } private function evaluateEqal($a, $b) { return $a == $b; } private function evaluateNotEqual($a, $b) { return $a != $b; } private function evaluateLess($a, $b) { return $this->checkType($a, $b) and $a < $b; } private function evaluateGreater($a, $b) { return $this->checkType($a, $b) and $a > $b; } private function evalueateInset($a, array $b) { return in_array($a, $b); } private function evalueateFunction($a, callable $b) { return $b($a); } } 

Резюме

Он может охватывать не все расширенные функции и иметь расширяемую архитектуру

Вышеприведенный класс показывает типичный пример того, что вы хотите. Вы можете легко decouple его, расширить его, чтобы поддерживать составные выражения, такие как $and и $or

Объекты выражения MongoDB-like легко понимают и используют, обеспечивая возможность писать чистый, самоочевидный код, поскольку как запрос, так и объекты для поиска, являются ассоциативными массивами.

Почему бы просто не написать массив в MongoDB данных MongoDB а не работать с массивами? Это более эффективно, и это сэкономит вам массу проблем

Я также должен упомянуть, что использовать лучший инструмент для лучшей работы … То, что вы хотите, в основном, является функцией базы данных

В основном говоря, его удобная функция для извлечения информации из php-массивов. Зная структуру массива (arrayPath), он позволит выполнять операции над данными многомерных массивов без необходимости в нескольких вложенных циклах.

В этом примере показано, как использовать путь для поиска значения, но вы все еще зависите от загрузки массива в память, а ваш класс выполняет несколько циклов рекурсии и не так эффективен, как база данных.

Я ценю советы по архитектуре, родственный или похожий код, который может быть хорошим примером практики для создания выражений php «if..else» на лету.

Вы действительно хотите, чтобы вы все хотели здесь?