9

Consider the following (rather complicated) query expressed in this JSON object:

{
    "name": "Kindle Fire",
    "sale": true,
    "price": {
        "$gt": 199,
        "$lt": 264
    },
    "price.vat": { // bogus, just to show $a['price.vat'] == $a['price']['vat']
        "$lte": 1.2
    },
    "$or": {
        "qty": {
            "$gt": 30
        },
        "eta": {
            "$or": {
                "$lt": 3,
                "$gt": 30
            }
        }
    },
    "countriesAvailable": {
        "$in": [
            "US",
            "CA"
        ]
    }
}

Objective


I want to parse that JSON so that it evaluates to the PHP equivalent of (where $a is my target data):

$a['name'] == 'Kindle Fire' &&
$a['sale'] == true &&
(
    $a['price'] > 199 && $a['price'] < 264
) &&
$a['price']['vat'] <= 1.2 &&
(
    $a['qty'] > 30 ||
    (
        $a['eta'] < 3 || $a['eta'] > 30
    )
) &&
in_array($a['countriesAvailable'], array('US', 'CA'))

I have little experience building expression evaluators. My idea is to traverse the query from the innermost level to the outermost level, calling the corresponding MongoDB operator methods as needed.

Assuming $a matches the query, this would be the evaluation plan:

$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = array();
$query['price']['$gt'] = true;
$query['price']['$lt'] = true;
$query['price']['vat'] = array();
$query['price']['vat']['$lte'] = true;
$query['$or'] = array();
$query['$or']['qty'] = array();
$query['$or']['qty']['$gt'] = false;
$query['$or']['eta'] = array();
$query['$or']['eta']['$or'] = array();
$query['$or']['eta']['$or']['$lt'] = true;
$query['$or']['eta']['$or']['$gt'] = false;
$query['countriesAvailable'] = array();
$query['countriesAvailable']['$in'] = true;

The second step:

$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = array();
$query['price']['$gt'] = true;
$query['price']['$lt'] = true;
$query['price']['vat'] = true;
$query['$or'] = array();
$query['$or']['qty'] false;
$query['$or']['eta'] = array();
$query['$or']['eta']['$or'] true;
$query['countriesAvailable'] = true;

The third step:

$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = true;
$query['$or'] = array();
$query['$or']['qty'] false;
$query['$or']['eta'] true;
$query['countriesAvailable'] = true;

The fourth step:

$query = array();
$query['name'] = true;
$query['sale'] = true;
$query['price'] = true;
$query['$or'] = true;
$query['countriesAvailable'] = true;

Since all the values are booleans the evaluation ends returning !in_array(false, $query, true).

If a better approach exists, let me know.

Problem: Accessing Parent Array Keys


I'm stuck trying to get the innermost the elements and the relevant (ignoring operators) array index path, for instance, if I use a RecursiveIteratorIterator I get the correct values for the first iteration:

$nodes = new ArrayIterator($query);
$iterator = new RecursiveArrayIterator($nodes);
$iteratorIterator = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::LEAVES_ONLY);

print_r(iterator_to_array($iteratorIterator));

Array
(
    [name] => Kindle Fire HD
    [sale] => 1
    [$gt] => 30
    [$lt] => 3
    [$lte] => 1.2
    [0] => US
    [1] => CA
)

However, it's of little use since I cannot be sure what $a index the keys are referring to, not to mention that the key values are being overwritten by latter entries and the fact that I can't change their values.

I've also tried playing with RecursiveArrayIterator, but without the hasParent() / getParent() methods it doesn't seem to give me much advantage over simply foreach'ing the array.

Any suggestions?

4
  • $a['price'] > 199 will not make no sense when it is an array ($a['price']['vat'] <= 1.2). Commented Dec 28, 2012 at 13:54
  • @dev-null-dweller: Indeed. It's a dummy example, please ignore that. Commented Dec 28, 2012 at 13:56
  • Function to access Parent Array Keys: stackoverflow.com/questions/2504685/… Commented Dec 28, 2012 at 16:08
  • @JirilmonGeorge: That seems to be roughly equivalent to array_search($needle, $iteratorIterator);, not sure how that helps me. Commented Dec 28, 2012 at 17:08

1 Answer 1

3

I quickly read your question it sounds like you want to visit leafs and know the key path to them.

so here:

$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($myArray));
foreach ($ritit as $leafValue) {
    $keyPath = array();
    foreach (range(0, $ritit->getDepth()) as $depth) {
        $keyPath[] = $ritit->getSubIterator($depth)->key();
    }
    // do something with $keyPath


    // or
    $hasParent = $ritit->getDepth() > 0;
    $parentIter = $ritit->getSubIterator($ritit->getDepth() - 1);
    $parentKey = $parentIter->key();
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.