-
- if (CONST_Debug) var_Dump($aGroupedSearches);
-
- if ($bReverseInPlan)
- {
- $aCopyGroupedSearches = $aGroupedSearches;
- foreach($aCopyGroupedSearches as $iGroup => $aSearches)
- {
- foreach($aSearches as $iSearch => $aSearch)
- {
- if (sizeof($aSearch['aAddress']))
- {
- $iReverseItem = array_pop($aSearch['aAddress']);
- if (isset($aPossibleMainWordIDs[$iReverseItem]))
- {
- $aSearch['aAddress'] = array_merge($aSearch['aAddress'], $aSearch['aName']);
- $aSearch['aName'] = array($iReverseItem);
- $aGroupedSearches[$iGroup][] = $aSearch;
- }
-// $aReverseSearch['aName'][$iReverseItem] = $iReverseItem;
- // $aGroupedSearches[$iGroup][] = $aReverseSearch;
- }
- }
- }
- }
-
- if (CONST_Search_TryDroppedAddressTerms && sizeof($aStructuredQuery) > 0)
- {
- $aCopyGroupedSearches = $aGroupedSearches;
- foreach($aCopyGroupedSearches as $iGroup => $aSearches)
- {
- foreach($aSearches as $iSearch => $aSearch)
- {
- $aReductionsList = array($aSearch['aAddress']);
- $iSearchRank = $aSearch['iSearchRank'];
- while(sizeof($aReductionsList) > 0)
- {
- $iSearchRank += 5;
- if ($iSearchRank > iMaxRank) break 3;
- $aNewReductionsList = array();
- foreach($aReductionsList as $aReductionsWordList)
- {
- for ($iReductionWord = 0; $iReductionWord < sizeof($aReductionsWordList); $iReductionWord++)
- {
- $aReductionsWordListResult = array_merge(array_slice($aReductionsWordList, 0, $iReductionWord), array_slice($aReductionsWordList, $iReductionWord+1));
- $aReverseSearch = $aSearch;
- $aSearch['aAddress'] = $aReductionsWordListResult;
- $aSearch['iSearchRank'] = $iSearchRank;
- $aGroupedSearches[$iSearchRank][] = $aReverseSearch;
- if (sizeof($aReductionsWordListResult) > 0)
- {
- $aNewReductionsList[] = $aReductionsWordListResult;
- }
- }
- }
- $aReductionsList = $aNewReductionsList;
- }
- }
- }
- ksort($aGroupedSearches);
- }
-
- // Filter out duplicate searches
- $aSearchHash = array();
- foreach($aGroupedSearches as $iGroup => $aSearches)
- {
- foreach($aSearches as $iSearch => $aSearch)
- {
- $sHash = serialize($aSearch);
- if (isset($aSearchHash[$sHash]))
- {
- unset($aGroupedSearches[$iGroup][$iSearch]);
- if (sizeof($aGroupedSearches[$iGroup]) == 0) unset($aGroupedSearches[$iGroup]);
- }
- else
- {
- $aSearchHash[$sHash] = 1;
- }
- }
- }
-
- if (CONST_Debug) _debugDumpGroupedSearches($aGroupedSearches, $aValidTokens);
-
- $iGroupLoop = 0;
- $iQueryLoop = 0;
- foreach($aGroupedSearches as $iGroupedRank => $aSearches)
- {
- $iGroupLoop++;
- foreach($aSearches as $aSearch)
- {
- $iQueryLoop++;
-
- // Must have a location term
- if (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && !$aSearch['fLon'])
- {
- if ($aSearch['sCountryCode'] && !$aSearch['sClass'] && !$aSearch['sHouseNumber'])
- {
- if (4 >= $iMinAddressRank && 4 <= $iMaxAddressRank)
- {
- $sSQL = "select place_id from placex where country_code='".$aSearch['sCountryCode']."' and rank_search = 4";
- if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";
- $sSQL .= " order by st_area(geometry) desc limit 1";
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- }
- else
- {
- if (!$bBoundingBoxSearch && !$aSearch['fLon']) continue;
- if (!$aSearch['sClass']) continue;
- if (CONST_Debug) var_dump('<hr>',$aSearch);
- if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens);
- $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'";
- if ($oDB->getOne($sSQL))
- {
- $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." ct";
- if ($sCountryCodesSQL) $sSQL .= " join placex using (place_id)";
- $sSQL .= " where st_contains($sViewboxSmallSQL, ct.centroid)";
- if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";
- if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, ct.centroid) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
-
- if (!sizeof($aPlaceIDs))
- {
- $sSQL = "select place_id from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." ct";
- if ($sCountryCodesSQL) $sSQL .= " join placex using (place_id)";
- $sSQL .= " where st_contains($sViewboxLargeSQL, ct.centroid)";
- if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";
- if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, ct.centroid) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- }
- else
- {
- $sSQL = "select place_id from placex where class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
- $sSQL .= " and st_contains($sViewboxSmallSQL, geometry) and linked_place_id is null";
- if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";
- if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
- }
- }
- else
- {
- if (CONST_Debug) var_dump('<hr>',$aSearch);
- if (CONST_Debug) _debugDumpGroupedSearches(array($iGroupedRank => array($aSearch)), $aValidTokens);
- $aPlaceIDs = array();
-
- // First we need a position, either aName or fLat or both
- $aTerms = array();
- $aOrder = array();
-
- // TODO: filter out the pointless search terms (2 letter name tokens and less)
- // they might be right - but they are just too darned expensive to run
- if (sizeof($aSearch['aName'])) $aTerms[] = "name_vector @> ARRAY[".join($aSearch['aName'],",")."]";
- if (sizeof($aSearch['aAddress']) && $aSearch['aName'] != $aSearch['aAddress'])
- {
- // For infrequent name terms disable index usage for address
- if (CONST_Search_NameOnlySearchFrequencyThreshold &&
- sizeof($aSearch['aName']) == 1 &&
- $aPossibleMainWordIDs[$aSearch['aName'][reset($aSearch['aName'])]] < CONST_Search_NameOnlySearchFrequencyThreshold)
- {
- $aTerms[] = "array_cat(nameaddress_vector,ARRAY[]::integer[]) @> ARRAY[".join($aSearch['aAddress'],",")."]";
- }
- else
- {
- $aTerms[] = "nameaddress_vector @> ARRAY[".join($aSearch['aAddress'],",")."]";
- }
- }
- if ($aSearch['sCountryCode']) $aTerms[] = "country_code = '".pg_escape_string($aSearch['sCountryCode'])."'";
- if ($aSearch['sHouseNumber']) $aTerms[] = "address_rank in (26,27)";
- if ($aSearch['fLon'] && $aSearch['fLat'])
- {
- $aTerms[] = "ST_DWithin(centroid, ST_SetSRID(ST_Point(".$aSearch['fLon'].",".$aSearch['fLat']."),4326), ".$aSearch['fRadius'].")";
- $aOrder[] = "ST_Distance(centroid, ST_SetSRID(ST_Point(".$aSearch['fLon'].",".$aSearch['fLat']."),4326)) ASC";
- }
- if (sizeof($aExcludePlaceIDs))
- {
- $aTerms[] = "place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- if ($sCountryCodesSQL)
- {
- $aTerms[] = "country_code in ($sCountryCodesSQL)";
- }
-
- if ($bBoundingBoxSearch) $aTerms[] = "centroid && $sViewboxSmallSQL";
- if ($sNearPointSQL) $aOrder[] = "ST_Distance($sNearPointSQL, centroid) asc";
-
- $sImportanceSQL = 'case when importance = 0 OR importance IS NULL then 0.75-(search_rank::float/40) else importance end';
-
- if ($sViewboxSmallSQL) $sImportanceSQL .= " * case when ST_Contains($sViewboxSmallSQL, centroid) THEN 1 ELSE 0.5 END";
- if ($sViewboxLargeSQL) $sImportanceSQL .= " * case when ST_Contains($sViewboxLargeSQL, centroid) THEN 1 ELSE 0.5 END";
- $aOrder[] = "$sImportanceSQL DESC";
-
- if (sizeof($aTerms))
- {
- $sSQL = "select place_id";
- $sSQL .= " from search_name";
- $sSQL .= " where ".join(' and ',$aTerms);
- $sSQL .= " order by ".join(', ',$aOrder);
- if ($aSearch['sHouseNumber'] || $aSearch['sClass'])
- $sSQL .= " limit 50";
- elseif (!sizeof($aSearch['aName']) && !sizeof($aSearch['aAddress']) && $aSearch['sClass'])
- $sSQL .= " limit 1";
- else
- $sSQL .= " limit ".$iLimit;
-
- if (CONST_Debug) { var_dump($sSQL); }
- $aViewBoxPlaceIDs = $oDB->getAll($sSQL);
- if (PEAR::IsError($aViewBoxPlaceIDs))
- {
- failInternalError("Could not get places for search terms.", $sSQL, $aViewBoxPlaceIDs);
- }
-//var_dump($aViewBoxPlaceIDs);
- // Did we have an viewbox matches?
- $aPlaceIDs = array();
- $bViewBoxMatch = false;
- foreach($aViewBoxPlaceIDs as $aViewBoxRow)
- {
-// if ($bViewBoxMatch == 1 && $aViewBoxRow['in_small'] == 'f') break;
-// if ($bViewBoxMatch == 2 && $aViewBoxRow['in_large'] == 'f') break;
-// if ($aViewBoxRow['in_small'] == 't') $bViewBoxMatch = 1;
-// else if ($aViewBoxRow['in_large'] == 't') $bViewBoxMatch = 2;
- $aPlaceIDs[] = $aViewBoxRow['place_id'];
- }
- }
-//var_Dump($aPlaceIDs);
-//exit;
-
- if ($aSearch['sHouseNumber'] && sizeof($aPlaceIDs))
- {
- $aRoadPlaceIDs = $aPlaceIDs;
- $sPlaceIDs = join(',',$aPlaceIDs);
-
- // Now they are indexed look for a house attached to a street we found
- $sHouseNumberRegex = '\\\\m'.str_replace(' ','[-,/ ]',$aSearch['sHouseNumber']).'\\\\M';
- $sSQL = "select place_id from placex where parent_place_id in (".$sPlaceIDs.") and housenumber ~* E'".$sHouseNumberRegex."'";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
-
- // If not try the aux fallback table
- if (!sizeof($aPlaceIDs))
- {
- $sSQL = "select place_id from location_property_aux where parent_place_id in (".$sPlaceIDs.") and housenumber = '".pg_escape_string($aSearch['sHouseNumber'])."'";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
-// $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
-
- if (!sizeof($aPlaceIDs))
- {
- $sSQL = "select place_id from location_property_tiger where parent_place_id in (".$sPlaceIDs.") and housenumber = '".pg_escape_string($aSearch['sHouseNumber'])."'";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
-// $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- }
-
- // Fallback to the road
- if (!sizeof($aPlaceIDs) && preg_match('/[0-9]+/', $aSearch['sHouseNumber']))
- {
- $aPlaceIDs = $aRoadPlaceIDs;
- }
-
- }
-
- if ($aSearch['sClass'] && sizeof($aPlaceIDs))
- {
- $sPlaceIDs = join(',',$aPlaceIDs);
-
- $aClassPlaceIDs = array();
-
- if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'name')
- {
- // If they were searching for a named class (i.e. 'Kings Head pub') then we might have an extra match
- $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and class='".$aSearch['sClass']."' and type='".$aSearch['sType']."'";
- $sSQL .= " and linked_place_id is null";
- if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";
- $sSQL .= " order by rank_search asc limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aClassPlaceIDs = $oDB->getCol($sSQL);
- }
-
- if (!$aSearch['sOperator'] || $aSearch['sOperator'] == 'near') // & in
- {
- $sSQL = "select count(*) from pg_tables where tablename = 'place_classtype_".$aSearch['sClass']."_".$aSearch['sType']."'";
- $bCacheTable = $oDB->getOne($sSQL);
-
- $sSQL = "select min(rank_search) from placex where place_id in ($sPlaceIDs)";
-
- if (CONST_Debug) var_dump($sSQL);
- $iMaxRank = ((int)$oDB->getOne($sSQL));
-
- // For state / country level searches the normal radius search doesn't work very well
- $sPlaceGeom = false;
- if ($iMaxRank < 9 && $bCacheTable)
- {
- // Try and get a polygon to search in instead
- $sSQL = "select geometry from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank + 5 and st_geometrytype(geometry) in ('ST_Polygon','ST_MultiPolygon') order by rank_search asc limit 1";
- if (CONST_Debug) var_dump($sSQL);
- $sPlaceGeom = $oDB->getOne($sSQL);
- }
-
- if ($sPlaceGeom)
- {
- $sPlaceIDs = false;
- }
- else
- {
- $iMaxRank += 5;
- $sSQL = "select place_id from placex where place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
- if (CONST_Debug) var_dump($sSQL);
- $aPlaceIDs = $oDB->getCol($sSQL);
- $sPlaceIDs = join(',',$aPlaceIDs);
- }
-
- if ($sPlaceIDs || $sPlaceGeom)
- {
-
- $fRange = 0.01;
- if ($bCacheTable)
- {
- // More efficient - can make the range bigger
- $fRange = 0.05;
-
- $sOrderBySQL = '';
- if ($sNearPointSQL) $sOrderBySQL = "ST_Distance($sNearPointSQL, l.centroid)";
- else if ($sPlaceIDs) $sOrderBySQL = "ST_Distance(l.centroid, f.geometry)";
- else if ($sPlaceGeom) $sOrderBysSQL = "ST_Distance(st_centroid('".$sPlaceGeom."'), l.centroid)";
-
- $sSQL = "select distinct l.place_id".($sOrderBySQL?','.$sOrderBySQL:'')." from place_classtype_".$aSearch['sClass']."_".$aSearch['sType']." as l";
- if ($sCountryCodesSQL) $sSQL .= " join placex as lp using (place_id)";
- if ($sPlaceIDs)
- {
- $sSQL .= ",placex as f where ";
- $sSQL .= "f.place_id in ($sPlaceIDs) and ST_DWithin(l.centroid, f.centroid, $fRange) ";
- }
- if ($sPlaceGeom)
- {
- $sSQL .= " where ";
- $sSQL .= "ST_Contains('".$sPlaceGeom."', l.centroid) ";
- }
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- if ($sCountryCodesSQL) $sSQL .= " and lp.country_code in ($sCountryCodesSQL)";
- if ($sOrderBySQL) $sSQL .= "order by ".$sOrderBySQL." asc";
- if ($iOffset) $sSQL .= " offset $iOffset";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aClassPlaceIDs = array_merge($aClassPlaceIDs, $oDB->getCol($sSQL));
- }
- else
- {
- if (isset($aSearch['fRadius']) && $aSearch['fRadius']) $fRange = $aSearch['fRadius'];
-
- $sOrderBySQL = '';
- if ($sNearPointSQL) $sOrderBySQL = "ST_Distance($sNearPointSQL, l.geometry)";
- else $sOrderBySQL = "ST_Distance(l.geometry, f.geometry)";
-
- $sSQL = "select distinct l.place_id".($sOrderBysSQL?','.$sOrderBysSQL:'')." from placex as l,placex as f where ";
- $sSQL .= "f.place_id in ( $sPlaceIDs) and ST_DWithin(l.geometry, f.centroid, $fRange) ";
- $sSQL .= "and l.class='".$aSearch['sClass']."' and l.type='".$aSearch['sType']."' ";
- if (sizeof($aExcludePlaceIDs))
- {
- $sSQL .= " and l.place_id not in (".join(',',$aExcludePlaceIDs).")";
- }
- if ($sCountryCodesSQL) $sSQL .= " and l.country_code in ($sCountryCodesSQL)";
- if ($sOrderBy) $sSQL .= "order by ".$OrderBysSQL." asc";
- if ($iOffset) $sSQL .= " offset $iOffset";
- $sSQL .= " limit $iLimit";
- if (CONST_Debug) var_dump($sSQL);
- $aClassPlaceIDs = array_merge($aClassPlaceIDs, $oDB->getCol($sSQL));
- }
- }
- }
-
- $aPlaceIDs = $aClassPlaceIDs;
-
- }
-
- }
-
- if (PEAR::IsError($aPlaceIDs))
- {
- failInternalError("Could not get place IDs from tokens." ,$sSQL, $aPlaceIDs);
- }
-
- if (CONST_Debug) var_Dump($aPlaceIDs);
-
- foreach($aPlaceIDs as $iPlaceID)
- {
- $aResultPlaceIDs[$iPlaceID] = $iPlaceID;
- }
- if ($iQueryLoop > 20) break;
- }
-
- //exit;
- if (isset($aResultPlaceIDs) && sizeof($aResultPlaceIDs)) break;
- if ($iGroupLoop > 4) break;
- if ($iQueryLoop > 30) break;
- }
-//exit;
- // Did we find anything?
- if (isset($aResultPlaceIDs) && sizeof($aResultPlaceIDs))
- {
-//var_Dump($aResultPlaceIDs);exit;
- // Get the details for display (is this a redundant extra step?)
- $sPlaceIDs = join(',',$aResultPlaceIDs);
- $sOrderSQL = 'CASE ';
- foreach(array_keys($aResultPlaceIDs) as $iOrder => $iPlaceID)
- {
- $sOrderSQL .= 'when min(place_id) = '.$iPlaceID.' then '.$iOrder.' ';
- }
- $sOrderSQL .= ' ELSE 10000000 END';
- $sSQL = "select osm_type,osm_id,class,type,admin_level,rank_search,rank_address,min(place_id) as place_id,country_code,";
- $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
- $sSQL .= "get_name_by_language(name, $sLanguagePrefArraySQL) as placename,";
- $sSQL .= "get_name_by_language(name, ARRAY['ref']) as ref,";
- $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, ";
-// $sSQL .= $sOrderSQL." as porder, ";
- $sSQL .= "coalesce(importance,0.75-(rank_search::float/40)) as importance ";
- $sSQL .= "from placex where place_id in ($sPlaceIDs) ";
- $sSQL .= "and placex.rank_address between $iMinAddressRank and $iMaxAddressRank ";
- if ($sAllowedTypesSQLList) $sSQL .= "and placex.class in $sAllowedTypesSQLList ";
- $sSQL .= "and linked_place_id is null ";
- $sSQL .= "group by osm_type,osm_id,class,type,admin_level,rank_search,rank_address,country_code,importance";
- if (!$bDeDupe) $sSQL .= ",place_id";
- $sSQL .= ",get_address_by_language(place_id, $sLanguagePrefArraySQL) ";
- $sSQL .= ",get_name_by_language(name, $sLanguagePrefArraySQL) ";
- $sSQL .= ",get_name_by_language(name, ARRAY['ref']) ";
- $sSQL .= " union ";
- $sSQL .= "select 'T' as osm_type,place_id as osm_id,'place' as class,'house' as type,null as admin_level,30 as rank_search,30 as rank_address,min(place_id) as place_id,'us' as country_code,";
- $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
- $sSQL .= "null as placename,";
- $sSQL .= "null as ref,";
- $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, ";
-// $sSQL .= $sOrderSQL." as porder, ";
- $sSQL .= "-0.15 as importance ";
- $sSQL .= "from location_property_tiger where place_id in ($sPlaceIDs) ";
- $sSQL .= "and 30 between $iMinAddressRank and $iMaxAddressRank ";
- $sSQL .= "group by place_id";
- if (!$bDeDupe) $sSQL .= ",place_id";
- $sSQL .= " union ";
- $sSQL .= "select 'L' as osm_type,place_id as osm_id,'place' as class,'house' as type,null as admin_level,30 as rank_search,30 as rank_address,min(place_id) as place_id,'us' as country_code,";
- $sSQL .= "get_address_by_language(place_id, $sLanguagePrefArraySQL) as langaddress,";
- $sSQL .= "null as placename,";
- $sSQL .= "null as ref,";
- $sSQL .= "avg(ST_X(centroid)) as lon,avg(ST_Y(centroid)) as lat, ";
-// $sSQL .= $sOrderSQL." as porder, ";
- $sSQL .= "-0.10 as importance ";
- $sSQL .= "from location_property_aux where place_id in ($sPlaceIDs) ";
- $sSQL .= "and 30 between $iMinAddressRank and $iMaxAddressRank ";
- $sSQL .= "group by place_id";
- if (!$bDeDupe) $sSQL .= ",place_id";
- $sSQL .= ",get_address_by_language(place_id, $sLanguagePrefArraySQL) ";
- $sSQL .= "order by importance desc";
-// $sSQL .= "order by rank_search,rank_address,porder asc";
- if (CONST_Debug) var_dump('<hr>',$sSQL);
- $aSearchResults = $oDB->getAll($sSQL);
-//var_dump($sSQL,$aSearchResults);exit;
-
- if (PEAR::IsError($aSearchResults))
- {
- failInternalError("Could not get details for place.", $sSQL, $aSearchResults);
- }
- }
- } // end if ($sQuery)