]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/search/SearchMssql.php
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / includes / search / SearchMssql.php
1 <?php
2 /**
3  * Mssql search engine
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup Search
22  */
23
24 /**
25  * Search engine hook base class for Mssql (ConText).
26  * @ingroup Search
27  */
28 class SearchMssql extends SearchDatabase {
29         /**
30          * Perform a full text search query and return a result set.
31          *
32          * @param string $term Raw search term
33          * @return SqlSearchResultSet
34          * @access public
35          */
36         function searchText( $term ) {
37                 $resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), true ) );
38                 return new SqlSearchResultSet( $resultSet, $this->searchTerms );
39         }
40
41         /**
42          * Perform a title-only search query and return a result set.
43          *
44          * @param string $term Raw search term
45          * @return SqlSearchResultSet
46          * @access public
47          */
48         function searchTitle( $term ) {
49                 $resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), false ) );
50                 return new SqlSearchResultSet( $resultSet, $this->searchTerms );
51         }
52
53         /**
54          * Return a partial WHERE clause to limit the search to the given namespaces
55          *
56          * @return string
57          * @private
58          */
59         function queryNamespaces() {
60                 $namespaces = implode( ',', $this->namespaces );
61                 if ( $namespaces == '' ) {
62                         $namespaces = '0';
63                 }
64                 return 'AND page_namespace IN (' . $namespaces . ')';
65         }
66
67         /**
68          * Return a LIMIT clause to limit results on the query.
69          *
70          * @param string $sql
71          *
72          * @return string
73          */
74         function queryLimit( $sql ) {
75                 return $this->db->limitResult( $sql, $this->limit, $this->offset );
76         }
77
78         /**
79          * Does not do anything for generic search engine
80          * subclasses may define this though
81          *
82          * @param string $filteredTerm
83          * @param bool $fulltext
84          * @return string
85          */
86         function queryRanking( $filteredTerm, $fulltext ) {
87                 return ' ORDER BY ftindex.[RANK] DESC'; // return ' ORDER BY score(1)';
88         }
89
90         /**
91          * Construct the full SQL query to do the search.
92          * The guts shoulds be constructed in queryMain()
93          *
94          * @param string $filteredTerm
95          * @param bool $fulltext
96          * @return string
97          */
98         function getQuery( $filteredTerm, $fulltext ) {
99                 return $this->queryLimit( $this->queryMain( $filteredTerm, $fulltext ) . ' ' .
100                         $this->queryNamespaces() . ' ' .
101                         $this->queryRanking( $filteredTerm, $fulltext ) . ' ' );
102         }
103
104         /**
105          * Picks which field to index on, depending on what type of query.
106          *
107          * @param bool $fulltext
108          * @return string
109          */
110         function getIndexField( $fulltext ) {
111                 return $fulltext ? 'si_text' : 'si_title';
112         }
113
114         /**
115          * Get the base part of the search query.
116          *
117          * @param string $filteredTerm
118          * @param bool $fulltext
119          * @return string
120          * @private
121          */
122         function queryMain( $filteredTerm, $fulltext ) {
123                 $match = $this->parseQuery( $filteredTerm, $fulltext );
124                 $page = $this->db->tableName( 'page' );
125                 $searchindex = $this->db->tableName( 'searchindex' );
126
127                 return 'SELECT page_id, page_namespace, page_title, ftindex.[RANK]' .
128                         "FROM $page,FREETEXTTABLE($searchindex , $match, LANGUAGE 'English') as ftindex " .
129                         'WHERE page_id=ftindex.[KEY] ';
130         }
131
132         /** @todo document
133          * @param string $filteredText
134          * @param bool $fulltext
135          * @return string
136          */
137         function parseQuery( $filteredText, $fulltext ) {
138                 global $wgContLang;
139                 $lc = $this->legalSearchChars( self::CHARS_NO_SYNTAX );
140                 $this->searchTerms = [];
141
142                 # @todo FIXME: This doesn't handle parenthetical expressions.
143                 $m = [];
144                 $q = [];
145
146                 if ( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/',
147                         $filteredText, $m, PREG_SET_ORDER ) ) {
148                         foreach ( $m as $terms ) {
149                                 $q[] = $terms[1] . $wgContLang->normalizeForSearch( $terms[2] );
150
151                                 if ( !empty( $terms[3] ) ) {
152                                         $regexp = preg_quote( $terms[3], '/' );
153                                         if ( $terms[4] ) {
154                                                 $regexp .= "[0-9A-Za-z_]+";
155                                         }
156                                 } else {
157                                         $regexp = preg_quote( str_replace( '"', '', $terms[2] ), '/' );
158                                 }
159                                 $this->searchTerms[] = $regexp;
160                         }
161                 }
162
163                 $searchon = $this->db->addQuotes( implode( ',', $q ) );
164                 $field = $this->getIndexField( $fulltext );
165                 return "$field, $searchon";
166         }
167
168         /**
169          * Create or update the search index record for the given page.
170          * Title and text should be pre-processed.
171          *
172          * @param int $id
173          * @param string $title
174          * @param string $text
175          * @return bool|ResultWrapper
176          */
177         function update( $id, $title, $text ) {
178                 // We store the column data as UTF-8 byte order marked binary stream
179                 // because we are invoking the plain text IFilter on it so that, and we want it
180                 // to properly decode the stream as UTF-8.  SQL doesn't support UTF8 as a data type
181                 // but the indexer will correctly handle it by this method.  Since all we are doing
182                 // is passing this data to the indexer and never retrieving it via PHP, this will save space
183                 $table = $this->db->tableName( 'searchindex' );
184                 $utf8bom = '0xEFBBBF';
185                 $si_title = $utf8bom . bin2hex( $title );
186                 $si_text = $utf8bom . bin2hex( $text );
187                 $sql = "DELETE FROM $table WHERE si_page = $id;";
188                 $sql .= "INSERT INTO $table (si_page, si_title, si_text) VALUES ($id, $si_title, $si_text)";
189                 return $this->db->query( $sql, 'SearchMssql::update' );
190         }
191
192         /**
193          * Update a search index record's title only.
194          * Title should be pre-processed.
195          *
196          * @param int $id
197          * @param string $title
198          * @return bool|ResultWrapper
199          */
200         function updateTitle( $id, $title ) {
201                 $table = $this->db->tableName( 'searchindex' );
202
203                 // see update for why we are using the utf8bom
204                 $utf8bom = '0xEFBBBF';
205                 $si_title = $utf8bom . bin2hex( $title );
206                 $sql = "DELETE FROM $table WHERE si_page = $id;";
207                 $sql .= "INSERT INTO $table (si_page, si_title, si_text) VALUES ($id, $si_title, 0x00)";
208                 return $this->db->query( $sql, 'SearchMssql::updateTitle' );
209         }
210 }