]> git.openstreetmap.org Git - nominatim.git/blobdiff - website/search.php
Allow option of structured search queries (params amenity,street,city,county,state...
[nominatim.git] / website / search.php
index a1d0db72bd83d3b7682ff75df6559565e19b6c38..2b6a4c3471fd6b26aeada6c6c1cba58a4bb9789f 100755 (executable)
@@ -36,8 +36,8 @@
        // Show address breakdown
        $bShowAddressDetails = isset($_GET['addressdetails']) && $_GET['addressdetails'];
 
-       // Prefered language    
-       $aLangPrefOrder = getPrefferedLangauges();
+       // Preferred language   
+       $aLangPrefOrder = getPreferredLanguages();
        if (isset($aLangPrefOrder['name:de'])) $bReverseInPlan = true;
        if (isset($aLangPrefOrder['name:ru'])) $bReverseInPlan = true;
        if (isset($aLangPrefOrder['name:ja'])) $bReverseInPlan = true;
@@ -53,7 +53,7 @@
                }
        }
 
-  // Only certain ranks of feature
+       // Only certain ranks of feature
        if (isset($_GET['featureType']) && !isset($_GET['featuretype'])) $_GET['featuretype'] = $_GET['featureType'];
 
        if (isset($_GET['featuretype']))
                
        // Search query
        $sQuery = (isset($_GET['q'])?trim($_GET['q']):'');
-       if (!$sQuery && $_SERVER['PATH_INFO'] && $_SERVER['PATH_INFO'][0] == '/')
+       if (!$sQuery && isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'][0] == '/')
        {
                $sQuery = substr($_SERVER['PATH_INFO'], 1);
 
-               // reverse order of '/' seperated string
+               // reverse order of '/' separated string
                $aPhrases = explode('/', $sQuery);              
                $aPhrases = array_reverse($aPhrases); 
                $sQuery = join(', ',$aPhrases);
        }
+
+       function structuredAddressElement(&$aStructuredQuery, &$iMinAddressRank, &$iMaxAddressRank, $aParams, $sKey, $iNewMinAddressRank, $iNewMaxAddressRank)
+       {
+               if (!isset($_GET[$sKey])) return false;
+               $sValue = trim($_GET[$sKey]);
+               if (!$sValue) return false;
+               $aStructuredQuery[$sKey] = $sValue;
+               if ($iMinAddressRank == 0 && $iMaxAddressRank == 30) {
+                       $iMinAddressRank = $iNewMinAddressRank;
+                       $iMaxAddressRank = $iNewMaxAddressRank;
+               }
+               return true;
+       }
+
+       // Structured query?
+       $aStructuredOptions = array(
+                               array('amenity', 26, 30),
+                               array('street', 26, 30),
+                               array('city', 14, 24),
+                               array('county', 9, 13),
+                               array('state', 8, 8),
+                               array('country', 4, 4),
+                               array('postalcode', 16, 25),
+                               );
+       $aStructuredQuery = array();
+       foreach($aStructuredOptions as $aStructuredOption)
+       {
+               loadStructuredAddressElement($aStructuredQuery, $iMinAddressRank, $iMaxAddressRank, $_GET, $aStructuredOption[0], $aStructuredOption[1], $aStructuredOption[2]);
+       }
+       if (sizeof($aStructuredQuery) > 0) {
+               $sQuery = join(', ', $aStructuredQuery);
+       }
+
        if ($sQuery)
        {
                $hLog = logStart($oDB, 'search', $sQuery, $aLangPrefOrder);
                 $sQuery = str_ireplace(array('New York, ny','new york, new york', 'New York ny','new york new york'), 'new york city, ny', $sQuery);
                if (isset($aLangPrefOrder['name:en']))          
                {
-                       $sQuery = preg_replace('/\\bil\\b/','illinois', $sQuery);
-                       $sQuery = preg_replace('/\\bal\\b/','alabama', $sQuery);
-                       $sQuery = preg_replace('/\\bla\\b/','louisiana', $sQuery);
-                       $sQuery = preg_replace('/\\bde louisiana\\b/','de la', $sQuery);
+                       $sQuery = preg_replace('/,\s*il\s*(,|$)/',', illinois\1', $sQuery);
+                       $sQuery = preg_replace('/,\s*al\s*(,|$)/',', alabama\1', $sQuery);
+                       $sQuery = preg_replace('/,\s*la\s*(,|$)/',', louisiana\1', $sQuery);
                }
 
                // If we have a view box create the SQL
                        $aCoOrdinates[1] += $fWidth;
                        $aCoOrdinates[3] -= $fWidth;
                        $sViewboxLargeSQL = "ST_SetSRID(ST_MakeBox2D(ST_Point(".(float)$aCoOrdinates[0].",".(float)$aCoOrdinates[1]."),ST_Point(".(float)$aCoOrdinates[2].",".(float)$aCoOrdinates[3].")),4326)";
+               } else {
+                       $bBoundingBoxSearch = false;
                }
                if (isset($_GET['route']) && $_GET['route'] && isset($_GET['routewidth']) && $_GET['routewidth'])
                {
                        }
                }
 
