Hello list. Please find attached, a proposal for geo distance search support of solr.
I'd like to point out a few issues I still see: 1) I figured altering the interface would be a bad idea, so I opted for altering ezcSearchQuerySolr::eq() to handle distance search fields. This is not extraordinarily pretty, but IMHO acceptable. 2) I added a struct for the search value, i.e. a geoposition (lat,lng) and a distance (km). Search field and value are stored in the query object in eq(). 3) ezcSearchSolrHandler::find() extracts a number of values from $query and passes them to search(). In order to add the geoposition search field and values, I had to extend search() and buildQuery() by these parameters. I wonder, why search() is not integrated into find() or $query passed to search() as a whole. 4) I had to alter the $queryWord join() command in find() in order to remove the NULL element present due to adding the geoposition search field via eq(). Afterthought: It looks as if ezcSearchSolrHandler might be extended by something like filterDistance($field, $value) without affecting the interface, which should eleminate the flaws I pointed out in 1) and 4). I would appreciate your comments and hints on oversights or problems. Also tell me what I should do next if there is a chance to integrate the geo distance search functionality to ezcSearch. Thanks. Best regards, David -- David Rekowski mailto:david.rekow...@gmx.de
Index: handlers/solr.php =================================================================== --- handlers/solr.php (revision 1180804) +++ handlers/solr.php (working copy) @@ -340,7 +340,7 @@ * @param array(string=>string) $order * @return array */ - private function buildQuery( $queryWord, $defaultField, $searchFieldList = array(), $returnFieldList = array(), $highlightFieldList = array(), $facetFieldList = array(), $limit = null, $offset = false, $order = array() ) + private function buildQuery( $queryWord, $defaultField, $searchFieldList = array(), $returnFieldList = array(), $highlightFieldList = array(), $facetFieldList = array(), $limit = null, $offset = false, $order = array(), $distanceField = '', $geoDistance = 0 ) { if ( count( $searchFieldList ) > 0 ) { @@ -359,6 +359,13 @@ $returnFieldList[] = 'score'; $queryFlags['fl'] = join( ' ', $returnFieldList ); + if ($distanceField != '') { + $queryFlags['fq'] = '{!geofilt}'; + $queryFlags['pt'] = $geoDistance->lat.','.$geoDistance->lng; + $queryFlags['d'] = $geoDistance->distance; + $queryFlags['sfield'] = $distanceField; + } + if ( count( $highlightFieldList ) ) { $queryFlags['hl'] = 'true'; @@ -510,9 +517,9 @@ * @param array(string=>string) $order * @return stdClass */ - public function search( $queryWord, $defaultField, $searchFieldList = array(), $returnFieldList = array(), $highlightFieldList = array(), $facetFieldList = array(), $limit = null, $offset = 0, $order = array() ) + public function search( $queryWord, $defaultField, $searchFieldList = array(), $returnFieldList = array(), $highlightFieldList = array(), $facetFieldList = array(), $limit = null, $offset = 0, $order = array(), $distanceField = '', $geoDistance = 0 ) { - $result = $this->sendRawGetCommand( 'select', $this->buildQuery( $queryWord, $defaultField, $searchFieldList, $returnFieldList, $highlightFieldList, $facetFieldList, $limit, $offset, $order ) ); + $result = $this->sendRawGetCommand( 'select', $this->buildQuery( $queryWord, $defaultField, $searchFieldList, $returnFieldList, $highlightFieldList, $facetFieldList, $limit, $offset, $order, $distanceField, $geoDistance ) ); if ( ( $data = json_decode( $result ) ) === null ) { throw new ezcSearchInvalidResultException( $result ); @@ -568,15 +575,17 @@ */ public function find( ezcSearchFindQuery $query ) { - $queryWord = join( ' AND ', $query->whereClauses ); + $queryWord = join( ' AND ', array_diff($query->whereClauses, array(NULL))); $resultFieldList = $query->resultFields; $highlightFieldList = $query->highlightFields; $facetFieldList = $query->facets; $limit = $query->limit; $offset = $query->offset; $order = $query->orderByClauses; + $geoDistanceField = $query->geoDistanceField; + $geoDistanceValue = $query->geoDistanceValue; - $res = $this->search( $queryWord, '', array(), $resultFieldList, $highlightFieldList, $facetFieldList, $limit, $offset, $order ); + $res = $this->search( $queryWord, '', array(), $resultFieldList, $highlightFieldList, $facetFieldList, $limit, $offset, $order, $geoDistanceField, $geoDistanceValue ); return $this->createResponseFromData( $query->getDefinition(), $res ); } Index: abstraction/implementations/solr.php =================================================================== --- abstraction/implementations/solr.php (revision 1180804) +++ abstraction/implementations/solr.php (working copy) @@ -278,14 +278,19 @@ $this->checkIfFieldExists( $field ); $fieldType = $this->definition->fields[$field]->type; - $value = $this->handler->mapFieldValueForSearch( $fieldType, $value ); - $fieldName = $this->handler->mapFieldType( $field, $this->definition->fields[$field]->type ); - - $ret = "$fieldName:$value"; - - if ( $this->definition->fields[$field]->boost != 1 ) - { - $ret .= "^{$this->definition->fields[$field]->boost}"; + if ($fieldType == ezcSearchDocumentDefinition::GEOPOSITION) { + $this->geoDistanceField = $field; + $this->geoDistanceValue = $value; + } else { + $value = $this->handler->mapFieldValueForSearch( $fieldType, $value ); + $fieldName = $this->handler->mapFieldType( $field, $this->definition->fields[$field]->type ); + + $ret = "$fieldName:$value"; + + if ( $this->definition->fields[$field]->boost != 1 ) + { + $ret .= "^{$this->definition->fields[$field]->boost}"; + } } return $ret; } Index: document_definition.php =================================================================== --- document_definition.php (revision 1180804) +++ document_definition.php (working copy) @@ -51,6 +51,8 @@ */ const BOOLEAN = 7; + const GEOPOSITION = 8; + /** * Contains the document type - which is the same as the class name. *
<?php class ezcSearchSolrGeoDistance { public $lat = 0.0; public $lng = 0.0; public $distance = 10; public function __construct($lat, $lng, $distance) { $this->lat = $lat; $this->lng = $lng; $this->distance = $distance; } }