]> git.openstreetmap.org Git - nominatim.git/blobdiff - lib/SearchDescription.php
take redirects into account for request limits
[nominatim.git] / lib / SearchDescription.php
index c4f05a0eb6840e510e8dac88f9100676cc053a94..5f01e01bcf9bb78f3a2dee104aab8d8158a345d3 100644 (file)
@@ -17,6 +17,8 @@ class SearchDescription
     private $sCountryCode = '';
     /// List of word ids making up the name of the object.
     private $aName = array();
+    /// True if the name is rare enough to force index use on name.
+    private $bRareName = false;
     /// List of word ids making up the address of the object.
     private $aAddress = array();
     /// Subset of word ids of full words making up the address.
@@ -43,7 +45,6 @@ class SearchDescription
     /// Index of phrase currently processed.
     private $iNamePhrase = -1;
 
-
     /**
      * Create an empty search description.
      *
@@ -293,6 +294,11 @@ class SearchDescription
                 $oSearch = clone $this;
                 $oSearch->iSearchRank++;
                 $oSearch->aName = array($iWordID => $iWordID);
+                if (CONST_Search_NameOnlySearchFrequencyThreshold) {
+                    $oSearch->bRareName =
+                        $aSearchTerm['search_name_count'] + 1
+                          < CONST_Search_NameOnlySearchFrequencyThreshold;
+                }
                 $aNewSearches[] = $oSearch;
             }
         }
@@ -369,6 +375,13 @@ class SearchDescription
                 $oSearch->iSearchRank += 2;
             }
             if ($aSearchTerm['search_name_count'] + 1 < CONST_Max_Word_Frequency) {
+                if (empty($this->aName) && CONST_Search_NameOnlySearchFrequencyThreshold) {
+                    $oSearch->bRareName =
+                        $aSearchTerm['search_name_count'] + 1
+                          < CONST_Search_NameOnlySearchFrequencyThreshold;
+                } else {
+                    $oSearch->bRareName = false;
+                }
                 $oSearch->aName[$iWordID] = $iWordID;
             } else {
                 $oSearch->aNameNonSearch[$iWordID] = $iWordID;
@@ -386,20 +399,16 @@ class SearchDescription
     /**
      * Query database for places that match this search.
      *
-     * @param object  $oDB                  Database connection to use.
-     * @param mixed[] $aWordFrequencyScores Number of times tokens appears
-     *                                      overall in a planet database.
-     * @param integer $iMinRank             Minimum address rank to restrict
-     *                                      search to.
-     * @param integer $iMaxRank             Maximum address rank to restrict
-     *                                      search to.
-     * @param integer $iLimit               Maximum number of results.
+     * @param object  $oDB      Database connection to use.
+     * @param integer $iMinRank Minimum address rank to restrict search to.
+     * @param integer $iMaxRank Maximum address rank to restrict search to.
+     * @param integer $iLimit   Maximum number of results.
      *
      * @return mixed[] An array with two fields: IDs contains the list of
      *                 matching place IDs and houseNumber the houseNumber
      *                 if appicable or -1 if not.
      */
-    public function query(&$oDB, &$aWordFrequencyScores, $iMinRank, $iMaxRank, $iLimit)
+    public function query(&$oDB, $iMinRank, $iMaxRank, $iLimit)
     {
         $aResults = array();
         $iHousenumber = -1;
@@ -428,7 +437,6 @@ class SearchDescription
             // First search for places according to name and address.
             $aResults = $this->queryNamedPlace(
                 $oDB,
-                $aWordFrequencyScores,
                 $iMinRank,
                 $iMaxRank,
                 $iLimit
@@ -437,7 +445,7 @@ class SearchDescription
             //now search for housenumber, if housenumber provided
             if ($this->sHouseNumber && !empty($aResults)) {
                 $aNamedPlaceIDs = $aResults;
-                $aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs, $iLimit);
+                $aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs);
 
                 if (empty($aResults) && $this->looksLikeFullAddress()) {
                     $aResults = $aNamedPlaceIDs;
@@ -450,10 +458,7 @@ class SearchDescription
             }
         }
 
