]> git.openstreetmap.org Git - nominatim.git/blob - lib/TokenList.php
Merge pull request #1043 from lonvia/token-as-a-class
[nominatim.git] / lib / TokenList.php
1 <?php
2
3 namespace Nominatim;
4
5 require_once(CONST_BasePath.'/lib/TokenCountry.php');
6 require_once(CONST_BasePath.'/lib/TokenHousenumber.php');
7 require_once(CONST_BasePath.'/lib/TokenPostcode.php');
8 require_once(CONST_BasePath.'/lib/TokenSpecialTerm.php');
9 require_once(CONST_BasePath.'/lib/TokenWord.php');
10 require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
11
12 /**
13  * Saves information about the tokens that appear in a search query.
14  *
15  * Tokens are sorted by their normalized form, the token word. There are different
16  * kinds of tokens, represented by different Token* classes. Note that
17  * tokens do not have a common base class. All tokens need to have a field
18  * with the word id that points to an entry in the `word` database table
19  * but otherwise the information saved about a token can be very different.
20  *
21  * There are two different kinds of token words: full words and partial terms.
22  *
23  * Full words start with a space. They represent a complete name of a place.
24  * All special tokens are normally full words.
25  *
26  * Partial terms have no space at the beginning. They may represent a part of
27  * a name of a place (e.g. in the name 'World Trade Center' a partial term
28  * would be 'Trade' or 'Trade Center'). They are only used in TokenWord.
29  */
30 class TokenList
31 {
32     // List of list of tokens indexed by their word_token.
33     private $aTokens = array();
34
35     /**
36      * Check if there are tokens for the given token word.
37      *
38      * @param string $sWord Token word to look for.
39      *
40      * @return bool True if there is one or more token for the token word.
41      */
42     public function contains($sWord)
43     {
44         return isset($this->aTokens[$sWord]);
45     }
46
47     /**
48      * Get the list of tokens for the given token word.
49      *
50      * @param string $sWord Token word to look for.
51      *
52      * @return object[] Array of tokens for the given token word or an
53      *                  empty array if no tokens could be found.
54      */
55     public function get($sWord)
56     {
57         return isset($this->aTokens[$sWord]) ? $this->aTokens[$sWord] : array();
58     }
59
60     /**
61      * Add token information from the word table in the database.
62      *
63      * @param object   $oDB           Database connection.
64      * @param string[] $aTokens       List of tokens to look up in the database.
65      * @param string[] $aCountryCodes List of country restrictions.
66      * @param string   $sNormQuery    Normalized query string.
67      * @param object   $oNormalizer   Normalizer function to use on tokens.
68      *
69      * @return void
70      */
71     public function addTokensFromDB(&$oDB, &$aTokens, &$aCountryCodes, $sNormQuery, $oNormalizer)
72     {
73         // Check which tokens we have, get the ID numbers
74         $sSQL = 'SELECT word_id, word_token, word, class, type, country_code,';
75         $sSQL .= ' operator, coalesce(search_name_count, 0) as count';
76         $sSQL .= ' FROM word WHERE word_token in (';
77         $sSQL .= join(',', array_map('getDBQuoted', $aTokens)).')';
78
79         Debug::printSQL($sSQL);
80
81         $aDBWords = chksql($oDB->getAll($sSQL), 'Could not get word tokens.');
82
83         foreach ($aDBWords as $aWord) {
84             $oToken = null;
85             $iId = (int) $aWord['word_id'];
86
87             if ($aWord['class']) {
88                 // Special terms need to appear in their normalized form.
89                 if ($aWord['word']) {
90                     $sNormWord = $aWord['word'];
91                     if ($oNormalizer != null) {
92                         $sNormWord = $oNormalizer->transliterate($aWord['word']);
93                     }
94                     if (strpos($sNormQuery, $sNormWord) === false) {
95                         continue;
96                     }
97                 }
98
99                 if ($aWord['class'] == 'place' && $aWord['type'] == 'house') {
100                     $oToken = new Token\HouseNumber($iId, trim($aWord['word_token']));
101                 } elseif ($aWord['class'] == 'place' && $aWord['type'] == 'postcode') {
102                     if ($aWord['word']
103                         && pg_escape_string($aWord['word']) == $aWord['word']
104                     ) {
105                         $oToken = new Token\Postcode(
106                             $iId,
107                             $aWord['word'],
108                             $aWord['country_code']
109                         );
110                     }
111                 } else {
112                     // near and in operator the same at the moment
113                     $oToken = new Token\SpecialTerm(
114                         $iId,
115                         $aWord['class'],
116                         $aWord['type'],
117                         $aWord['operator'] ? Operator::NONE : Operator::NEAR
118                     );
119                 }
120             } elseif ($aWord['country_code']) {
121                 // Filter country tokens that do not match restricted countries.
122                 if (!$aCountryCodes
123                     || in_array($aWord['country_code'], $aCountryCodes)
124                 ) {
125                     $oToken = new Token\Country($iId, $aWord['country_code']);
126                 }
127             } else {
128                 $oToken = new Token\Word(
129                     $iId,
130                     $aWord['word'][0] != ' ',
131                     (int) $aWord['count']
132                 );
133             }
134
135             if ($oToken) {
136                 $this->addToken($aWord['word_token'], $oToken);
137             }
138         }
139     }
140
141     /**
142      * Add a new token for the given word.
143      *
144      * @param string $sWord  Word the token describes.
145      * @param object $oToken Token object to add.
146      *
147      * @return void
148      */
149     public function addToken($sWord, $oToken)
150     {
151         if (isset($this->aTokens[$sWord])) {
152             $this->aTokens[$sWord][] = $oToken;
153         } else {
154             $this->aTokens[$sWord] = array($oToken);
155         }
156     }
157
158     public function debugTokenByWordIdList()
159     {
160         $aWordsIDs = array();
161         foreach ($this->aTokens as $sToken => $aWords) {
162             foreach ($aWords as $aToken) {
163                 if ($aToken->iId !== null) {
164                     $aWordsIDs[$aToken->iId] =
165                         '#'.$sToken.'('.$aToken->iId.')#';
166                 }
167             }
168         }
169
170         return $aWordsIDs;
171     }
172
173     public function debugInfo()
174     {
175         return $this->aTokens;
176     }
177 }