/** * merge one MingoCriteria instance into this MingoCriteria instance * * @since 1-3-12 * @param \MingoCriteria $criteria the criteria to merge into this one * @return self */ public function merge(MingoCriteria $criteria) { $this->command_symbol = $criteria->getCommandSymbol(); $this->map_where = $criteria->getWhere(); $this->map_sort = $criteria->getSort(); $this->field_map = $criteria->getFields(); if ($criteria->hasLimit()) { $this->setLimit($criteria->getLimit()); } //if if ($criteria->hasPage()) { $this->setPage($criteria->getPage()); } //if if ($criteria->hasOffset()) { $this->setOffset($criteria->getOffset()); } //if return $this; }
/** * this should be used to take the generic $where_criteria and turn it into something * the interface can use (eg, for a SQL interface, the $where_criteria would be turned * into a valid SQL string). * * @param MingoTable $table * @param MingoCriteria $where_criteria * @return mixed return whatever you want, however you want to return it, whatever is easiest for you */ protected function normalizeCriteria(MingoTable $table, MingoCriteria $where_criteria = null) { $ret_map = array(); $ret_map['select_str'] = '*'; $ret_map['table_str'] = $this->normalizeTableSQL($table); $ret_map['where_criteria'] = $where_criteria; $ret_map['where_str'] = ''; $ret_map['where_params'] = array(); $ret_map['sort_str'] = ''; $ret_map['limit_str'] = ''; $ret_map['limit'] = array(0, 0); // canary if (empty($where_criteria)) { return $ret_map; } //if $ret_where = $ret_sort = ''; $criteria_where = $where_criteria->getWhere(); $criteria_sort = $where_criteria->getSort(); $command_symbol = $where_criteria->getCommandSymbol(); foreach ($criteria_where as $name => $map) { $where_sql = ''; $where_val = array(); $name_sql = $this->normalizeNameSQL($name); if (is_array($map)) { $total_map = count($map); // go through each map val and append it to the sql string... foreach ($map as $command => $val) { if ($where_criteria->isCommand($command)) { $command_bare = $where_criteria->getCommand($command); $command_sql = ''; $command_val = array(); // build the sql... if (isset($this->method_map[$command_bare])) { $symbol = empty($this->method_map[$command_bare]['symbol']) ? '' : $this->method_map[$command_bare]['symbol']; if (!empty($this->method_map[$command_bare]['arg'])) { $callback = $this->method_map[$command_bare]['arg']; list($command_sql, $command_val) = $this->{$callback}($symbol, $name_sql, $map[$command]); } //if list($where_sql, $where_val) = $this->appendSql('AND', $command_sql, $command_val, $where_sql, $where_val); } //if } else { throw new UnexpectedValueException(sprintf('there is an error in the internal structure of your %s instance', get_class($where_criteria))); } //if/else } //foreach // we want to parenthesize the sql since there was more than one value for the field if ($total_map > 1) { $where_sql = sprintf(' (%s)', trim($where_sql)); } //if } else { // we have a NAME=VAL (an is* method call)... list($where_sql, $where_val) = $this->normalizeValSql('=', $name_sql, $map); } //if/else list($ret_map['where_str'], $ret_map['where_params']) = $this->appendSql('AND', $where_sql, $where_val, $ret_map['where_str'], $ret_map['where_params']); } //foreach if (!empty($ret_map['where_params'])) { $ret_map['where_str'] = sprintf('WHERE%s', $ret_map['where_str']); } //if // build the sort sql... foreach ($criteria_sort as $name => $direction) { $name_sql = $this->normalizeNameSQL($name); $dir_sql = $direction > 0 ? 'ASC' : 'DESC'; if (empty($ret_map['sort_sql'])) { $ret_map['sort_str'] = sprintf('ORDER BY %s %s', $name_sql, $dir_sql); } else { $ret_map['sort_str'] = sprintf('%s,%s %s', $ret_map['sort_sql'], $name_sql, $dir_sql); } //if/else } //foreach $ret_map = $this->normalizeLimitCriteria($ret_map, $where_criteria->getLimit(), $where_criteria->getOffset()); return $ret_map; }
/** * this should be used to take the generic $where_criteria and turn it into something * the interface can use (eg, for a SQL interface, the $where_criteria would be turned * into a valid SQL string). * * currently not supported: 'near' * * @link http://lucene.apache.org/java/2_4_0/queryparsersyntax.html * * @param MingoTable $table * @param MingoCriteria $where_criteria * @return mixed return whatever you want, however you want to return it, whatever is easiest for you */ protected function normalizeCriteria(MingoTable $table, MingoCriteria $where_criteria = null) { $ret_map = array(); $ret_map['where_criteria'] = $where_criteria; $ret_map['limit'] = array(0, 0); $query = new Zend_Search_Lucene_Search_Query_Boolean(); if ($where_criteria !== null) { // add all the required search keys and their values... $criteria_where = $where_criteria->getWhere(); foreach ($criteria_where as $name => $map) { $subquery = null; $where_sql = ''; $where_val = array(); $required = true; if (is_array($map)) { $command = $where_criteria->normalizeCommand('in'); if (isset($map[$command])) { $subquery = $this->handleMulti($name, $map[$command], true); $required = true; } //if // according to: http://lucene.apache.org/java/2_4_0/queryparsersyntax.html // Lucene cannot do nin queries without something before it (eg, foo:1 NOT foo(2 3) but // (NOT foo(2 3) doesn't work) $command = $where_criteria->normalizeCommand('nin'); if (isset($map[$command])) { $subquery = $this->handleMulti($name, $map[$command], false); $required = false; } //if $command = $where_criteria->normalizeCommand('not'); if (isset($map[$command])) { $subquery = $this->handleEquals($name, $map[$command], false); } //if $command1 = $where_criteria->normalizeCommand('gte'); $command2 = $where_criteria->normalizeCommand('lte'); if (isset($map[$command1]) && isset($map[$command2])) { $subquery = $this->handleRange($name, $map[$command1], $map[$command2], true); } //if if (isset($map[$command1])) { $subquery = $this->handleRange($name, $map[$command1], null, true); } //if if (isset($map[$command2])) { $subquery = $this->handleRange($name, null, $map[$command2], true); } //if $command = $where_criteria->normalizeCommand('gt'); if (isset($map[$command])) { $subquery = $this->handleRange($name, $map[$command], null, false); } //if $command = $where_criteria->normalizeCommand('lt'); if (isset($map[$command])) { $subquery = $this->handleRange($name, null, $map[$command], false); } //if } else { if ($name === '_q') { $subquery = Zend_Search_Lucene_Search_QueryParser::parse($map); } else { $subquery = $this->handleEquals($name, $map, true); } //if/else } //if/else if ($subquery) { $query->addSubquery($subquery, $required); } //method } //foreach // limit offset... $offset = $where_criteria->getOffset(); $ret_map['limit'] = array($where_criteria->getLimit() + $offset, $offset); // caution from docs: // Please use caution when using a non-default search order; // the query needs to retrieve documents completely from an index, // which may dramatically reduce search performance. // http://framework.zend.com/manual/en/zend.search.lucene.searching.html#zend.search.lucene.searching.sorting if ($where_criteria->hasSort()) { $criteria_sort = $where_criteria->getSort(); $sort_list = array(); // build the sort sql... foreach ($criteria_sort as $name => $direction) { $sort_list[] = $name; $sort_list[] = SORT_REGULAR; ///$sort_list[] = SORT_STRING; ///SORT_NUMERIC; $sort_list[] = $direction > 0 ? SORT_ASC : SORT_DESC; } //foreach $ret_map['sort'] = $sort_list; } //if } //if $ret_map['query'] = $query; return $ret_map; }
/** * find the index table name from the table and the list of fields the index comprises * * @param MingoTable $table the main table's name * @param MingoCriteria $where_criteria * @return string the index table name */ protected function findIndexTableName(MingoTable $table, MingoCriteria $where_criteria) { if (!$where_criteria->hasWhere() && !$where_criteria->hasSort()) { return ''; } //if $ret_str = ''; $is_match = false; $where_map = $where_criteria->getWhere(); $sort_map = $where_criteria->getSort(); // php >= 5.3, use when Mingo is ported to namespaces... ///$field_list = array_keys(array_replace($where_map,$sort_map)); // we need to get a list of all the fields used in the order they will be used $field_list = array_keys($where_map); if (!empty($sort_map)) { $field_list = array_unique(array_merge($field_list, array_keys($sort_map))); } //if // now go through the index and see if it matches all the fields... foreach ($table->getIndexes() as $index) { $field_i = 0; foreach ($index->getFieldNames() as $field) { if (isset($field_list[$field_i])) { if ($field === $field_list[$field_i]) { $is_match = true; $field_i++; } else { $is_match = false; break; } //if/else } else { break; } //if/else } //foreach if ($is_match) { // we're done, we found a match... $ret_str = $this->getIndexTableName($table, $index); break; } //if } //foreach if (!$is_match) { // since we couldn't find an index table, make sure the query can be valid // on the main table // we are selecting on the main table (no index is being used) so we can only // select or sort on 4 fields (by default): _id, _created, and _updated foreach ($field_list as $field) { // if a field in the where map is not in the main table we've got trouble // since an index table couldn't be found if (!in_array($field, $this->non_body_fields)) { $e_msg = sprintf('Could not match fields: [%s] sorted by fields: [%s] with an index table.', join(',', array_keys($where_map)), join(',', array_keys($sort_map))); // list the available index tables if we are in debug mode... if ($this->hasDebug()) { $e_msg .= ' Indexes available: '; $index_list = $this->getIndexes($table); $e_index_list = array(); foreach ($index_list as $index) { $e_index_list[] = sprintf('%s(%s)', $index->getName(), join(',', $index->getFieldNames())); } //foreach $e_msg .= join(', ', $e_index_list); } //if throw new RuntimeException($e_msg); } //if } //foreach } //if/else return $ret_str; }