sql >> Databasteknik >  >> NoSQL >> MongoDB

Implementering av goMongoDB-liknande frågeuttrycksobjektutvärdering

Introduktion

Jag tror att utvärdering av MongoDB-liknande JSON-frågor i PHP har gett all information du behöver. allt du behöver är att vara kreativ med lösningen och du uppnår det du vill

Arrayen

Låt oss anta att vi har följande json konverteras till array

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

Exempel 1

kontrollera om key - Different skulle vara så enkelt som

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

Utdata

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

Exempel 2 Kontrollera om release year är 2013

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

Utdata

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

Exempel 3

Räkna var Year är 2012

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

Exempel 4

Låt oss ta från ditt exempel var du vill kontrollera version är grater than 22

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

Utdata

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

Exempel 5

Kontrollera om release.arch värdet är IN en uppsättning som [x86,x100] (Exempel)

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

Utdata

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
        )

)

Exempel 6

Använder 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);
}

Utdata

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

)

Exempel 7

Registrera ditt eget uttrycksnamn

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

Utdata

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

Klass använd

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

Sammanfattning

Det kanske inte täcker alla avancerade funktioner och bör ha utbyggbar arkitektur

Klassen ovan visar ett typiskt exempel på vad du vill ha .. du kan enkelt decouple it , utöka det för att stödja sammansatta uttryck som $and och $or

MongoDB-liknande frågeuttrycksobjekt är lätta att förstå och använda, vilket ger möjlighet att skriva ren, självförklarande kod, eftersom både fråga och objekt att söka i är associativa arrayer.

Varför inte bara skriva arrayen till en MongoDB databas snarare än att arbeta med det arrayer ?? Det är mer effektivt och det skulle spara dig många problem

Jag måste också nämna att använda det bästa verktyget för det bästa jobbet ... Det du vill ha är i grunden en funktion av en databas

I princip talar det om en bekväm funktion för att extrahera information från php-matriser. Genom att känna till arraystrukturen (arrayPath), kommer det att tillåta att utföra operationer på multidimensionella arraydata, utan att behöva flera kapslade loopar.

Exemplet visar hur du använder en sökväg för att söka efter värde men du är fortfarande beroende av att ladda arrayen till minnet och att din klass utför flera rekursions- och loopar som inte är lika effektivt som en databas.

Jag uppskattar arkitekturtips, relaterad eller liknande kod, som kan vara ett bra exempel för att bygga php "if..else"-uttryck i farten.

Menar du verkligen att du vill ha alla de här bara ???



  1. Vad är profil? och Web och Worker

  2. hur dödar jag inaktiva redis-klienter

  3. Hur uppdaterar jag delvis ett objekt i MongoDB så att det nya objektet överlappar / smälter samman med det befintliga

  4. Finns det några skäl till varför jag bör/inte bör använda ObjectIds i mina RESTful-url:s