sql >> Base de Datos >  >> NoSQL >> MongoDB

Implementación de una evaluación de objeto de expresión de consulta similar a goMongoDB

Introducción

Creo que la evaluación de consultas JSON similares a MongoDB en PHP le ha brindado toda la información que necesita. todo lo que necesita es ser creativo con la solución y logrará lo que desea

La Matriz

Supongamos que tenemos el siguiente json convertido a 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);

Ejemplo 1

comprobar si key - Different sería tan simple como

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

Salida

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

Ejemplo 2 Compruebe si release year es 2013

echo new ArrayCollection($array, array("release.year" => 2013));

Salida

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

Ejemplo 3

Cuenta donde Year es 2012

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

Ejemplo 4

Tomemos de su ejemplo donde desea verificar version es grater than 22

$c = new ArrayCollection($array, array("release.version" => array('$gt'=>22)));
echo $c;

Salida

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

Ejemplo 5

Compruebe si release.arch el valor es IN un conjunto como [x86,x100] (Ejemplo)

$c = new ArrayCollection($array, array("release.arch" => array('$in'=>array("x86","x100"))));
foreach($c as $var)
{
    print_r($var);
}

Salida

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
        )

)

Ejemplo 6

Uso de Llamable

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

Salida

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

)

Ejemplo 7

Registre su propio nombre de expresión

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

Salida

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

Clase utilizada

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

Resumen

Es posible que no cubra todas las funciones avanzadas y debe tener una arquitectura extensible

La clase anterior muestra un ejemplo típico de lo que desea ... puede decouple fácilmente it , extiéndalo para admitir expresiones compuestas como $and y $or

Los objetos de expresión de consulta similares a MongoDB son fáciles de entender y usar, y brindan la capacidad de escribir código limpio y autoexplicativo, porque tanto la consulta como los objetos para buscar son matrices asociativas.

¿Por qué no simplemente escribir la matriz en un MongoDB? base de datos en lugar de trabajar en matrices? Es más eficiente y te ahorraría muchos problemas

También debo mencionar que use la mejor herramienta para el mejor trabajo... Lo que quiere es básicamente una función de una base de datos

Básicamente hablando, es una función conveniente para extraer información de matrices php. Conociendo la estructura de la matriz (arrayPath), permitirá realizar operaciones en datos de matrices multidimensionales, sin la necesidad de múltiples bucles anidados.

El ejemplo muestra cómo usar una ruta para buscar valor, pero aún depende de cargar la matriz en la memoria y su clase realiza múltiples recursiones y bucles, lo que no es tan eficiente como una base de datos.

Aprecio los consejos de arquitectura, código relacionado o similar, que puede ser un ejemplo de buena práctica para construir expresiones php "if..else" sobre la marcha.

¿De verdad quieres decir que los quieres todos aquí?