* @package MediaWiki not Mediawiki
[lhc/web/wiklou.git] / includes / SearchEngine.php
1 <?php
2 /**
3 * Contain a class for special pages
4 * @package MediaWiki
5 */
6
7 /**
8 * @package MediaWiki
9 */
10 class SearchEngine {
11 var $limit = 10;
12 var $offset = 0;
13 var $searchTerms = array();
14 var $namespaces = array( 0 );
15 var $showRedirects = false;
16
17 /**
18 * Perform a full text search query and return a result set.
19 *
20 * @param string $term - Raw search term
21 * @param array $namespaces - List of namespaces to search
22 * @return ResultWrapper
23 * @access public
24 */
25 function searchText( $term ) {
26 return $this->db->resultObject( $this->db->query( $this->getQuery( $this->filter( $term ), true ) ) );
27 }
28
29 /**
30 * Perform a title-only search query and return a result set.
31 *
32 * @param string $term - Raw search term
33 * @param array $namespaces - List of namespaces to search
34 * @return ResultWrapper
35 * @access public
36 */
37 function searchTitle( $term ) {
38 return $this->db->resultObject( $this->db->query( $this->getQuery( $this->filter( $term ), false ) ) );
39 }
40
41 /**
42 * If an exact title match can be find, or a very slightly close match,
43 * return the title. If no match, returns NULL.
44 *
45 * @param string $term
46 * @return Title
47 * @access private
48 */
49 function getNearMatch( $term ) {
50 # Exact match? No need to look further.
51 $title = Title::newFromText( $term );
52 if ( $title->getNamespace() == NS_SPECIAL || 0 != $title->getArticleID() ) {
53 return $title;
54 }
55
56 # Now try all lower case (i.e. first letter capitalized)
57 #
58 $title = Title::newFromText( strtolower( $term ) );
59 if ( 0 != $title->getArticleID() ) {
60 return $title;
61 }
62
63 # Now try capitalized string
64 #
65 $title = Title::newFromText( ucwords( strtolower( $term ) ) );
66 if ( 0 != $title->getArticleID() ) {
67 return $title;
68 }
69
70 # Now try all upper case
71 #
72 $title = Title::newFromText( strtoupper( $term ) );
73 if ( 0 != $title->getArticleID() ) {
74 return $title;
75 }
76
77 $title = Title::newFromText( $term );
78
79 # Entering an IP address goes to the contributions page
80 if ( ( $title->getNamespace() == NS_USER && User::isIP($title->getText() ) )
81 || User::isIP( trim( $term ) ) ) {
82 return Title::makeTitle( NS_SPECIAL, "Contributions/" . $title->getDbkey() );
83 }
84
85
86 # Entering a user goes to the user page whether it's there or not
87 if ( $title->getNamespace() == NS_USER ) {
88 return $title;
89 }
90
91 return NULL;
92 }
93
94 function legalSearchChars() {
95 return "A-Za-z_'0-9\\x80-\\xFF\\-";
96 }
97
98 /**
99 * Set the maximum number of results to return
100 * and how many to skip before returning the first.
101 *
102 * @param int $limit
103 * @param int $offset
104 * @access public
105 */
106 function setLimitOffset( $limit, $offset = 0 ) {
107 $this->limit = IntVal( $limit );
108 $this->offset = IntVal( $offset );
109 }
110
111 /**
112 * Set which namespaces the search should include.
113 * Give an array of namespace index numbers.
114 *
115 * @param array $namespaces
116 * @access public
117 */
118 function setNamespaces( $namespaces ) {
119 $this->namespaces = $namespaces;
120 }
121
122 /**
123 * Make a list of searchable namespaces and their canonical names.
124 * @return array
125 * @access public
126 */
127 function searchableNamespaces() {
128 global $wgContLang;
129 $arr = array();
130 foreach( $wgContLang->getNamespaces() as $ns => $name ) {
131 if( $ns >= NS_MAIN ) {
132 $arr[$ns] = $name;
133 }
134 }
135 return $arr;
136 }
137
138 /**
139 * Fetch an array of regular expression fragments for matching
140 * the search terms as parsed by this engine in a text extract.
141 *
142 * @return array
143 * @access public
144 */
145 function termMatches() {
146 return $this->searchTerms;
147 }
148
149 /**
150 * Return a 'cleaned up' search string
151 *
152 * @return string
153 * @access public
154 */
155 function filter( $text ) {
156 $lc = $this->legalSearchChars();
157 return trim( preg_replace( "/[^{$lc}]/", " ", $text ) );
158 }
159
160 /**
161 * Return a partial WHERE clause to exclude redirects, if so set
162 * @return string
163 * @access private
164 */
165 function queryRedirect() {
166 if( $this->showRedirects ) {
167 return 'AND cur_is_redirect=0';
168 } else {
169 return '';
170 }
171 }
172
173 /**
174 * Return a partial WHERE clause to limit the search to the given namespaces
175 * @return string
176 * @access private
177 */
178 function queryNamespaces() {
179 $namespaces = implode( ',', $this->namespaces );
180 if ($namespaces == '') {
181 $namespaces = '0';
182 }
183 return 'AND page_namespace IN (' . $namespaces . ')';
184 }
185
186 /**
187 * Return a LIMIT clause to limit results on the query.
188 * @return string
189 * @access private
190 */
191 function queryLimit() {
192 return $this->db->limitResult( $this->limit, $this->offset );
193 }
194
195 /**
196 * Does not do anything for generic search engine
197 * subclasses may define this though
198 * @return string
199 * @access private
200 */
201 function queryRanking($filteredTerm,$fulltext) {
202 return "";
203 }
204
205 /**
206 * Construct the full SQL query to do the search.
207 * The guts shoulds be constructed in queryMain()
208 * @param string $filteredTerm
209 * @param bool $fulltext
210 * @access private
211 */
212 function getQuery( $filteredTerm, $fulltext ) {
213 return $this->queryMain( $filteredTerm, $fulltext ) . ' ' .
214 $this->queryRedirect() . ' ' .
215 $this->queryNamespaces() . ' ' .
216 $this->queryRanking($filteredTerm, $fulltext) . ' ' .
217 $this->queryLimit();
218 }
219
220 /**
221 * Load up the appropriate search engine class for the currently
222 * active database backend, and return a configured instance.
223 *
224 * @return SearchEngine
225 * @access private
226 */
227 function create() {
228 global $wgDBtype, $wgDBmysql4, $wgSearchType;
229 if( $wgDBtype == 'mysql' ) {
230 if( $wgDBmysql4 ) {
231 $class = 'SearchMySQL4';
232 require_once( 'SearchMySQL4.php' );
233 } else {
234 $class = 'SearchMysql3';
235 require_once( 'SearchMySQL3.php' );
236 }
237 } else if ( $wgDBtype == 'PostgreSQL' ) {
238 $class = 'SearchTsearch2';
239 require_once( 'SearchTsearch2.php' );
240 } else {
241 $class = 'SearchEngineDummy';
242 }
243 $search = new $class( wfGetDB( DB_SLAVE ) );
244 $search->setLimitOffset(0,0);
245 return $search;
246 }
247
248
249 }
250
251 /**
252 * @package MediaWiki
253 */
254 class SearchEngineDummy {
255 function search( $term ) {
256 return null;
257 }
258 }
259