【问题标题】:Implementing goMongoDB-like Query expression object evaluation实现类似 goMongoDB 的查询表达式对象评估
【发布时间】:2013-02-04 23:48:55
【问题描述】:

我一直在寻找类似MongoDb的http://docs.mongodb.org/manual/applications/read/#find,docs.mongodb.org/manual/reference/operators/)查询表达式对象评估函数 实现或类。它可能不会涵盖所有高级功能,并且应该具有可扩展的架构。

类似 MongoDB 的查询表达式对象易于理解和使用,提供编写简洁、自解释代码的能力,因为查询和要搜索的对象都是关联数组。

基本上说它是一个从 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.

家庭作业

  • 我发现 gossner.net/articles/JsonPath/ 可能 满足我的需求(不是完全匹配,因为它使用类似 Xpath 表达式),需要注意的是,它严重依赖于正则 表达式和字符串解析,什么肯定会减慢它 与仅数组(类似 JSON)实现相比。

  • 我也在这里找到了一个类似的问题,@stackoverflow Evaluating MongoDB-like JSON Queries in PHP。 得到的答案是使用我使用的一些 SPL 函数 避免大部分时间。
    想知道作者是否想出了函数,他一直在尝试 发展。

  • 可能的 arrayPath 实现位于 thereisamoduleforthat.com/content/dealing-deep-arrays-php, 因此缺乏这种实现,是它依赖于指针。

我知道这不是一个简单的问题,这就是为什么我在开始我自己的课程的实际开发之前问它的原因。

我很欣赏架构技巧、相关或类似代码,这可能是构建 php "if..else" 表达式的良好实践示例。强调文本

非SPL版本如何写?

@Baba 提供了一个使用 SPL 编写的优秀类。 我想知道如何在没有 SPL 的情况下重写此代码。

有两个原因

  • 多次调用该类会产生函数开销,这可以避免在原始 PHP 中重写它。
  • 它很容易移植到 SPL 不可用的原始 Javascript,从而在两个平台上更容易代码维护

结果

在 Github 上创建的 ArrayQuery class is published,考虑签出存储库以获取更新。

SPL、原始 PHP 版本和 Chequer2 FORP profiler 输出

简而言之-

  1. 原始 PHP 版本的执行速度比 SPL 版本快 10 倍,消耗 内存减少 20%。
  2. Chequer2 类的执行速度比 PHP SPL 类慢 40%,并且几乎 比原始 PHP 版本慢 20 倍。
  3. MongoDb 是最快的(比原始 PHP 实现快 10 倍,消耗的内存少 5 倍),做 除非您确定要避免,否则不要使用这些类 与 MongoDb 交互。

MongoDb 版本

SPL 版本

原始 PHP(最新的 ArrayQuery 类)版本

Checker2 版本

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

带有 SPL 类分析代码的 PHP

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 类 parse() 函数稍作修改,执行后返回值,也可以修改为接受表达式,但它对于分析目的并不是必需的,因为每次都会重新评估表达式。

原始 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 );

checker2 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 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>

【问题讨论】:

    标签: php mongodb if-statement multidimensional-array query-expressions


    【解决方案1】:

    最新更新

    @baba 提供了一个很棒的原始 PHP 版本的类,它实现了类似 MongoDB 的查询表达式对象评估,但是输出结构有点不同,我的意思是嵌套数组输出中的点表示法([release.arch] => x86 ),而不是常规数组( [release] => Array([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];
        }
    

    只是为了好玩

    答.支持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-&gt;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 where release.arch = x86release.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;
        }
    }
    

    【讨论】:

    • 很棒的课程,我将立即使用 forp PHP 分析器通过课程比较来更新问题。
    • 谢谢。输出结构略有不同。我看了一眼输出的实现,希望在分析之前用简写方式修复它,但看起来这只是类逻辑的行为。我的意思是原始 PHP 版本嵌套数组输出中的点表示法( [release.arch] => x86 ),而不是常规数组( [release] => Array([arch] => x86) )。我将不胜感激您提示如何使这些类按此顺序完全兼容。
    • 这与 SPL 速度有关。我只花时间优化这个类,比使用数组展平方法的前一个答案更好。我确定如果我使用 SLP 重新编写它肯定会更快更好
    • 哦,好吧……你的问题不清楚……我会调查一下并更新我的答案
    • 回答了这个问题......你现在仍然可以坚持为什么我在更复杂的查询上尝试它
    【解决方案2】:

    简介

    我认为Evaluating MongoDB-like JSON Queries in 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

    计算Year2012 的位置

    $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

    使用可调用对象

    $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 的查询表达式对象易于理解和使用,提供编写简洁、自解释代码的能力,因为查询和要搜索的对象都是关联数组。

    为什么不直接将数组写入MongoDB 数据库而不是使用数组?它更高效,它会为你省去很多麻烦

    我还必须提到,使用最好的工具做最好的工作......你想要的基本上是一个数据库的功能

    基本上说它是一个从 php 数组中提取信息的便捷函数。知道了数组结构(arrayPath),就可以对多维数组数据进行操作,而不需要多个嵌套循环。

    该示例展示了如何使用路径搜索值,但您仍然依赖于将数组加载到内存以及您的类执行多个递归 ans 循环,这不如数据库高效。

    我很欣赏架构技巧、相关或类似代码,这可能是动态构建 php "if..else" 表达式的一个很好的实践示例。

    你真的是说你想要所有这些人吗???

    【讨论】:

    • 您的代码绝对易于使用和扩展。 ##为什么不写入 MongoDB?## 因为它提供了额外的数据库交互开销,所以无需进行基准测试就可以说您提供的类将在已经预加载的小型数组上运行得更快。 ##为什么不直接将数组写入 MongoDB 数据库而不是使用数组## 是的,我想将一些非常方便的功能从 MongoDb 移植到 PHP,以简化 PHP 应用程序的开发。这段代码打算在应用层对预加载的数据做一些操作,仍然依赖 mongoDB 做繁重的数据库操作。
    • ##你真的是说你想要所有这些都在这里吗???## 哈哈,在得到如此完整的答案之后,我仍然感兴趣的唯一提示是如何在没有SPL 功能。想知道为什么?因为多次调用这个类会产生函数开销,所以可以避免用普通 PHP 重写它,并且 也许 使用 goto 语句来避免递归函数调用。
    • @fitheflow:避免过早优化。试试吧。永远不要让 anything 比它需要的更快。在这个过程中,你总是会丢失一些重要的东西(比如可维护性)。如果您担心函数开销,甚至没有尝试过,那您就做错了(tm)。
    • @ircmaxell 这不是一个过早的优化,类功能已经很好地定义了。我要避免的是 SPL。您对课程分析结果感兴趣吗?
    • @fitheflow SPL 实际上不应该是相当高效的,尤其是它与 PHP 核心的链接方式。我必须承认它确实使用了相当多的对象和上面的调用,但我认为这正是在一天结束时实际解码 MongoDB 查询的查询文档所需要的。
    猜你喜欢
    • 1970-01-01
    • 2023-01-12
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多