]> git.openstreetmap.org Git - nominatim.git/blob - lib-php/Phrase.php
Merge pull request #2373 from lonvia/tweak-search-cost
[nominatim.git] / lib-php / Phrase.php
1 <?php
2
3 namespace Nominatim;
4
5 /**
6  * Segment of a query string.
7  *
8  * The parts of a query strings are usually separated by commas.
9  */
10 class Phrase
11 {
12     const MAX_WORDSET_LEN = 20;
13     const MAX_WORDSETS = 100;
14
15     // Complete phrase as a string.
16     private $sPhrase;
17     // Element type for structured searches.
18     private $sPhraseType;
19     // Possible segmentations of the phrase.
20     private $aWordSets;
21
22     public static function cmpByArraylen($aA, $aB)
23     {
24         $iALen = count($aA);
25         $iBLen = count($aB);
26
27         if ($iALen == $iBLen) {
28             return 0;
29         }
30
31         return ($iALen < $iBLen) ? -1 : 1;
32     }
33
34
35     public function __construct($sPhrase, $sPhraseType)
36     {
37         $this->sPhrase = trim($sPhrase);
38         $this->sPhraseType = $sPhraseType;
39     }
40
41     /**
42      * Get the orginal phrase of the string.
43      */
44     public function getPhrase()
45     {
46         return $this->sPhrase;
47     }
48
49     /**
50      * Return the element type of the phrase.
51      *
52      * @return string Pharse type if the phrase comes from a structured query
53      *                or empty string otherwise.
54      */
55     public function getPhraseType()
56     {
57         return $this->sPhraseType;
58     }
59
60     /**
61      * Return the array of possible segmentations of the phrase.
62      *
63      * @return string[][] Array of segmentations, each consisting of an
64      *                    array of terms.
65      */
66     public function getWordSets()
67     {
68         return $this->aWordSets;
69     }
70
71     /**
72      * Invert the set of possible segmentations.
73      *
74      * @return void
75      */
76     public function invertWordSets()
77     {
78         foreach ($this->aWordSets as $i => $aSet) {
79             $this->aWordSets[$i] = array_reverse($aSet);
80         }
81     }
82
83     public function computeWordSets($aWords, $oTokens)
84     {
85         $iNumWords = count($aWords);
86
87         if ($iNumWords == 0) {
88             $this->aWordSets = null;
89             return;
90         }
91
92         // Caches the word set for the partial phrase up to word i.
93         $aSetCache = array_fill(0, $iNumWords, array());
94
95         // Initialise first element of cache. There can only be the word.
96         if ($oTokens->containsAny($aWords[0])) {
97             $aSetCache[0][] = array($aWords[0]);
98         }
99
100         // Now do the next elements using what we already have.
101         for ($i = 1; $i < $iNumWords; $i++) {
102             for ($j = $i; $j > 0; $j--) {
103                 $sPartial = $j == $i ? $aWords[$j] : $aWords[$j].' '.$sPartial;
104                 if (!empty($aSetCache[$j - 1]) && $oTokens->containsAny($sPartial)) {
105                     $aPartial = array($sPartial);
106                     foreach ($aSetCache[$j - 1] as $aSet) {
107                         if (count($aSet) < Phrase::MAX_WORDSET_LEN) {
108                             $aSetCache[$i][] = array_merge($aSet, $aPartial);
109                         }
110                     }
111                     if (count($aSetCache[$i]) > 2 * Phrase::MAX_WORDSETS) {
112                         usort(
113                             $aSetCache[$i],
114                             array('\Nominatim\Phrase', 'cmpByArraylen')
115                         );
116                         $aSetCache[$i] = array_slice(
117                             $aSetCache[$i],
118                             0,
119                             Phrase::MAX_WORDSETS
120                         );
121                     }
122                 }
123             }
124
125             // finally the current full phrase
126             $sPartial = $aWords[0].' '.$sPartial;
127             if ($oTokens->containsAny($sPartial)) {
128                 $aSetCache[$i][] = array($sPartial);
129             }
130         }
131
132         $this->aWordSets = $aSetCache[$iNumWords - 1];
133         usort($this->aWordSets, array('\Nominatim\Phrase', 'cmpByArraylen'));
134         $this->aWordSets = array_slice($this->aWordSets, 0, Phrase::MAX_WORDSETS);
135     }
136
137
138     public function debugInfo()
139     {
140         return array(
141                 'Type' => $this->sPhraseType,
142                 'Phrase' => $this->sPhrase,
143                 'WordSets' => $this->aWordSets
144                );
145     }
146 }