MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Implémentation de l'évaluation d'objet d'expression de requête de type goMongoDB

Présentation

Je pense que l'évaluation des requêtes JSON de type MongoDB en PHP a fourni toutes les informations dont vous avez besoin. tout ce dont vous avez besoin est d'être créatif avec la solution et vous obtenez ce que vous voulez

Le tableau

Supposons que nous ayons le suivant json converti en tableau

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

Exemple 1

vérifier si key - Different serait aussi simple que

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

Sortie

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

Exemple 2 Vérifiez si release year est 2013

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

Sortie

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

Exemple 3

Compter où Year est 2012

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

Exemple 4

Prenons de votre exemple où vous voulez vérifier version est grater than 22

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

Sortie

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

Exemple 5

Vérifiez si release.arch la valeur est IN un ensemble tel que [x86,x100] (Exemple)

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

Sortie

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
        )

)

Exemple 6

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

Sortie

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

)

Exemple 7

Enregistrez votre propre nom d'expression

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

Sortie

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

Classe utilisée

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

Résumé

Il peut ne pas couvrir toutes les fonctionnalités avancées et doit avoir une architecture extensible

La classe ci-dessus montre un exemple typique de ce que vous voulez .. vous pouvez facilement decouple it , étendez-le pour prendre en charge les expressions composées telles que $and et $or

Les objets d'expression de requête de type MongoDB sont faciles à comprendre et à utiliser, offrant la possibilité d'écrire du code propre et explicite, car la requête et les objets dans lesquels rechercher sont des tableaux associatifs.

Pourquoi ne pas simplement écrire le tableau dans un MongoDB base de données plutôt que de travailler avec des tableaux ?? C'est plus efficace et cela vous éviterait bien des soucis

Je dois également mentionner qu'utiliser le meilleur outil pour le meilleur travail... Ce que vous voulez est essentiellement une fonction d'une base de données

Fondamentalement, c'est une fonction pratique pour extraire des informations à partir de tableaux php. Connaissant la structure du tableau (le arrayPath), cela permettra d'effectuer des opérations sur des données de tableaux multidimensionnels, sans avoir besoin de plusieurs boucles imbriquées.

L'exemple montre comment utiliser un chemin pour rechercher une valeur, mais vous dépendez toujours du chargement du tableau en mémoire et de votre classe effectuant plusieurs boucles de récursivité, ce qui n'est pas aussi efficace qu'une base de données.

J'apprécie les conseils d'architecture, le code connexe ou similaire, qui peut être un exemple de bonne pratique pour créer des expressions php "if..else" à la volée.

Voulez-vous vraiment dire que vous voulez tous ceux qui sont juste ici ???