MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Implementando avaliação de objeto de expressão de consulta semelhante ao goMongoDB

Introdução


Acho que a avaliação de consultas JSON do tipo MongoDB em PHP forneceu todas as informações que você precisa. tudo o que você precisa é ser criativo com a solução e você consegue o que deseja

A matriz

Vamos supor que temos o seguinte json convertido em matriz
$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);

Exemplo 1

verifique se key - Different seria tão simples quanto
echo new ArrayCollection($array, array("key" => "Diffrent"));

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

Exemplo 2 Verifique se release year é 2013
echo new ArrayCollection($array, array("release.year" => 2013));

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

Exemplo 3

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

Exemplo 4

Vamos tirar do seu exemplo onde você deseja verificar a version é grater than 22
$c = new ArrayCollection($array, array("release.version" => array('$gt'=>22)));
echo $c;

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

Exemplo 5

Verifique se release.arch o valor é IN um conjunto como [x86,x100] (Exemplo)
$c = new ArrayCollection($array, array("release.arch" => array('$in'=>array("x86","x100"))));
foreach($c as $var)
{
    print_r($var);
}

Saída
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
        )

)

Exemplo 6

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

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

)

Exemplo 7

Registre seu próprio nome de expressão
$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;

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

Classe usada
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);
    }
}

Resumo


Pode não abranger todos os recursos avançados e deve ter arquitetura extensível

A classe acima mostra um exemplo típico do que você quer .. você pode facilmente decouple it , estenda-o para suportar expressões compostas como $and e $or

Objetos de expressão de consulta do tipo MongoDB são fáceis de entender e usar, fornecendo a capacidade de escrever código limpo e autoexplicativo, porque tanto a consulta quanto os objetos a serem pesquisados ​​são matrizes associativas.

Por que não apenas escrever o array em um MongoDB banco de dados em vez de trabalhar com matrizes ?? É mais eficiente e economizaria muitos problemas

Devo também mencionar que use a melhor ferramenta para o melhor trabalho... O que você quer é basicamente uma função de um Banco de Dados

Basicamente falando, é uma função conveniente para extrair informações de arrays php. Conhecendo a estrutura do array (o arrayPath), ele permitirá realizar operações em dados de arrays multidimensionais, sem a necessidade de múltiplos loops aninhados.

O exemplo mostra como usar um caminho para procurar valor, mas você ainda depende de carregar o array na memória e sua classe executar vários loops e recursão, o que não é tão eficiente quanto um banco de dados.

Eu aprecio dicas de arquitetura, código relacionado ou similar, que pode ser um exemplo de boa prática para construir expressões php "if..else" em tempo real.

Você realmente quer dizer que quer todos aqueles aqui ???