]> git.openstreetmap.org Git - nominatim.git/blobdiff - lib/SearchDescription.php
Merge remote-tracking branch 'upstream/master'
[nominatim.git] / lib / SearchDescription.php
index a1910231ef8b53285ff561b52f1a00a1191c88ab..6345f50fc5aa570c06ccb1099e1544ad11bb88e2 100644 (file)
@@ -43,7 +43,6 @@ class SearchDescription
     /// Index of phrase currently processed.
     private $iNamePhrase = -1;
 
-
     /**
      * Create an empty search description.
      *
@@ -94,8 +93,8 @@ class SearchDescription
      */
     public function looksLikeFullAddress()
     {
-        return sizeof($this->aName)
-               && (sizeof($this->aAddress || $this->sCountryCode))
+        return (!empty($this->aName))
+               && (!empty($this->aAddress) || $this->sCountryCode)
                && preg_match('/[0-9]+/', $this->sHouseNumber);
     }
 
@@ -147,7 +146,7 @@ class SearchDescription
      */
     public function isValidSearch()
     {
-        if (!sizeof($this->aName)) {
+        if (empty($this->aName)) {
             if ($this->sHouseNumber) {
                 return false;
             }
@@ -223,7 +222,7 @@ class SearchDescription
                 // If we have a structured search or this is not the first term,
                 // add the postcode as an addendum.
                 if ($this->iOperator != Operator::POSTCODE
-                    && ($sPhraseType == 'postalcode' || sizeof($this->aName))
+                    && ($sPhraseType == 'postalcode' || !empty($this->aName))
                 ) {
                     $oSearch = clone $this;
                     $oSearch->iSearchRank++;
@@ -247,8 +246,8 @@ class SearchDescription
                     $oSearch->iSearchRank++;
                 }
                 // also must not appear in the middle of the address
-                if (sizeof($this->aAddress)
-                    || sizeof($this->aAddressNonSearch)
+                if (!empty($this->aAddress)
+                    || (!empty($this->aAddressNonSearch))
                     || $this->sPostcode
                 ) {
                     $oSearch->iSearchRank++;
@@ -262,7 +261,7 @@ class SearchDescription
 
                 $iOp = Operator::NEAR; // near == in for the moment
                 if ($aSearchTerm['operator'] == '') {
-                    if (sizeof($this->aName) || $this->oContext->isBoundedSearch()) {
+                    if (!empty($this->aName) || $this->oContext->isBoundedSearch()) {
                         $iOp = Operator::NAME;
                     }
                     $oSearch->iSearchRank += 2;
@@ -276,11 +275,12 @@ class SearchDescription
                   && $sPhraseType != 'country'
         ) {
             $iWordID = $aSearchTerm['word_id'];
-            if (sizeof($this->aName)) {
-                if (($sPhraseType == '' || !$bFirstPhrase)
-                    && $sPhraseType != 'country'
-                    && !$bHasPartial
-                ) {
+            // Full words can only be a name if they appear at the beginning
+            // of the phrase. In structured search the name must forcably in
+            // the first phrase. In unstructured search it may be in a later
+            // phrase when the first phrase is a house number.
+            if (!empty($this->aName) || !($bFirstPhrase || $sPhraseType == '')) {
+                if (($sPhraseType == '' || !$bFirstPhrase) && !$bHasPartial) {
                     $oSearch = clone $this;
                     $oSearch->iSearchRank++;
                     $oSearch->aAddress[$iWordID] = $iWordID;
@@ -289,14 +289,10 @@ class SearchDescription
                     $this->aFullNameAddress[$iWordID] = $iWordID;
                 }
             } else {
-                // in structured search only the first phrase can be the
-                // designated name
-                if ($sPhraseType == '' || $bFirstPhrase) {
-                    $oSearch = clone $this;
-                    $oSearch->iSearchRank++;
-                    $oSearch->aName = array($iWordID => $iWordID);
-                    $aNewSearches[] = $oSearch;
-                }
+                $oSearch = clone $this;
+                $oSearch->iSearchRank++;
+                $oSearch->aName = array($iWordID => $iWordID);
+                $aNewSearches[] = $oSearch;
             }
         }
 
@@ -325,7 +321,7 @@ class SearchDescription
         $iWordID = $aSearchTerm['word_id'];
 
         if ((!$bStructuredPhrases || $iPhrase > 0)
-            && sizeof($this->aName)
+            && (!empty($this->aName))
             && strpos($aSearchTerm['word_token'], ' ') === false
         ) {
             if ($aSearchTerm['search_name_count'] + 1 < CONST_Max_Word_Frequency) {
@@ -340,7 +336,7 @@ class SearchDescription
                 if (preg_match('#^[0-9]+$#', $aSearchTerm['word_token'])) {
                     $oSearch->iSearchRank += 2;
                 }
-                if (sizeof($aFullTokens)) {
+                if (!empty($aFullTokens)) {
                     $oSearch->iSearchRank++;
                 }
                 $aNewSearches[] = $oSearch;
@@ -361,11 +357,11 @@ class SearchDescription
         }
 
         if ((!$this->sPostcode && !$this->aAddress && !$this->aAddressNonSearch)
-            && (!sizeof($this->aName) || $this->iNamePhrase == $iPhrase)
+            && (empty($this->aName) || $this->iNamePhrase == $iPhrase)
         ) {
             $oSearch = clone $this;
             $oSearch->iSearchRank += 2;
-            if (!sizeof($this->aName)) {
+            if (empty($this->aName)) {
                 $oSearch->iSearchRank += 1;
             }
             if (preg_match('#^[0-9]+$#', $aSearchTerm['word_token'])) {
@@ -408,7 +404,7 @@ class SearchDescription
         $iHousenumber = -1;
 
         if ($this->sCountryCode
-            && !sizeof($this->aName)
+            && empty($this->aName)
             && !$this->iOperator
             && !$this->sClass
             && !$this->oContext->hasNearPoint()
@@ -417,7 +413,7 @@ class SearchDescription
             if (4 >= $iMinRank && 4 <= $iMaxRank) {
                 $aResults = $this->queryCountry($oDB);
             }
-        } elseif (!sizeof($this->aName) && !sizeof($this->aAddress)) {
+        } elseif (empty($this->aName) && empty($this->aAddress)) {
             // Neither name nor address? Then we must be
             // looking for a POI in a geographic area.
             if ($this->oContext->isBoundedSearch()) {
@@ -438,33 +434,30 @@ class SearchDescription
             );
 
             //now search for housenumber, if housenumber provided
-            if ($this->sHouseNumber && sizeof($aResults)) {
+            if ($this->sHouseNumber && !empty($aResults)) {
                 $aNamedPlaceIDs = $aResults;
-                $aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs, $iLimit);
+                $aResults = $this->queryHouseNumber($oDB, $aNamedPlaceIDs);
 
-                if (!sizeof($aResults) && $this->looksLikeFullAddress()) {
+                if (empty($aResults) && $this->looksLikeFullAddress()) {
                     $aResults = $aNamedPlaceIDs;
                 }
             }
 
             // finally get POIs if requested
-            if ($this->sClass && sizeof($aResults)) {
+            if ($this->sClass && !empty($aResults)) {
                 $aResults = $this->queryPoiByOperator($oDB, $aResults, $iLimit);
             }
         }
 
-        if (CONST_Debug) {
-            echo '<br><b>Place IDs:</b> ';
-            var_dump(array_keys($aResults));
-        }
+        Debug::printDebugTable('Place IDs', $aResults);
 
-        if (sizeof($aResults) && $this->sPostcode) {
+        if (!empty($aResults) && $this->sPostcode) {
             $sPlaceIds = Result::joinIdsByTable($aResults, Result::TABLE_PLACEX);
             if ($sPlaceIds) {
                 $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();
@@ -472,10 +465,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);
                 }
             }
         }
@@ -494,7 +484,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) {
@@ -535,7 +525,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));
         }
 
@@ -549,7 +539,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));
         }
 
@@ -565,7 +555,7 @@ class SearchDescription
     {
         $sSQL = 'SELECT p.place_id FROM location_postcode p ';
 
-        if (sizeof($this->aAddress)) {
+        if (!empty($this->aAddress)) {
             $sSQL .= ', search_name s ';
             $sSQL .= 'WHERE s.place_id = p.parent_place_id ';
             $sSQL .= 'AND array_cat(s.nameaddress_vector, s.name_vector)';
@@ -579,7 +569,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) {
@@ -594,7 +584,7 @@ class SearchDescription
         $aTerms = array();
         $aOrder = array();
 
-        if ($this->sHouseNumber && sizeof($this->aAddress)) {
+        if ($this->sHouseNumber && !empty($this->aAddress)) {
             $sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
             $aOrder[] = ' (';
             $aOrder[0] .= 'EXISTS(';
@@ -620,13 +610,13 @@ class SearchDescription
             $aOrder[0] .= ') DESC';
         }
 
-        if (sizeof($this->aName)) {
+        if (!empty($this->aName)) {
             $aTerms[] = 'name_vector @> '.getArraySQL($this->aName);
         }
-        if (sizeof($this->aAddress)) {
+        if (!empty($this->aAddress)) {
             // For infrequent name terms disable index usage for address
             if (CONST_Search_NameOnlySearchFrequencyThreshold
-                && sizeof($this->aName) == 1
+                && count($this->aName) == 1
                 && $aWordFrequencyScores[$this->aName[reset($this->aName)]]
                      < CONST_Search_NameOnlySearchFrequencyThreshold
             ) {
@@ -656,7 +646,7 @@ class SearchDescription
             $aTerms[] = $this->oContext->withinSQL('centroid');
             $aOrder[] = $this->oContext->distanceSQL('centroid');
         } elseif ($this->sPostcode) {
-            if (!sizeof($this->aAddress)) {
+            if (empty($this->aAddress)) {
                 $aTerms[] = "EXISTS(SELECT place_id FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."' AND ST_DWithin(search_name.centroid, p.geometry, 0.1))";
             } else {
                 $aOrder[] = "(SELECT min(ST_Distance(search_name.centroid, p.geometry)) FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."')";
@@ -684,7 +674,7 @@ class SearchDescription
         $sImportanceSQL .= $this->oContext->viewboxImportanceSQL('centroid');
         $aOrder[] = "$sImportanceSQL DESC";
 
-        if (sizeof($this->aFullNameAddress)) {
+        if (!empty($this->aFullNameAddress)) {
             $sExactMatchSQL = ' ( ';
             $sExactMatchSQL .= ' SELECT count(*) FROM ( ';
             $sExactMatchSQL .= '  SELECT unnest('.getArraySQL($this->aFullNameAddress).')';
@@ -703,14 +693,14 @@ class SearchDescription
 
         $aResults = array();
 
-        if (sizeof($aTerms)) {
+        if (!empty($aTerms)) {
             $sSQL = 'SELECT place_id,'.$sExactMatchSQL;
             $sSQL .= ' FROM search_name';
             $sSQL .= ' WHERE '.join(' and ', $aTerms);
             $sSQL .= ' ORDER BY '.join(', ', $aOrder);
             $sSQL .= ' LIMIT '.$iLimit;
 
-            if (CONST_Debug) var_dump($sSQL);
+            Debug::printSQL($sSQL);
 
             $aDBResults = chksql(
                 $oDB->getAll($sSQL),
@@ -727,7 +717,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);
@@ -741,9 +731,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) {
@@ -752,7 +741,7 @@ class SearchDescription
 
         $bIsIntHouseNumber= (bool) preg_match('/[0-9]+/', $this->sHouseNumber);
         $iHousenumber = intval($this->sHouseNumber);
-        if ($bIsIntHouseNumber && !sizeof($aResults)) {
+        if ($bIsIntHouseNumber && empty($aResults)) {
             // if nothing found, search in the interpolation line table
             $sSQL = 'SELECT distinct place_id FROM location_property_osmline';
             $sSQL .= ' WHERE startnumber is not NULL';
@@ -769,9 +758,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);
@@ -781,14 +769,13 @@ class SearchDescription
         }
 
         // If nothing found try the aux fallback table
-        if (CONST_Use_Aux_Location_data && !sizeof($aResults)) {
+        if (CONST_Use_Aux_Location_data && empty($aResults)) {
             $sSQL = 'SELECT place_id FROM location_property_aux';
             $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);
@@ -796,7 +783,7 @@ class SearchDescription
         }
 
         // If nothing found then search in Tiger data (location_property_tiger)
-        if (CONST_Use_US_Tiger_Data && $bIsIntHouseNumber && !sizeof($aResults)) {
+        if (CONST_Use_US_Tiger_Data && $bIsIntHouseNumber && empty($aResults)) {
             $sSQL = 'SELECT place_id FROM location_property_tiger';
             $sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.') and (';
             if ($iHousenumber % 2 == 0) {
@@ -808,9 +795,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);
@@ -844,7 +830,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);
@@ -858,7 +844,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
@@ -871,7 +857,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));
             }
 
@@ -881,7 +867,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);
             }
@@ -926,7 +912,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);
@@ -958,7 +944,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);
@@ -1003,6 +989,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) {