-        if (CONST_Debug) {
-            echo '<br><b>Place IDs:</b> ';
-            var_dump(array_keys($aResults));
-        }
+        Debug::printDebugTable('Place IDs', $aResults);
 
         if (!empty($aResults) && $this->sPostcode) {
             $sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
@@ -461,7 +466,7 @@ class SearchDescription
                 $sSQL = 'SELECT place_id FROM placex';
                 $sSQL .= ' WHERE place_id in ('.$sPlaceIds.')';
                 $sSQL .= " AND postcode = '".$this->sPostcode."'";
-                if (CONST_Debug) var_dump($sSQL);
+                Debug::printSQL($sSQL);
                 $aFilteredPlaceIDs = chksql($oDB->getCol($sSQL));
                 if ($aFilteredPlaceIDs) {
                     $aNewResults = array();
@@ -469,10 +474,7 @@ class SearchDescription
                         $aNewResults[$iPlaceId] = $aResults[$iPlaceId];
                     }
                     $aResults = $aNewResults;
-                    if (CONST_Debug) {
-                        echo '<br><b>Place IDs after postcode filtering:</b> ';
-                        var_dump(array_keys($aResults));
-                    }
+                    Debug::printVar('Place IDs after postcode filtering', $aResults);
                 }
             }
         }
@@ -491,7 +493,7 @@ class SearchDescription
         }
         $sSQL .= ' ORDER BY st_area(geometry) DESC LIMIT 1';
 
-        if (CONST_Debug) var_dump($sSQL);
+        Debug::printSQL($sSQL);
 
         $aResults = array();
         foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
@@ -532,7 +534,7 @@ class SearchDescription
                 $sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC';
             }
             $sSQL .= " limit $iLimit";
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
             $aDBResults = chksql($oDB->getCol($sSQL));
         }
 
@@ -546,7 +548,7 @@ class SearchDescription
             }
             $sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('centroid').' ASC';
             $sSQL .= " LIMIT $iLimit";
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
             $aDBResults = chksql($oDB->getCol($sSQL));
         }
 
@@ -576,7 +578,7 @@ class SearchDescription
         $sSQL .= $this->oContext->excludeSQL(' AND p.place_id');
         $sSQL .= " LIMIT $iLimit";
 
-        if (CONST_Debug) var_dump($sSQL);
+        Debug::printSQL($sSQL);
 
         $aResults = array();
         foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
@@ -586,12 +588,16 @@ class SearchDescription
         return $aResults;
     }
 
