Рассмотрим следующий (довольно сложный) запрос, выраженный в этом объекте JSON:
{ "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" ] } }
Я хочу проанализировать этот JSON так, чтобы он оценивал эквивалент PHP (где $a
– мои целевые данные):
$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'))
У меня мало опыта создания оценщиков выражений. Моя идея состоит в том, чтобы пройти запрос от самого внутреннего уровня до самого внешнего уровня, вызвав соответствующие методы оператора MongoDB по мере необходимости.
Предполагая, что $a
соответствует запросу, это будет план оценки:
$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;
Второй шаг:
$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;
Третий шаг:
$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;
Четвертый шаг:
$query = array(); $query['name'] = true; $query['sale'] = true; $query['price'] = true; $query['$or'] = true; $query['countriesAvailable'] = true;
Поскольку все значения являются логическими, оценка заканчивается возвратом !in_array(false, $query, true)
.
Если есть лучший подход, дайте мне знать.
Я застреваю, пытаясь получить самые внутренние элементы и соответствующий (игнорирующий операторов) путь индекса массива, например, если я использую RecursiveIteratorIterator
я получаю правильные значения для первой итерации:
$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 )
Тем не менее, это малопригодно, так как я не могу быть уверен в том, что $a
индекс, на который ссылаются ключи, не говоря уже о том, что значения ключа переписываются последними записями и тот факт, что я не могу изменить их значения.
Я также пробовал играть с RecursiveArrayIterator
, но без hasParent()
/ getParent()
это, похоже, не дает мне много преимуществ перед простое вычисление массива.
Какие-либо предложения?
Я быстро прочитал ваш вопрос, похоже, что вы хотите посетить листья и узнать ключ к ним.
так вот:
$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(); }