Php 实现类goMongoDB查询表达式对象求值
我一直在寻找一个类似MongoDb的实现或类(docs.MongoDb.org/manual/reference/operators/)查询表达式对象求值函数。它可能不涵盖所有高级功能,并且应该具有可扩展的体系结构 类似MongoDB的查询表达式对象易于理解和使用,提供了编写干净、自解释代码的能力,因为查询和要搜索的对象都是关联数组 基本上讲,从php数组中提取信息是一个方便的函数。了解数组结构(arrayPath),它将允许对多维数组数据执行操作,而不需要多个嵌套循环 如果您不熟悉MongoDb,请查看要搜索的给定表达式对象和数组 为了简单起见,我将其编写为JSON字符串。对象内容毫无意义,只是展示了MongoDb查询语法 类MongoDb查询表达式对象 要搜索的数组 使用类似Mongo的查询表达式查找 因此,在函数的帮助下,我们应该能够向目标数组发出以下查询Php 实现类goMongoDB查询表达式对象求值,php,mongodb,if-statement,multidimensional-array,query-expressions,Php,Mongodb,If Statement,Multidimensional Array,Query Expressions,我一直在寻找一个类似MongoDb的实现或类(docs.MongoDb.org/manual/reference/operators/)查询表达式对象求值函数。它可能不涵盖所有高级功能,并且应该具有可扩展的体系结构 类似MongoDB的查询表达式对象易于理解和使用,提供了编写干净、自解释代码的能力,因为查询和要搜索的对象都是关联数组 基本上讲,从php数组中提取信息是一个方便的函数。了解数组结构(arrayPath),它将允许对多维数组数据执行操作,而不需要多个嵌套循环 如果您不熟悉MongoD
$found=findLikeMongo($array, $queryExpr); //resulting in a $array[0] value;
//@return found array
使用类似Mongo的查询表达式获取数组路径
作业
- 我发现goessner.net/articles/JsonPath/可能 满足我的需求(不完全匹配,因为它使用类似Xpath的 表达式),警告是,它严重依赖正则表达式 表达式和字符串解析,什么会让它慢下来 与仅阵列(类似JSON)实现相比
- 我还发现了一个类似的问题,@stackoverflow
.
最终的答案是使用一些SPL函数,我就是这么用的
避免大部分时间。
不知道作者是否提出了函数,他一直试图 发展 - 在上找到了可能的arrayPath实现 有一个moduleforthat.com/content/deep-arrays-php, 因此,这种实现的不足之处在于它依赖于指针
- 多次调用该类将产生函数开销,这可以避免在原始PHP中重写它
- 在SPL不可用的情况下,它可以很容易地移植到原始Javascript,从而在两种平台上更容易地维护代码
在GITHUB上创建的,考虑检查存储库是否有更新。
SPL、原始PHP版本和Chequer2输出 简言之—
正文{margin:0px}
简介
我想我已经提供了你需要的所有信息。你所需要的就是创造性地解决问题,实现你想要的
阵列
假设我们将下面的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
检查键
-不同
是否与
echo new ArrayCollection($array, array("key" => "Diffrent"));
输出
{"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 23
[year] => 2013
)
)
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
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
(
[3] => Array
(
[name] => MongoBuster
[release] => Array
(
[arch] => Array
(
[0] => x86
[1] => x64
)
[version] => 23
[year] => 2013
)
[type] => db
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 22
[release.year] => 2012
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
示例2
检查发布年份是否为2013
echo new ArrayCollection($array, array("release.year" => 2013));
$d = $s->find(array(
"release.year" => "2013"
));
print_r($d);
输出
{"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 23
[year] => 2013
)
)
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
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
(
[3] => Array
(
[name] => MongoBuster
[release] => Array
(
[arch] => Array
(
[0] => x86
[1] => x64
)
[version] => 23
[year] => 2013
)
[type] => db
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 22
[release.year] => 2012
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
示例3
计算年份
为2012年
$c = new ArrayCollection($array, array("release.year" => 2012));
echo count($c); // output 2
示例4
让我们从您想要检查<代码>版本
是否<代码>大于22
$c = new ArrayCollection($array, array("release.version" => array('$gt'=>22)));
echo $c;
输出
{"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 23
[year] => 2013
)
)
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
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
(
[3] => Array
(
[name] => MongoBuster
[release] => Array
(
[arch] => Array
(
[0] => x86
[1] => x64
)
[version] => 23
[year] => 2013
)
[type] => db
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 22
[release.year] => 2012
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
示例5
检查release.arch
值是否在[x86,x100]
等集合中(示例)
输出
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
)
)
Array
(
[1] => Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x64
[version] => 21
[year] => 2012
)
)
)
示例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);
}
输出
{"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 23
[year] => 2013
)
)
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
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
(
[3] => Array
(
[name] => MongoBuster
[release] => Array
(
[arch] => Array
(
[0] => x86
[1] => x64
)
[version] => 23
[year] => 2013
)
[type] => db
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 22
[release.year] => 2012
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.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;
输出
{"3":{"key":"Diffrent","value":"cool","children":{"tech":"json","lang":"php","year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
Array
(
[name] => Mongo
[type] => db
[release] => Array
(
[arch] => x86
[version] => 23
[year] => 2013
)
)
{"2":{"name":"Mongo","type":"db","release":{"arch":"x86","version":23,"year":2013}}}
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
(
[3] => Array
(
[name] => MongoBuster
[release] => Array
(
[arch] => Array
(
[0] => x86
[1] => x64
)
[version] => 23
[year] => 2013
)
[type] => db
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.year] => 2013
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 22
[release.year] => 2012
)
)
Array
(
[0] => Array
(
[name] => Mongo
[type] => db
[release.arch] => x86
[release.version] => 23
[release.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);
}
}
$s = new ArrayStandard($array);
print_r($s->find(array("release.arch"=>"x86")));
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;
}
}
class ArrayCollection实现迭代器聚合、可计数、JsonSerializable{
私有$数组;
private$found=array();
私人$log;
私人表达;
私人登记册;
函数_构造(数组$array,数组$expression,$parse=true){
$this->array=$array;
$this->expression=$expression;
$this->registerDefault();
$parse===true和$this->parse();
}
公共函数{
返回$this->jsonSerialize();
}
公共函数jsonSerialize(){
返回json_encode($this->found);
}
公共函数getIterator(){
返回新的阵列计数器($this->found);
}
公共功能计数(){
返回计数($this->found);
}
公共函数getLog(){
返回$this->log;
}
公共功能寄存器($offset,$value){
if(strpos($offset,“$”)!==0)
抛出新的InvalidArgumentException('表达式名称必须始终以“$”符号开头');
if(设置($this->register[$offset]))
抛出新的InvalidArgumentException(sprintf)