-    private function queryNamedPlace(&$oDB, $aWordFrequencyScores, $iMinAddressRank, $iMaxAddressRank, $iLimit)
+    private function queryNamedPlace(&$oDB, $iMinAddressRank, $iMaxAddressRank, $iLimit)
     {
         $aTerms = array();
         $aOrder = array();
 
-        if ($this->sHouseNumber && !empty($this->aAddress)) {
+        // Sort by existence of the requested house number but only if not
+        // too many results are expected for the street, i.e. if the result
+        // will be narrowed down by an address. Remeber that with ordering
+        // every single result has to be checked.
+        if ($this->sHouseNumber && (!empty($this->aAddress) || $this->sPostcode)) {
             $sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
             $aOrder[] = ' (';
             $aOrder[0] .= 'EXISTS(';
@@ -622,11 +628,7 @@ class SearchDescription
         }
         if (!empty($this->aAddress)) {
             // For infrequent name terms disable index usage for address
-            if (CONST_Search_NameOnlySearchFrequencyThreshold
-                && count($this->aName) == 1
-                && $aWordFrequencyScores[$this->aName[reset($this->aName)]]
-                     < CONST_Search_NameOnlySearchFrequencyThreshold
-            ) {
+            if ($this->bRareName) {
                 $aTerms[] = 'array_cat(nameaddress_vector,ARRAY[]::integer[]) @> '.getArraySQL($this->aAddress);
             } else {
                 $aTerms[] = 'nameaddress_vector @> '.getArraySQL($this->aAddress);
@@ -707,7 +709,7 @@ class SearchDescription
             $sSQL .= ' ORDER BY '.join(', ', $aOrder);
             $sSQL .= ' LIMIT '.$iLimit;
 
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
 
             $aDBResults = chksql(
                 $oDB->getAll($sSQL),
@@ -724,7 +726,7 @@ class SearchDescription
         return $aResults;
     }
 
-    private function queryHouseNumber(&$oDB, $aRoadPlaceIDs, $iLimit)
+    private function queryHouseNumber(&$oDB, $aRoadPlaceIDs)
     {
         $aResults = array();
         $sPlaceIDs = Result::joinIdsByTable($aRoadPlaceIDs, Result::TABLE_PLACEX);
@@ -738,9 +740,8 @@ class SearchDescription
         $sSQL .= 'WHERE parent_place_id in ('.$sPlaceIDs.')';
         $sSQL .= "  AND transliteration(housenumber) ~* E'".$sHouseNumberRegex."'";
         $sSQL .= $this->oContext->excludeSQL(' AND place_id');
-        $sSQL .= " LIMIT $iLimit";
 
-        if (CONST_Debug) var_dump($sSQL);
+        Debug::printSQL($sSQL);
 
         // XXX should inherit the exactMatches from its parent
         foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
@@ -766,9 +767,8 @@ class SearchDescription
             $sSQL .= $iHousenumber.'>=startnumber and ';
             $sSQL .= $iHousenumber.'<=endnumber';
             $sSQL .= $this->oContext->excludeSQL(' AND place_id');
-            $sSQL .= " limit $iLimit";
 
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
 
             foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
                 $oResult = new Result($iPlaceId, Result::TABLE_OSMLINE);
@@ -783,9 +783,8 @@ class SearchDescription
             $sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.')';
             $sSQL .= " AND housenumber = '".$this->sHouseNumber."'";
             $sSQL .= $this->oContext->excludeSQL(' AND place_id');
-            $sSQL .= " limit $iLimit";
 
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
 
             foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
                 $aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_AUX);
@@ -805,9 +804,8 @@ class SearchDescription
             $sSQL .= $iHousenumber.'>=startnumber and ';
             $sSQL .= $iHousenumber.'<=endnumber';
             $sSQL .= $this->oContext->excludeSQL(' AND place_id');
-            $sSQL .= " limit $iLimit";
 
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
 
             foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
                 $oResult = new Result($iPlaceId, Result::TABLE_TIGER);
@@ -841,7 +839,7 @@ class SearchDescription
             $sSQL .= ' ORDER BY rank_search ASC ';
             $sSQL .= " LIMIT $iLimit";
 
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
 
             foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
                 $aResults[$iPlaceId] = new Result($iPlaceId);
@@ -855,7 +853,7 @@ class SearchDescription
             $bCacheTable = (bool) chksql($oDB->getOne($sSQL));
 
             $sSQL = "SELECT min(rank_search) FROM placex WHERE place_id in ($sPlaceIDs)";
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
             $iMaxRank = (int)chksql($oDB->getOne($sSQL));
 
             // For state / country level searches the normal radius search doesn't work very well
@@ -868,7 +866,7 @@ class SearchDescription
                 $sSQL .= "   AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')";
                 $sSQL .= ' ORDER BY rank_search ASC ';
                 $sSQL .= ' LIMIT 1';
-                if (CONST_Debug) var_dump($sSQL);
+                Debug::printSQL($sSQL);
                 $sPlaceGeom = chksql($oDB->getOne($sSQL));
             }
 
@@ -878,7 +876,7 @@ class SearchDescription
                 $iMaxRank += 5;
                 $sSQL = 'SELECT place_id FROM placex';
                 $sSQL .= " WHERE place_id in ($sPlaceIDs) and rank_search < $iMaxRank";
-                if (CONST_Debug) var_dump($sSQL);
+                Debug::printSQL($sSQL);
                 $aPlaceIDs = chksql($oDB->getCol($sSQL));
                 $sPlaceIDs = join(',', $aPlaceIDs);
             }
@@ -923,7 +921,7 @@ class SearchDescription
                     }
                     $sSQL .= " limit $iLimit";
 
-                    if (CONST_Debug) var_dump($sSQL);
+                    Debug::printSQL($sSQL);
 
                     foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
                         $aResults[$iPlaceId] = new Result($iPlaceId);
@@ -955,7 +953,7 @@ class SearchDescription
                     }
                     $sSQL .= " limit $iLimit";
 
-                    if (CONST_Debug) var_dump($sSQL);
+                    Debug::printSQL($sSQL);
 
                     foreach (chksql($oDB->getCol($sSQL)) as $iPlaceId) {
                         $aResults[$iPlaceId] = new Result($iPlaceId);
@@ -1000,6 +998,24 @@ class SearchDescription
     //////////// Debugging functions
 
 
+    public function debugInfo()
+    {
+        return array(
+                'Search rank' => $this->iSearchRank,
+                'Country code' => $this->sCountryCode,
+                'Name terms' => $this->aName,
+                'Name terms (stop words)' => $this->aNameNonSearch,
+                'Address terms' => $this->aAddress,
+                'Address terms (stop words)' => $this->aAddressNonSearch,
+                'Address terms (full words)' => $this->aFullNameAddress,
+                'Special search' => $this->iOperator,
+                'Class' => $this->sClass,
+                'Type' => $this->sType,
+                'House number' => $this->sHouseNumber,
+                'Postcode' => $this->sPostcode
+               );
+    }
+
     public function dumpAsHtmlTableRow(&$aWordIDs)
     {
         $kf = function ($k) use (&$aWordIDs) {