class Solr_Base_Query in Apache Solr Search 5
Same name and namespace in other branches
- 5.2 Solr_Base_Query.php \Solr_Base_Query
- 6 Solr_Base_Query.php \Solr_Base_Query
- 6.2 Solr_Base_Query.php \Solr_Base_Query
Hierarchy
- class \Solr_Base_Query
Expanded class hierarchy of Solr_Base_Query
File
- ./
Solr_Base_Query.php, line 3
View source
class Solr_Base_Query {
/**
* Luke query to find out what fields the Lucene index already knows about.
* TODO: Does this belong in this class? Or in a Service class? Or in the module itself?
*/
static function get_fields_in_index() {
static $fields;
if (empty($fields)) {
// TODO: The apachesolr_base_url() is the only dependency on the module. Make it a static
// class method?
$response = drupal_http_request(apachesolr_base_url() . "/admin/luke?numTerms=0&wt=json");
if ($response->code == '200') {
$data = json_decode($response->data);
}
}
return $data->fields;
}
/**
* This is copied from search module. The search module implementation doesn't
* handle quoted terms correctly (bug) and this function is copied here until
* I have the bugfix perfected, at which point a patch will be submitted to search
* module with the goal of removing the function here.
*
* Extract a module-specific search option from a search query. e.g. 'type:book'
*/
static function query_extract($keys, $option) {
$pattern = '/(^| )' . $option . ':(\\"([^\\"]*)\\")/i';
preg_match_all($pattern, $keys, $matches);
if (!empty($matches[2])) {
// The preg_replace removes beginning and trailing quotations.
return preg_replace('/^"|"$/', '', $matches[2]);
}
$pattern = '/(^| )' . $option . ':([^ ]*)/i';
if (preg_match_all($pattern, $keys, $matches)) {
if (!empty($matches[2])) {
return $matches[2];
}
}
}
/**
* Replaces all occurances of $option in $keys.
*/
static function query_replace($keys, $option) {
$matches = Solr_Base_Query::query_extract($keys, $option);
if (count($matches) > 0) {
foreach ($matches as $match) {
// TODO: Make some sort of name->value container object.
$found = Solr_Base_Query::make_field(array(
'#name' => $option,
'#value' => $match,
));
$keys = str_replace($found, '', $keys);
}
}
return $keys;
}
/**
* Takes an array $values and combines the #name and #value in a way
* suitable for use in a Solr query.
*/
static function make_field(array $values) {
if (empty($values['#name'])) {
return implode(' ', array_filter(explode(' ', $values['#value']), 'trim'));
}
else {
// if the field value has spaces in it, wrap it in double quotes.
if (count(explode(' ', $values['#value'])) > 1) {
$values['#value'] = '"' . $values['#value'] . '"';
}
return $values['#name'] . ':' . $values['#value'];
}
}
/**
* A keyed array where the key is a position integer and the value
* is an array with #name and #value properties.
*/
private $_fields;
/**
* An array of subqueries.
*/
private $_subqueries = array();
/**
* The query string.
*/
private $_query;
/**
* Should fields be AND'd or OR'd together?
*/
private $_field_operator;
/**
* @param $querystring
* The string that a user would type into the search box. Suitable input
* may come from search_get_keys()
* @param $field_operator
* An object level operator. AND is the implicit default. All segments will
* be joined with this operator.
*/
function __construct($querystring, $field_operator = "AND") {
$this->_field_operator = $field_operator;
$this->_query = trim($querystring);
$this
->parse_query();
}
function add_field($field, $value) {
// microtime guarantees that added fields come at the end of the query,
// in order.
$this->_fields[microtime()] = array(
'#name' => $field,
'#value' => trim($value),
);
$this
->rebuild_query();
}
function get_fields() {
return $this->_fields;
}
function remove_field($name, $value = NULL) {
// We can only remove named fields.
if (empty($name)) {
return;
}
if (empty($value)) {
foreach ($this->_fields as $pos => $values) {
if ($values['#name'] == $name) {
unset($this->_fields[$pos]);
}
}
}
else {
foreach ($this->_fields as $pos => $values) {
if ($values['#name'] == $name && $values['#value'] == $value) {
unset($this->_fields[$pos]);
}
}
}
$this
->rebuild_query();
}
function has_field($name, $value) {
foreach ($this->_fields as $pos => $values) {
if (!empty($values['#name']) && !empty($values['#value']) && $values['#name'] == $name && $values['#value'] == $value) {
return TRUE;
}
}
return FALSE;
}
/**
* A subquery is another instance of a Solr_Base_Query that should be joined
* to the query. The operator determines whether it will be joined with AND or
* OR.
*
* @param $query
* An instance of Solr_Base_Query.
*
* @param $operator
* 'AND' or 'OR'
*/
function add_subquery(Solr_Base_Query $query, $operator = 'AND') {
$this->_subqueries[$query
->get_query_basic()] = array(
'#query' => $query,
'#operator' => $operator,
);
}
function remove_subquery(Solr_Base_Query $query) {
unset($this->_subqueries[$query
->get_query_basic()]);
}
function remove_subqueries() {
$this->_subqueries = array();
}
function get_query() {
$this
->rebuild_query();
return $this->_query;
}
/**
* A function to get just the keyword components of the query,
* omitting any field:value portions.
*/
function get_query_basic() {
$nonames = array_filter($this->_fields, create_function('$a', 'return empty($a[\'#name\']);'));
$result = array();
foreach ($nonames as $pos => $field) {
$result[] = $field['#value'];
}
return implode(' ', $result);
}
function get_breadcrumb() {
// This encodes an assumption that the breadcrumb is always building off
// of the current page. Could be a problem.
$breadcrumb = menu_get_active_breadcrumb();
// double check that the fields are ordered by position.
ksort($this->_fields);
$progressive_crumb = array();
// TODO: Don't know if hardcoding this is going to come back to bite.
$base = 'search/' . arg(1) . '/';
foreach ($this->_fields as $field) {
$progressive_crumb[] = Solr_Base_Query::make_field($field);
if (empty($field['#name'])) {
$breadcrumb[] = l($field['#value'], $base . implode(' ', $progressive_crumb));
}
else {
if ($themed = theme("apachesolr_breadcrumb_{$field['#name']}", $field['#value'])) {
$breadcrumb[] = l($themed, $base . implode(' ', $progressive_crumb));
}
else {
$breadcrumb[] = l($field['#value'], $base . implode(' ', $progressive_crumb));
}
}
}
// the last breadcrumb is the current page, so it shouldn't be a link.
$last = count($breadcrumb) - 1;
$breadcrumb[$last] = strip_tags($breadcrumb[$last]);
drupal_set_breadcrumb($breadcrumb);
return $breadcrumb;
}
private function parse_query() {
$this->_fields = array();
$_keys = $this->_query;
// Gets information about the fields already in solr index.
$index_fields = Solr_Base_Query::get_fields_in_index();
$rows = array();
foreach ((array) $index_fields as $name => $field) {
do {
// save the strlen so we can detect if it has changed at the bottom
// of the do loop
$a = (int) strlen($_keys);
// Get the values for $name
$values = Solr_Base_Query::query_extract($_keys, $name);
if (count($values) > 0) {
foreach ($values as $value) {
$found = Solr_Base_Query::make_field(array(
'#name' => $name,
'#value' => $value,
));
$pos = strpos($this->_query, $found);
// $solr_keys and $solr_crumbs are keyed on $pos so that query order
// is maintained. This is important for breadcrumbs.
$this->_fields[$pos] = array(
'#name' => $name,
'#value' => trim($value),
);
}
// Update the local copy of $_keys by removing the key that was just found.
$_keys = trim(Solr_Base_Query::query_replace($_keys, $name));
}
// Take new strlen to compare with $a.
$b = (int) strlen($_keys);
} while ($a !== $b);
// Clean up by adding remaining keywords.
if (!empty($_keys)) {
$pos = strpos($this->_query, $_keys);
$this->_fields[$pos] = array(
'#name' => '',
'#value' => trim($_keys),
);
}
}
// Even though the array has the right keys they are likely in the wrong
// order. ksort() sorts the array by key while maintaining the key.
ksort($this->_fields);
}
private function rebuild_query() {
$fields = array();
foreach ($this->_fields as $pos => $values) {
$fields[] = Solr_Base_Query::make_field($values);
}
$join_delim = $this->_field_operator == 'AND' ? ' ' : ' OR ';
$this->_query = trim(implode($join_delim, array_filter($fields, 'trim')));
foreach ($this->_subqueries as $id => $data) {
$operator = $data['#operator'];
$subquery = $data['#query']
->get_query();
$this->_query .= " {$operator} ({$subquery})";
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Solr_Base_Query:: |
private | property | A keyed array where the key is a position integer and the value is an array with #name and #value properties. | |
Solr_Base_Query:: |
private | property | Should fields be AND'd or OR'd together? | |
Solr_Base_Query:: |
private | property | The query string. | |
Solr_Base_Query:: |
private | property | An array of subqueries. | |
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
function | A subquery is another instance of a Solr_Base_Query that should be joined to the query. The operator determines whether it will be joined with AND or OR. | ||
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
static | function | Luke query to find out what fields the Lucene index already knows about. TODO: Does this belong in this class? Or in a Service class? Or in the module itself? | |
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
function | A function to get just the keyword components of the query, omitting any field:value portions. | ||
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
static | function | Takes an array $values and combines the #name and #value in a way suitable for use in a Solr query. | |
Solr_Base_Query:: |
private | function | ||
Solr_Base_Query:: |
static | function | This is copied from search module. The search module implementation doesn't handle quoted terms correctly (bug) and this function is copied here until I have the bugfix perfected, at which point a patch will be submitted to search module with the… | |
Solr_Base_Query:: |
static | function | Replaces all occurances of $option in $keys. | |
Solr_Base_Query:: |
private | function | ||
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
function | |||
Solr_Base_Query:: |
function |