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

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

Объекты выражения 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-like

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

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

Получить массив с использованием выражений выражений Mongo-like

$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-like выражения), оговорка состоит в том, что она в значительной степени зависит от регулярных выражения и синтаксический анализ строк, что определенно замедлит его по сравнению с реализацией только массива (как 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 profiler output

Вкратце -

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

версия MongoDb

MongoDb reference profiling results

версия SPL

PHP with SPL class profiling results

версия Raw PHP (последний класс ArrayQuery)

raw PHP ArrayQuery class profiling results

версия Chequer2

Chequer2 PHP class profiling results

Справочный код проверки ссылочного теста 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 );

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

raw 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 );

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);
?>

используемые данные (такие же, как @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 (для вызова с? profile = 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 скриптs 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 скрипт to profile
include($_GET['profile']);

// stop forp
forp_end();
?>
</body>
</html>

Ответы

Ответ 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;
    }
}

Ответ 2

Введение

Я думаю, Оценка запросов, заданных MongoDB JSON в 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

проверьте, что key - Different будет таким же простым, как

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

Выход

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

Пример 2 Проверьте, есть ли 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);
    }
}

Резюме

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

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

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

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

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

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

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

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

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