X-Git-Url: https://git.openstreetmap.org./nominatim.git/blobdiff_plain/00b05e239495fe147f9a74f0e36eb243c8a32227..b4fec57b6d53f8e8a45c46ff11f13cbcbea1006a:/lib-php/SearchDescription.php diff --git a/lib-php/SearchDescription.php b/lib-php/SearchDescription.php index 94cadda3..6091fd61 100644 --- a/lib-php/SearchDescription.php +++ b/lib-php/SearchDescription.php @@ -153,8 +153,6 @@ class SearchDescription * Derive new searches by adding a full term to the existing search. * * @param object $oSearchTerm Description of the token. - * @param bool $bHasPartial True if there are also tokens of partial terms - * with the same name. * @param string $sPhraseType Type of phrase the token is contained in. * @param bool $bFirstToken True if the token is at the beginning of the * query. @@ -164,7 +162,7 @@ class SearchDescription * * @return SearchDescription[] List of derived search descriptions. */ - public function extendWithFullTerm($oSearchTerm, $bHasPartial, $sPhraseType, $bFirstToken, $bFirstPhrase, $bLastToken) + public function extendWithFullTerm($oSearchTerm, $sPhraseType, $bFirstToken, $bFirstPhrase, $bLastToken) { $aNewSearches = array(); @@ -179,6 +177,7 @@ class SearchDescription // - increase score for finding it anywhere else (optimisation) if (!$bLastToken) { $oSearch->iSearchRank += 5; + $oSearch->iNamePhrase = -1; } $aNewSearches[] = $oSearch; } @@ -205,6 +204,7 @@ class SearchDescription ) { $oSearch = clone $this; $oSearch->iSearchRank++; + $oSearch->iNamePhrase = -1; if (strlen($oSearchTerm->sPostcode) < 4) { $oSearch->iSearchRank += 4 - strlen($oSearchTerm->sPostcode); } @@ -216,29 +216,33 @@ class SearchDescription && is_a($oSearchTerm, '\Nominatim\Token\HouseNumber') ) { if (!$this->sHouseNumber && $this->iOperator != Operator::POSTCODE) { - $oSearch = clone $this; - $oSearch->iSearchRank++; - $oSearch->sHouseNumber = $oSearchTerm->sToken; - if ($this->iOperator != Operator::NONE) { - $oSearch->iSearchRank++; - } // sanity check: if the housenumber is not mainly made // up of numbers, add a penalty - if (preg_match('/\\d/', $oSearch->sHouseNumber) === 0 - || preg_match_all('/[^0-9]/', $oSearch->sHouseNumber, $aMatches) > 2) { - $oSearch->iSearchRank++; + $iSearchCost = 1; + if (preg_match('/\\d/', $oSearchTerm->sToken) === 0 + || preg_match_all('/[^0-9]/', $oSearchTerm->sToken, $aMatches) > 2) { + $iSearchCost++; + } + if ($this->iOperator != Operator::NONE) { + $iSearchCost++; } if (empty($oSearchTerm->iId)) { - $oSearch->iSearchRank++; + $iSearchCost++; } // also must not appear in the middle of the address if (!empty($this->aAddress) || (!empty($this->aAddressNonSearch)) || $this->sPostcode ) { - $oSearch->iSearchRank++; + $iSearchCost++; } + + $oSearch = clone $this; + $oSearch->iSearchRank += $iSearchCost; + $oSearch->iNamePhrase = -1; + $oSearch->sHouseNumber = $oSearchTerm->sToken; $aNewSearches[] = $oSearch; + // Housenumbers may appear in the name when the place has its own // address terms. if ($oSearchTerm->iId !== null @@ -246,7 +250,7 @@ class SearchDescription && empty($this->aAddress) ) { $oSearch = clone $this; - $oSearch->iSearchRank++; + $oSearch->iSearchRank += $iSearchCost; $oSearch->aAddress = $this->aName; $oSearch->bRareName = false; $oSearch->aName = array($oSearchTerm->iId => $oSearchTerm->iId); @@ -259,6 +263,7 @@ class SearchDescription if ($this->iOperator == Operator::NONE) { $oSearch = clone $this; $oSearch->iSearchRank += 2; + $oSearch->iNamePhrase = -1; $iOp = $oSearchTerm->iOperator; if ($iOp == Operator::NONE) { @@ -268,7 +273,7 @@ class SearchDescription $iOp = Operator::NEAR; } $oSearch->iSearchRank += 2; - } else if (!$bFirstToken && !$bLastToken) { + } elseif (!$bFirstToken && !$bLastToken) { $oSearch->iSearchRank += 2; } if ($this->sHouseNumber) { @@ -291,13 +296,14 @@ class SearchDescription // 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) { + if (($sPhraseType == '' || !$bFirstPhrase) && $oSearchTerm->iTermCount > 1) { $oSearch = clone $this; - $oSearch->iSearchRank += 3 * $oSearchTerm->iTermCount; + $oSearch->iNamePhrase = -1; + $oSearch->iSearchRank += 1; $oSearch->aAddress[$iWordID] = $iWordID; $aNewSearches[] = $oSearch; } - } else if (empty($this->aNameNonSearch)) { + } elseif (empty($this->aNameNonSearch)) { $oSearch = clone $this; $oSearch->iSearchRank++; $oSearch->aName = array($iWordID => $iWordID); @@ -328,7 +334,9 @@ class SearchDescription public function extendWithPartialTerm($sToken, $oSearchTerm, $bStructuredPhrases, $iPhrase, $aFullTokens) { // Only allow name terms. - if (!(is_a($oSearchTerm, '\Nominatim\Token\Word'))) { + if (!(is_a($oSearchTerm, '\Nominatim\Token\Word')) + || strpos($sToken, ' ') !== false + ) { return array(); } @@ -407,7 +415,6 @@ class SearchDescription public function query(&$oDB, $iMinRank, $iMaxRank, $iLimit) { $aResults = array(); - $iHousenumber = -1; if ($this->sCountryCode && empty($this->aName) @@ -440,23 +447,24 @@ class SearchDescription // Now search for housenumber, if housenumber provided. Can be zero. if (($this->sHouseNumber || $this->sHouseNumber === '0') && !empty($aResults)) { + $aHnResults = $this->queryHouseNumber($oDB, $aResults); + // Downgrade the rank of the street results, they are missing - // the housenumber. + // the housenumber. Also drop POI places (rank 30) here, they + // cannot be a parent place and therefore must not be shown + // as a result for a search with a missing housenumber. foreach ($aResults as $oRes) { - if ($oRes->iAddressRank >= 26) { - $oRes->iResultRank++; - } else { - $oRes->iResultRank += 2; + if ($oRes->iAddressRank < 28) { + if ($oRes->iAddressRank >= 26) { + $oRes->iResultRank++; + } else { + $oRes->iResultRank += 2; + } + $aHnResults[$oRes->iId] = $oRes; } } - $aHnResults = $this->queryHouseNumber($oDB, $aResults); - - if (!empty($aHnResults)) { - foreach ($aHnResults as $oRes) { - $aResults[$oRes->iId] = $oRes; - } - } + $aResults = $aHnResults; } // finally get POIs if requested @@ -608,14 +616,14 @@ class SearchDescription // 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)) { + if ($this->sHouseNumber && ($this->bRareName || !empty($this->aAddress) || $this->sPostcode)) { $sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M'; $aOrder[] = ' ('; $aOrder[0] .= 'EXISTS('; $aOrder[0] .= ' SELECT place_id'; $aOrder[0] .= ' FROM placex'; $aOrder[0] .= ' WHERE parent_place_id = search_name.place_id'; - $aOrder[0] .= " AND transliteration(housenumber) ~* E'".$sHouseNumberRegex."'"; + $aOrder[0] .= " AND housenumber ~* E'".$sHouseNumberRegex."'"; $aOrder[0] .= ' LIMIT 1'; $aOrder[0] .= ') '; // also housenumbers from interpolation lines table are needed @@ -736,16 +744,33 @@ class SearchDescription private function queryHouseNumber(&$oDB, $aRoadPlaceIDs) { $aResults = array(); - $sPlaceIDs = Result::joinIdsByTable($aRoadPlaceIDs, Result::TABLE_PLACEX); + $sRoadPlaceIDs = Result::joinIdsByTableMaxRank( + $aRoadPlaceIDs, + Result::TABLE_PLACEX, + 27 + ); + $sPOIPlaceIDs = Result::joinIdsByTableMinRank( + $aRoadPlaceIDs, + Result::TABLE_PLACEX, + 30 + ); - if (!$sPlaceIDs) { + $aIDCondition = array(); + if ($sRoadPlaceIDs) { + $aIDCondition[] = 'parent_place_id in ('.$sRoadPlaceIDs.')'; + } + if ($sPOIPlaceIDs) { + $aIDCondition[] = 'place_id in ('.$sPOIPlaceIDs.')'; + } + + if (empty($aIDCondition)) { return $aResults; } $sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M'; - $sSQL = 'SELECT place_id FROM placex '; - $sSQL .= 'WHERE parent_place_id in ('.$sPlaceIDs.')'; - $sSQL .= " AND transliteration(housenumber) ~* E'".$sHouseNumberRegex."'"; + $sSQL = 'SELECT place_id FROM placex WHERE'; + $sSQL .= " housenumber ~* E'".$sHouseNumberRegex."'"; + $sSQL .= ' AND ('.join(' OR ', $aIDCondition).')'; $sSQL .= $this->oContext->excludeSQL(' AND place_id'); Debug::printSQL($sSQL); @@ -757,11 +782,11 @@ class SearchDescription $bIsIntHouseNumber= (bool) preg_match('/[0-9]+/', $this->sHouseNumber); $iHousenumber = intval($this->sHouseNumber); - if ($bIsIntHouseNumber && empty($aResults)) { + if ($bIsIntHouseNumber && $sRoadPlaceIDs && 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'; - $sSQL .= ' AND parent_place_id in ('.$sPlaceIDs.') AND ('; + $sSQL .= ' AND parent_place_id in ('.$sRoadPlaceIDs.') AND ('; if ($iHousenumber % 2 == 0) { // If housenumber is even, look for housenumber in streets // with interpolationtype even or all. @@ -784,24 +809,10 @@ class SearchDescription } } - // If nothing found try the aux fallback table - 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'); - - Debug::printSQL($sSQL); - - foreach ($oDB->getCol($sSQL) as $iPlaceId) { - $aResults[$iPlaceId] = new Result($iPlaceId, Result::TABLE_AUX); - } - } - // If nothing found then search in Tiger data (location_property_tiger) - if (CONST_Use_US_Tiger_Data && $bIsIntHouseNumber && empty($aResults)) { + if (CONST_Use_US_Tiger_Data && $sRoadPlaceIDs && $bIsIntHouseNumber && empty($aResults)) { $sSQL = 'SELECT place_id FROM location_property_tiger'; - $sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.') and ('; + $sSQL .= ' WHERE parent_place_id in ('.$sRoadPlaceIDs.') and ('; if ($iHousenumber % 2 == 0) { $sSQL .= "interpolationtype='even'"; } else {