我一直在寻找类似 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.
家庭作业
我发现 gosner.net/articles/JsonPath/ 可能满足我的需求(不是完全匹配,因为它使用类似 Xpath 的表达式),需要注意的是,它严重依赖正则表达式和字符串解析,这肯定会慢与仅数组(类似 JSON)的实现相比,它下降了。
我也在这里找到了一个类似的问题,@stackoverflow Evaluating MongoDB-like JSON Queries in PHP。得到的答案是使用一些 SPL 函数,我习惯于在大多数情况下避免使用这些函数。
不知道作者是否提出了功能,他一直在努力开发。在 thereisamoduleforthat.com/content/dealing-deep-arrays-php 上找到了可能的 arrayPath 实现,因此缺少此实现是因为它依赖于指针。
我知道这不是一个简单的问题,这就是为什么我在开始我自己的课程的实际开发之前问它的原因。
我很欣赏架构技巧、相关或类似的代码,这可能是动态构建 php“if..else”表达式的一个很好的实践示例。强调文本
如何编写非 SPL 版本?
@Baba 提供了一个优秀的类,它是使用 SPL 编写的。我想知道如何在没有 SPL 的情况下重写此代码。
有两个原因
- 多次调用该类会产生函数开销,这可以避免在原始 PHP 中重写它。
- 它可以很容易地移植到 SPL 不可用的原始 Javascript,从而更容易在两个平台上进行代码维护。
结果
创建的ArrayQuery 类发布在 Github 上,请考虑签出存储库以获取更新。
SPL、原始 PHP 版本和 Chequer2 FORP分析器输出
简单来说-
- 原始 PHP 版本的执行速度比 SPL 版本快 10 倍,消耗的内存减少了 20%。
- Chequer2 类的执行速度比 PHP SPL 类慢 40%,比原始 PHP 版本慢近 20 倍。
- MongoDb 是最快的(比原始 PHP 实现快 10 倍,消耗的内存少 5 倍),除非您确定要避免与 MongoDb 交互,否则不要使用这些类。
MongoDb 版本
声压级版本
原始 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>