namespace Nominatim;
-require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
-require_once(CONST_BasePath.'/lib/SearchContext.php');
-require_once(CONST_BasePath.'/lib/Result.php');
+require_once(CONST_LibDir.'/SpecialSearchOperator.php');
+require_once(CONST_LibDir.'/SearchContext.php');
+require_once(CONST_LibDir.'/Result.php');
/**
* Description of a single interpretation of a search query.
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.
- private $aFullNameAddress = array();
/// List of word ids that appear in the name but should be ignored.
private $aNameNonSearch = array();
/// List of word ids that appear in the address but should be ignored.
$this->sType = $sType;
}
- /**
- * Check if this might be a full address search.
- *
- * @return bool True if the search contains name, address and housenumber.
- */
- public function looksLikeFullAddress()
- {
- return (!empty($this->aName))
- && (!empty($this->aAddress) || $this->sCountryCode)
- && preg_match('/[0-9]+/', $this->sHouseNumber);
- }
-
/**
* Check if any operator is set.
*
} elseif (($sPhraseType == '' || $sPhraseType == 'postalcode')
&& is_a($oSearchTerm, '\Nominatim\Token\Postcode')
) {
- // We need to try the case where the postal code is the primary element
- // (i.e. no way to tell if it is (postalcode, city) OR (city, postalcode)
- // so try both.
if (!$this->sPostcode) {
// If we have structured search or this is the first term,
// make the postcode the primary search element.
- if ($this->iOperator == Operator::NONE
- && ($sPhraseType == 'postalcode' || $bFirstToken)
- ) {
+ if ($this->iOperator == Operator::NONE && $bFirstToken) {
$oSearch = clone $this;
$oSearch->iSearchRank++;
$oSearch->iOperator = Operator::POSTCODE;
) {
$oSearch = clone $this;
$oSearch->iSearchRank++;
+ if (strlen($oSearchTerm->sPostcode) < 4) {
+ $oSearch->iSearchRank += 4 - strlen($oSearchTerm->sPostcode);
+ }
$oSearch->sPostcode = $oSearchTerm->sPostcode;
$aNewSearches[] = $oSearch;
}
$oSearch->iSearchRank++;
}
$aNewSearches[] = $oSearch;
+ // Housenumbers may appear in the name when the place has its own
+ // address terms.
+ if ($oSearchTerm->iId !== null
+ && ($this->iNamePhrase >= 0 || empty($this->aName))
+ && empty($this->aAddress)
+ ) {
+ $oSearch = clone $this;
+ $oSearch->iSearchRank++;
+ $oSearch->aAddress = $this->aName;
+ $oSearch->bRareName = false;
+ $oSearch->aName = array($oSearchTerm->iId => $oSearchTerm->iId);
+ $aNewSearches[] = $oSearch;
+ }
}
} elseif ($sPhraseType == ''
&& is_a($oSearchTerm, '\Nominatim\Token\SpecialTerm')
if (!empty($this->aName) || !($bFirstPhrase || $sPhraseType == '')) {
if (($sPhraseType == '' || !$bFirstPhrase) && !$bHasPartial) {
$oSearch = clone $this;
- $oSearch->iSearchRank += 2;
+ $oSearch->iSearchRank += 3 * $oSearchTerm->iTermCount;
$oSearch->aAddress[$iWordID] = $iWordID;
$aNewSearches[] = $oSearch;
- } else {
- $this->aFullNameAddress[$iWordID] = $iWordID;
}
} else {
$oSearch = clone $this;
) {
if ($oSearchTerm->iSearchNameCount < CONST_Max_Word_Frequency) {
$oSearch = clone $this;
- $oSearch->iSearchRank += 2;
+ $oSearch->iSearchRank += $oSearchTerm->iTermCount + 1;
+ if (empty($this->aName)) {
+ $oSearch->iSearchRank++;
+ }
+ if (preg_match('#^[0-9]+$#', $sToken)) {
+ $oSearch->iSearchRank++;
+ }
$oSearch->aAddress[$iWordID] = $iWordID;
$aNewSearches[] = $oSearch;
} else {
$oSearch = clone $this;
- $oSearch->iSearchRank++;
+ $oSearch->iSearchRank += $oSearchTerm->iTermCount + 1;
$oSearch->aAddressNonSearch[$iWordID] = $iWordID;
- if (preg_match('#^[0-9]+$#', $sToken)) {
- $oSearch->iSearchRank += 2;
- }
if (!empty($aFullTokens)) {
$oSearch->iSearchRank++;
}
foreach ($aFullTokens as $oSearchTermToken) {
if (is_a($oSearchTermToken, '\Nominatim\Token\Word')) {
$oSearch = clone $this;
- $oSearch->iSearchRank++;
+ $oSearch->iSearchRank += 3;
$oSearch->aAddress[$oSearchTermToken->iId]
= $oSearchTermToken->iId;
$aNewSearches[] = $oSearch;
$iLimit
);
- //now search for housenumber, if housenumber provided
- if ($this->sHouseNumber && !empty($aResults)) {
+ // Now search for housenumber, if housenumber provided. Can be zero.
+ if (($this->sHouseNumber || $this->sHouseNumber === '0') && !empty($aResults)) {
// Downgrade the rank of the street results, they are missing
// the housenumber.
foreach ($aResults as $oRes) {
}
if ($this->sHouseNumber) {
- $aTerms[] = 'address_rank between 16 and 27';
+ $aTerms[] = 'address_rank between 16 and 30';
} elseif (!$this->sClass || $this->iOperator == Operator::NAME) {
if ($iMinAddressRank > 0) {
- $aTerms[] = 'address_rank >= '.$iMinAddressRank;
- }
- if ($iMaxAddressRank < 30) {
- $aTerms[] = 'address_rank <= '.$iMaxAddressRank;
+ $aTerms[] = "((address_rank between $iMinAddressRank and $iMaxAddressRank) or (search_rank between $iMinAddressRank and $iMaxAddressRank))";
}
}
$sImportanceSQL .= $this->oContext->viewboxImportanceSQL('centroid');
$aOrder[] = "$sImportanceSQL DESC";
- if (!empty($this->aFullNameAddress)) {
+ $aFullNameAddress = $this->oContext->getFullNameTerms();
+ if (!empty($aFullNameAddress)) {
$sExactMatchSQL = ' ( ';
$sExactMatchSQL .= ' SELECT count(*) FROM ( ';
- $sExactMatchSQL .= ' SELECT unnest('.$oDB->getArraySQL($this->aFullNameAddress).')';
+ $sExactMatchSQL .= ' SELECT unnest('.$oDB->getArraySQL($aFullNameAddress).')';
$sExactMatchSQL .= ' INTERSECT ';
$sExactMatchSQL .= ' SELECT unnest(nameaddress_vector)';
$sExactMatchSQL .= ' ) s';
'Name terms (stop words)' => $this->aNameNonSearch,
'Address terms' => $this->aAddress,
'Address terms (stop words)' => $this->aAddressNonSearch,
- 'Address terms (full words)' => $this->aFullNameAddress,
+ 'Address terms (full words)' => $this->aFullNameAddress ?? '',
'Special search' => $this->iOperator,
'Class' => $this->sClass,
'Type' => $this->sType,
public function dumpAsHtmlTableRow(&$aWordIDs)
{
$kf = function ($k) use (&$aWordIDs) {
- return $aWordIDs[$k];
+ return $aWordIDs[$k] ?? '['.$k.']';
};
echo '<tr>';