r6782 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r6781‎ | r6782 | r6783 >
Date:07:51, 23 December 2004
Author:kateturner
Status:old
Tags:
Comment:
find-as-you-type searching (unfinished), various formatting niceties
Modified paths:
  • /trunk/extensions/LuceneSearch.php (modified) (history)

Diff [purge]

Index: trunk/extensions/LuceneSearch.php
@@ -21,7 +21,7 @@
2222
2323 function wfLuceneSearch() {
2424 global $IP;
25 -require_once( "$IP/includes/SpecialPage.php" );
 25+require_once("$IP/includes/SpecialPage.php");
2626
2727 class LuceneSearch extends SpecialPage
2828 {
@@ -42,8 +42,8 @@
4343 return $link;
4444 }
4545
46 - function execute( $par ) {
47 - global $wgRequest, $wgOut, $wgTitle, $wgContLang;
 46+ function execute($par) {
 47+ global $wgRequest, $wgOut, $wgTitle, $wgContLang, $wgUser;
4848
4949 $this->setHeaders();
5050
@@ -56,88 +56,136 @@
5757
5858 $q = $wgRequest->getText('search');
5959
60 - if ($wgRequest->getText('go') === 'Go') {
61 - $t = SearchEngine::getNearMatch($q);
62 - if(!is_null($t)) {
63 - $wgOut->redirect($t->getFullURL());
64 - return;
 60+ if ($wgRequest->getText('gen') == 'titlematch') {
 61+ $limit = $wgRequest->getInt("limit");
 62+ if ($limit < 1 || $limit > 50)
 63+ $limit = 20;
 64+ header("Content-Type: text/plain; charset=ISO_8859-1");
 65+ if (strlen($q) < 1)
 66+ wfAbruptExit();
 67+
 68+ $db =& wfGetDB(DB_SLAVE);
 69+ $page = $db->tableName('page');
 70+ $sql = "SELECT page_title FROM $page WHERE page_namespace=0 AND "
 71+ . "lower(page_title) LIKE lower('".wfStrEncode($q)."%') "
 72+ . "LIMIT $limit";
 73+ $res = $db->query($sql, 'LuceneSearch::execute');
 74+ while ($row = $db->fetchObject($res)) {
 75+ $t = Title::makeTitle(0, $row->page_title);
 76+ echo $t->getPrefixedDBKey() . "\n";
6577 }
 78+ wfAbruptExit();
6679 }
6780
68 - $r = $this->doLuceneSearch($q);
69 - $numresults = $r[0];
70 - $results = $r[1];
71 -
72 - $wgOut->setSubtitle(wfMsg('searchquery', htmlspecialchars($q)));
7381 $wgOut->addWikiText(wfMsg('searchresulttext'));
7482 $wgOut->addHTML($this->showShortDialog($q));
7583
76 - if ($numresults < 1) {
77 - $wgOut->addWikiText(wfMsg("searchnoresults"));
 84+ if ($q != null && strlen($q) > 1) {
 85+ if ($wgRequest->getText('go') === 'Go') {
 86+ $t = SearchEngine::getNearMatch($q);
 87+ if(!is_null($t)) {
 88+ $wgOut->redirect($t->getFullURL());
 89+ return;
 90+ }
 91+ }
 92+
 93+ $r = $this->doLuceneSearch($q);
 94+ $numresults = $r[0];
 95+ $results = $r[1];
 96+
 97+ $limit = $wgRequest->getInt('limit');
 98+ $offset = $wgRequest->getInt('offset');
 99+
 100+ $wgOut->setSubtitle(wfMsg('searchquery', htmlspecialchars($q)));
 101+
78102 $suggestion = trim($results);
79103 if ($numresults == -1 && strlen($suggestion) > 0) {
80 - $wgOut->addHTML(wfMsg("searchdidyoumean",
 104+ $o .= " " . wfMsg("searchdidyoumean",
81105 $this->makelink($suggestion, $offset, $limit),
82 - htmlspecialchars($suggestion)));
 106+ htmlspecialchars($suggestion));
83107 }
84 - } else {
85 - $limit = $wgRequest->getInt('limit');
86 - $offset = $wgRequest->getInt('offset');
87 - if ($limit == 0 || $limit > 100)
88 - $limit = LS_PER_PAGE;
89 -
90 - $showresults = min($limit, count($results)-$numresults);
91 - $i = $offset;
92 - $resq = trim(preg_replace("/[] \\|[()\"{}]+/", " ", $q));
93 - $contextWords = implode("|",
 108+ $wgOut->addHTML("<div style='text-align: center'>".$o."</div>");
 109+
 110+ $nmtext = "";
 111+ if ($offset == 0) {
 112+ $titles = $this->doTitleMatches($q);
 113+ if (count($titles) > 0) {
 114+ $sk =& $wgUser->getSkin();
 115+ $nmtext = "<p>".wfMsg('searchnearmatches');
 116+ $i = 0;
 117+ $wgOut->addHTML("<ul>");
 118+ foreach ($titles as $title) {
 119+ if (++$i > 5) break;
 120+ $nmtext .= wfMsg('searchnearmatch',
 121+ $sk->makeKnownLinkObj($title, ''));
 122+ }
 123+ $nmtext .= "</ul>";
 124+ $nmtext .= "<hr/></p>";
 125+ }
 126+ }
 127+
 128+ $wgOut->addHTML($nmtext);
 129+
 130+ if ($numresults < 1) {
 131+ $o = wfMsg("searchnoresults");
 132+ $wgOut->addHTML($o);
 133+ } else {
 134+ if ($limit == 0 || $limit > 100)
 135+ $limit = LS_PER_PAGE;
 136+
 137+ $showresults = min($limit, count($results)-$numresults);
 138+ $i = $offset;
 139+ $resq = trim(preg_replace("/[] \\|[()\"{}]+/", " ", $q));
 140+ $contextWords = implode("|",
94141 $wgContLang->convertForSearchResult(split(" ", $resq)));
95142
96 - $top = wfMsg("searchnumber", $offset + 1,
97 - min($numresults, $offset+$limit), $numresults);
98 - $out = "<ul start=".($offset + 1).">";
99 - $chunks = array_chunk($results, $limit);
100 - $numchunks = ceil($numresults / $limit);
101 - $whichchunk = $offset / $limit;
102 - $prevnext = "";
103 - if ($whichchunk > 0)
104 - $prevnext .= "<a href=\"".
105 - $this->makelink($q, $offset-$limit, $limit)."\">".
106 - wfMsg("searchprev")."</a> ";
107 - $first = max($whichchunk - 11, 0);
108 - $last = min($numchunks, $whichchunk + 11);
109 - //$wgOut->addWikiText("whichchunk=$whichchunk numchunks=$numchunks first=$first last=$last num=".count($chunks)." limit=$limit offset=$offset results=".count($results)."\n\n");
110 - for($i = $first; $i < $last; $i++) {
111 - if ($i === $whichchunk)
112 - $prevnext .= "<strong>".($i+1)."</strong> ";
113 - else
 143+ $top = wfMsg("searchnumber", $offset + 1,
 144+ min($numresults, $offset+$limit), $numresults);
 145+ $out = "<ul start=".($offset + 1).">";
 146+ $chunks = array_chunk($results, $limit);
 147+ $numchunks = ceil($numresults / $limit);
 148+ $whichchunk = $offset / $limit;
 149+ $prevnext = "";
 150+ if ($whichchunk > 0)
114151 $prevnext .= "<a href=\"".
115 - $this->makelink($q, $limit*$i,
116 - $limit)."\">".($i+1)."</a> ";
 152+ $this->makelink($q, $offset-$limit, $limit)."\">".
 153+ wfMsg("searchprev")."</a> ";
 154+ $first = max($whichchunk - 11, 0);
 155+ $last = min($numchunks, $whichchunk + 11);
 156+ //$wgOut->addWikiText("whichchunk=$whichchunk numchunks=$numchunks first=$first last=$last num=".count($chunks)." limit=$limit offset=$offset results=".count($results)."\n\n");
 157+ for($i = $first; $i < $last; $i++) {
 158+ if ($i === $whichchunk)
 159+ $prevnext .= "<strong>".($i+1)."</strong> ";
 160+ else
 161+ $prevnext .= "<a href=\"".
 162+ $this->makelink($q, $limit*$i,
 163+ $limit)."\">".($i+1)."</a> ";
 164+ }
 165+ if ($whichchuck < $numchunks)
 166+ $prevnext .= "<a href=\"".
 167+ $this->makelink($q, $offset + $limit, $limit)."\">".
 168+ wfMsg("searchnext")."</a> ";
 169+ $prevnext = "<div style='text-align: center'>$prevnext</div>";
 170+ $top .= $prevnext;
 171+ foreach ($chunks[$whichchunk] as $result) {
 172+ $out .= $this->showHit($result[0], $result[1], $contextWords);
 173+ }
 174+ $out .= "</ol>";
117175 }
118 - if ($whichchuck < $numchunks)
119 - $prevnext .= "<a href=\"".
120 - $this->makelink($q, $offset + $limit, $limit)."\">".
121 - wfMsg("searchnext")."</a> ";
122 - $prevnext = "<div style='text-align: center'>$prevnext</div>";
123 - $top .= $prevnext;
124 - foreach ($chunks[$whichchunk] as $result) {
125 - $out .= $this->showHit($result[0], $result[1], $contextWords);
126 - }
127 - $out .= "</ol>";
 176+ $wgOut->addHTML("<hr/>" . $top . $out);
 177+ $wgOut->addHTML("<hr/>" . $prevnext);
 178+ $wgOut->addHTML($this->showFullDialog($q));
128179 }
129 - $wgOut->addHTML("<hr/>" . $top . $out);
130 - $wgOut->addHTML("<hr/>" . $prevnext);
131 - $wgOut->addHTML($this->showFullDialog($q));
132180 $wgOut->setRobotpolicy('noindex,nofollow');
133181 }
134182
135183 function showHit($score, $t, $terms) {
136184 $fname = 'LuceneSearch::showHit';
137 - wfProfileIn( $fname );
 185+ wfProfileIn($fname);
138186 global $wgUser, $wgContLang;
139187
140188 if(is_null($t)) {
141 - wfProfileOut( $fname );
 189+ wfProfileOut($fname);
142190 return "<!-- Broken link in search result -->\n";
143191 }
144192 $sk =& $wgUser->getSkin();
@@ -159,7 +207,7 @@
160208
161209 $lines = explode("\n", $text);
162210
163 - $max = IntVal( $contextchars ) + 1;
 211+ $max = IntVal($contextchars) + 1;
164212 $pat1 = "/(.*)($terms)(.{0,$max})/i";
165213
166214 $lineno = 0;
@@ -189,8 +237,8 @@
190238
191239 $extract .= "<br /><small>{$line}</small>\n";
192240 }
193 - wfProfileOut( "$fname-extract" );
194 - wfProfileOut( $fname );
 241+ wfProfileOut("$fname-extract");
 242+ wfProfileOut($fname);
195243 $date = $wgContLang->timeanddate($rev->getTimestamp());
196244 $percent = sprintf("%2.1f%%", $score * 100);
197245 //$score = wfMsg("searchscore", $percent);
@@ -218,13 +266,34 @@
219267 return $text;
220268 }
221269
222 - function doLuceneSearch( $query ) {
 270+ function doTitleMatches($query) {
223271 global $wgLuceneHost, $wgLucenePort;
224272 $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
225273 $conn = socket_connect($sock, $wgLuceneHost, $wgLucenePort);
226 - socket_write($sock, urlencode($query) . "\n");
 274+ socket_write($sock, "TITLEMATCH\n" . urlencode($query) . "\n");
227275 $results = array();
 276+ while (($result = @socket_read($sock, 1024, PHP_NORMAL_READ)) != FALSE) {
 277+ $result = chop($result);
 278+ list($score, $namespace, $title) = split(" ", $result);
 279+ if (!in_array($namespace, $this->namespaces)) {
 280+ continue;
 281+ }
 282+ $fulltitle = Title::makeTitle($namespace, $title);
 283+ if ($fulltitle === null) {
 284+ continue;
 285+ }
 286+ $results[] = $fulltitle;
 287+ }
 288+ return $results;
 289+ }
228290
 291+ function doLuceneSearch($query) {
 292+ global $wgLuceneHost, $wgLucenePort;
 293+ $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
 294+ $conn = socket_connect($sock, $wgLuceneHost, $wgLucenePort);
 295+ socket_write($sock, "SEARCH\n" . urlencode($query) . "\n");
 296+ $results = array();
 297+
229298 $numresults = @socket_read($sock, 1024, PHP_NORMAL_READ);
230299 wfDebug("total [$numresults] hits\n");
231300 if ($numresults === FALSE)
@@ -257,14 +326,20 @@
258327 }
259328
260329 function showShortDialog($term) {
261 - $searchField = "<input type='text' name=\"search\" value=\"" .
262 - htmlspecialchars($term) ."\" width=\"80\" />\n";
263 -
264330 $searchButton = '<input type="submit" name="searchx" value="' .
265331 htmlspecialchars(wfMsg('powersearch')) . "\" />\n";
266 - $ret = $searchField . $searchButton;
267 - return "<form id=\"search\" method=\"get\" " .
268 - "action=\"$action\">\n<div style='text-align: center'>{$ret}</div>\n</form>\n";
 332+ $searchField = "<div><input type='text' id='lsearchbox' onkeyup=\"resultType()\" "
 333+ . "name=\"search\" value=\""
 334+ . htmlspecialchars($term) ."\" style='width: 50%;margin-left: 25%' "
 335+ . " autocomplete=\"off\" />\n"
 336+ . "<span id='loadStatus'></span>"
 337+ . $searchButton
 338+ . "<div id='results'></div></div>";
 339+
 340+ $ret = $searchField /*. $searchButton*/;
 341+ return $this->makeSuggestJS()
 342+ . "<form id=\"search\" method=\"get\" "
 343+ . "action=\"$action\">\n<div>{$ret}</div>\n</form>\n";
269344 }
270345
271346 function showFullDialog($term) {
@@ -274,7 +349,7 @@
275350 $checked = in_array($ns, $this->namespaces)
276351 ? ' checked="checked"'
277352 : '';
278 - $name = str_replace( '_', ' ', $name );
 353+ $name = str_replace('_', ' ', $name);
279354 if('' == $name) {
280355 $name = wfMsg('blanknamespace');
281356 }
@@ -283,7 +358,7 @@
284359 }
285360
286361 $searchField = "<input type='text' name=\"search\" value=\"" .
287 - htmlspecialchars( $term ) ."\" width=\"80\" />\n";
 362+ htmlspecialchars($term) ."\" width=\"80\" />\n";
288363
289364 $searchButton = '<input type="submit" name="searchx" value="' .
290365 htmlspecialchars(wfMsg('powersearch')) . "\" />\n";
@@ -291,24 +366,104 @@
292367 $ret = wfMsg('lucenepowersearchtext',
293368 $namespaces, $redirect, $searchField,
294369 '', '', '', '', '', # Dummy placeholders
295 - $searchButton );
 370+ $searchButton);
296371
297 - $title = Title::makeTitle( NS_SPECIAL, 'Search' );
 372+ $title = Title::makeTitle(NS_SPECIAL, 'Search');
298373 $action = $title->escapeLocalURL();
299374 return "<br /><br />\n<form id=\"powersearch\" method=\"get\" " .
300375 "action=\"$action\">\n{$ret}\n</form>\n";
301376 }
 377+
 378+ function makeSuggestJS() {
 379+ global $wgScript;
 380+ return <<<___EOF___
 381+<script language="javascript">
 382+
 383+var xmlHttp = (window.XMLHttpRequest) ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP");
 384+var searchCache = {};
 385+var searchStr;
 386+var searchTimeout;
 387+
 388+function getResults()
 389+{
 390+// alert(searchStr)
 391+ //xmlHttp.open("GET", "$wgScript?title=Special:Search?gen=titlematch&ns0=0&limit=10&search=" + escape(searchStr), true);
 392+ var encStr = escape(searchStr.replace(/ /g, '_'));
 393+ xmlHttp.open("GET", "/w/Special:Search?gen=titlematch&ns0=0&limit=10&search=" + encStr, true);
 394+
 395+ xmlHttp.onreadystatechange = parseResults;
 396+ xmlHttp.send(null);
 397+ //document.getElementById("results").innerHTML = "Loading...";
302398 }
303399
 400+function parseResults()
 401+{
 402+ if (xmlHttp.readyState > 3)
 403+ {
 404+ document.getElementById("loadStatus").innerHTML = "";
 405+ var resultArr = xmlHttp.responseText.split("\\n");
 406+ searchCache[searchStr.toLowerCase()] = resultArr;
 407+ showResults(resultArr);
 408+ }
 409+}
 410+
 411+function showResults(resultArr)
 412+{
 413+ var returnStr = "";
 414+ var resultsEl = document.getElementById("results");
 415+
 416+ if (resultArr.length < 2)
 417+ resultsEl.innerHTML = "No results";
 418+ else
 419+ {
 420+ resultsEl.innerHTML = "";
 421+
 422+ for (var i=0; i < resultArr.length; i++)
 423+ {
 424+ var linkEl = document.createElement("a");
 425+ linkEl.href = "$wgScript?title=" + resultArr[i];
 426+ var textEl = document.createTextNode(resultArr[i].replace(/_/g, ' '));
 427+ linkEl.appendChild(textEl);
 428+ resultsEl.appendChild(linkEl);
 429+ }
 430+ }
 431+
 432+ resultsEl.style.display = "block";
 433+}
 434+
 435+function resultType()
 436+{
 437+ searchStr = document.getElementById("lsearchbox").value;
 438+ if (searchTimeout) clearTimeout(searchTimeout);
 439+
 440+ if (searchStr != "")
 441+ {
 442+ if (searchCache[searchStr.toLowerCase()])
 443+ showResults(searchCache[searchStr.toLowerCase()])
 444+ else
 445+ searchTimeout = setTimeout(getResults, 500);
 446+ }
 447+ else
 448+ {
 449+ document.getElementById("results").style.display = "none";
 450+ }
 451+}
 452+</script>
 453+___EOF___;
 454+ }
 455+}
 456+
304457 global $wgMessageCache;
305 -SpecialPage::addPage( new LuceneSearch );
 458+SpecialPage::addPage(new LuceneSearch);
306459 $wgMessageCache->addMessage("searchnumber", "<strong>Results $1-$2 of $3</strong>");
307460 $wgMessageCache->addMessage("searchprev", "&#x00AB; <span style='font-size: small'>Prev</span>");
308461 $wgMessageCache->addMessage("searchnext", "<span style='font-size: small'>Next</span> &#x00BB;");
309462 $wgMessageCache->addMessage("searchscore", "Relevancy: $1");
310463 $wgMessageCache->addMessage("searchsize", "$1k ($2 words)");
311 -$wgMessageCache->addMessage("searchdidyoumean", "Did you mean \"<a href=\"$1\">$2</a>\"?");
312 -$wgMessageCache->addMessage("searchnoresults", "Sorry, there were no matches to your query.");
 464+$wgMessageCache->addMessage("searchdidyoumean", "Did you mean: \"<a href=\"$1\">$2</a>\"?");
 465+$wgMessageCache->addMessage("searchnoresults", "Sorry, there were no exact matches to your query.");
 466+$wgMessageCache->addMessage("searchnearmatches", "<b>These pages have similar titles to your query:</b>\n");
 467+$wgMessageCache->addMessage("searchnearmatch", "<li>$1</li>\n");
313468 $wgMessageCache->addMessage("lucenepowersearchtext", "
314469 Search in namespaces:\n
315470 $1\n

Status & tagging log