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