-               if ($sQuery)
+               if ($sQuery || $aStructuredQuery)
                {
                        // Start with a blank search
                        $aSearches = array(
 
                        preg_match_all('/\\[([a-zA-Z]*)\\]/', $sQuery, $aSpecialTermsRaw, PREG_SET_ORDER);
                        $aSpecialTerms = array();
+                       if (isset($aStructuredQuery['amenity']) && $aStructuredQuery['amenity'])
+                       {
+                               $aSpecialTermsRaw[] = array('['.$aStructuredQuery['amenity'].']', $aStructuredQuery['amenity']);
+                               unset($aStructuredQuery['amenity']);
+                       }
                        foreach($aSpecialTermsRaw as $aSpecialTerm)
                        {
                                $sQuery = str_replace($aSpecialTerm[0], ' ', $sQuery);
                                $sToken = $oDB->getOne("select make_standard_name('".$aSpecialTerm[1]."') as string");
                                $sSQL = 'select * from (select word_id,word_token, word, class, type, location, country_code, operator';
-                               $sSQL .= ' from word where word_token in (\' '.$sToken.'\')) as x where (class is not null and class not in (\'place\',\'highway\')) or country_code is not null';
+                               $sSQL .= ' from word where word_token in (\' '.$sToken.'\')) as x where (class is not null and class not in (\'place\')) or country_code is not null';
                                if (CONST_Debug) var_Dump($sSQL);
                                $aSearchWords = $oDB->getAll($sSQL);
                                $aNewSearches = array();
 
                        // Split query into phrases
                        // Commas are used to reduce the search space by indicating where phrases split
-                       $aPhrases = explode(',',$sQuery);
+                       if (sizeof($aStructuredQuery) > 0)
+                       {
+                               $aPhrases = $aStructuredQuery;
+                               $bStructuredPhrases = true;
+                       }
+                       else
+                       {
+                               $aPhrases = explode(',',$sQuery);
+                               $bStructuredPhrases = false;
+                       }
+
 
                        // Convert each phrase to standard form
                        // Create a list of standard words
                        }
 
                        // reindex phrases - we make assumptions later on
+                       $aPhraseTypes = array_keys($aPhrases);
                        $aPhrases = array_values($aPhrases);
 
                        if (sizeof($aTokens))
                        // Check which tokens we have, get the ID numbers                       
                        $sSQL = 'select word_id,word_token, word, class, type, location, country_code, operator';
                        $sSQL .= ' from word where word_token in ('.join(',',array_map("getDBQuoted",$aTokens)).')';
-                       $sSQL .= ' and (class is null or class not in (\'highway\'))';
+                       $sSQL .= ' and search_name_count < '.CONST_Max_Word_Frequency;
 //                     $sSQL .= ' group by word_token, word, class, type, location, country_code';
 
                        if (CONST_Debug) var_Dump($sSQL);
                                Calculate all searches using aValidTokens i.e.
 
                                'Wodsworth Road, Sheffield' =>
-                                       
+
                                Phrase Wordset
                                0      0       (wodsworth road)
                                0      1       (wodsworth)(road)
                                1      0       (sheffield)
-                               
+
                                Score how good the search is so they can be ordered
                        */
                                foreach($aPhrases as $iPhrase => $sPhrase)
                                {
                                        $aNewPhraseSearches = array();
+                                       if ($bStructuredPhrases) $sPhraseType = $aPhraseTypes[$iPhrase];
+                                       else $sPhraseType = '';
 
-                                       foreach($aPhrases[$iPhrase]['wordsets'] as $iWordset => $aWordset)
+                                       foreach($aPhrases[$iPhrase]['wordsets'] as $aWordset)
                                        {
                                                $aWordsetSearches = $aSearches;
 
                                                // Add all words from this wordset
-                                               foreach($aWordset as $sToken)
+                                               foreach($aWordset as $iToken => $sToken)
                                                {
 //echo "<br><b>$sToken</b>";
                                                        $aNewWordsetSearches = array();
-                                                       
+
                                                        foreach($aWordsetSearches as $aCurrentSearch)
                                                        {
 //echo "<i>";
                                                                        {
                                                                                $aSearch = $aCurrentSearch;
                                                                                $aSearch['iSearchRank']++;
-                                                                               if ($aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0')
+                                                                               if (($sPhraseType == '' || $sPhraseType == 'country') && $aSearchTerm['country_code'] !== null && $aSearchTerm['country_code'] != '0')
                                                                                {
                                                                                        if ($aSearch['sCountryCode'] === false)
                                                                                        {
                                                                                                $aSearch['sCountryCode'] = strtolower($aSearchTerm['country_code']);
-                                                                                               // Country is almost always at the end of the string - increase score for finding it anywhere else (opimisation)
-                                                                                               if ($iWordset+1 != sizeof($aPhrases[$iPhrase]['wordsets']) || $iPhrase+1 != sizeof($aPhrases)) $aSearch['iSearchRank'] += 5;
+                                                                                               // Country is almost always at the end of the string - increase score for finding it anywhere else (optimisation)
+                                                                                               if ($iToken+1 != sizeof($aWordset) || $iPhrase+1 != sizeof($aPhrases)) $aSearch['iSearchRank'] += 5;
                                                                                                if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
                                                                                        }
                                                                                }
-                                                                               elseif ($aSearchTerm['lat'] !== '' && $aSearchTerm['lat'] !== null)
+                                                                               elseif (isset($aSearchTerm['lat']) && $aSearchTerm['lat'] !== '' && $aSearchTerm['lat'] !== null)
                                                                                {
                                                                                        if ($aSearch['fLat'] === '')
                                                                                        {
                                                                                                if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
                                                                                        }
                                                                                }
-                                                                               elseif ($aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house')
+                                                                               elseif (($sPhraseType == '' || $sPhraseType == 'street') && $aSearchTerm['class'] == 'place' && $aSearchTerm['type'] == 'house')
                                                                                {
                                                                                        if ($aSearch['sHouseNumber'] === '')
                                                                                        {
 */
                                                                                        }
                                                                                }
-                                                                               elseif ($aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null)
+                                                                               elseif ($sPhraseType == '' && $aSearchTerm['class'] !== '' && $aSearchTerm['class'] !== null)
                                                                                {
                                                                                        if ($aSearch['sClass'] === '')
                                                                                        {
                                                                                {
                                                                                        if (sizeof($aSearch['aName']))
                                                                                        {
-                                                                                               if (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4 || strpos($sToken, ' ') !== false)
+                                                                                               if (($sPhraseType != 'street' && $sPhraseType != 'country') && (!isset($aValidTokens[$sToken]) || strlen($sToken) < 4 || strpos($sToken, ' ') !== false))
                                                                                                {
                                                                                                        $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
                                                                                                }
                                                                        {
                                                                                if (isset($aSearchTerm['word_id']) && $aSearchTerm['word_id'])
                                                                                {
-//var_Dump('<hr>',$aSearch['aName']);
-
-                                                                               if (sizeof($aCurrentSearch['aName'])  && strlen($sToken) >= 4)
-                                                                               {
-                                                                               $aSearch = $aCurrentSearch;
-                                                                                       $aSearch['iSearchRank'] += 1;
-                                                                                       $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
-                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
-                                                                               }
+                                                                                       if (($sPhraseType != 'street') && sizeof($aCurrentSearch['aName']) && strlen($sToken) >= 4)
+                                                                                       {
+                                                                                               $aSearch = $aCurrentSearch;
+                                                                                               $aSearch['iSearchRank'] += 1;
+                                                                                               $aSearch['aAddress'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
 
-                                                                               if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase)
-                                                                               {
-                                                                               $aSearch = $aCurrentSearch;
-                                                                                       $aSearch['iSearchRank'] += 2;
-                                                                                       if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2;
-                                                                                       $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
-                                                                                       $aSearch['iNamePhrase'] = $iPhrase;
-                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
-                                                                               }
+                                                                                       if (!sizeof($aCurrentSearch['aName']) || $aCurrentSearch['iNamePhrase'] == $iPhrase)
+                                                                                       {
+                                                                                               $aSearch = $aCurrentSearch;
+                                                                                               $aSearch['iSearchRank'] += 2;
+                                                                                               if (preg_match('#^[0-9]+$#', $sToken)) $aSearch['iSearchRank'] += 2;
+                                                                                               $aSearch['aName'][$aSearchTerm['word_id']] = $aSearchTerm['word_id'];
+                                                                                               $aSearch['iNamePhrase'] = $iPhrase;
+                                                                                               if ($aSearch['iSearchRank'] < $iMaxRank) $aNewWordsetSearches[] = $aSearch;
+                                                                                       }
                                                                                }
                                                                        }
                                                                }
                                                                $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'];                                                               
+                                                               $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, centroid)";
+                                                               $sSQL .= " where st_contains($sViewboxSmallSQL, ct.centroid)";
                                                                if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";                                                             
-                                                               if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
+                                                               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'];                                                               
+                                                                       $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, centroid)";
+                                                                       $sSQL .= " where st_contains($sViewboxLargeSQL, ct.centroid)";
                                                                        if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";                                                             
-                                                                       if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
+                                                                       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, centroid)";
+                                                               $sSQL .= " and st_contains($sViewboxSmallSQL, geometry)";
                                                                if ($sCountryCodesSQL) $sSQL .= " and country_code in ($sCountryCodesSQL)";                                                             
                                                                if ($sViewboxCentreSQL) $sSQL .= " order by st_distance($sViewboxCentreSQL, centroid) asc";
                                                                $sSQL .= " limit $iLimit";
                {
                        if (stripos($sAddress, $sWord)!==false) $iCountWords++;
                }
-               $aResult['importance'] = $aResult['importance'] + $iCountWords;
+               $aResult['importance'] = $aResult['importance'] + ($iCountWords*0.1); // 0.1 is a completely arbitrary number but something in the range 0.1 to 0.5 would seem right
 
 //if (CONST_Debug) var_dump($aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']);
 /*
                        $bFirst = false;
                }
                if (!$bDeDupe || (!isset($aOSMIDDone[$aResult['osm_type'].$aResult['osm_id']])
-                       && !isset($aClassTypeNameDone[$aResult['osm_type'].$aResult['osm_class'].$aResult['name']])))
+                       && !isset($aClassTypeNameDone[$aResult['osm_type'].$aResult['class'].$aResult['name']])))
                {
                        $aOSMIDDone[$aResult['osm_type'].$aResult['osm_id']] = true;
-                       $aClassTypeNameDone[$aResult['osm_type'].$aResult['osm_class'].$aResult['name']] = true;
+                       $aClassTypeNameDone[$aResult['osm_type'].$aResult['class'].$aResult['name']] = true;
                        $aSearchResults[] = $aResult;
                }