Document xapian-compact --blocksize takes an argument
[xapian.git] / xapian-core / tests / queryparsertest.cc
blob4f8237f300aba433d9ad2328f4430993b7baadb4
1 /* queryparsertest.cc: Tests of Xapian::QueryParser
3 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2015 Olly Betts
4 * Copyright (C) 2007,2009 Lemur Consulting Ltd
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
22 #include <config.h>
24 #include <xapian.h>
26 #include "cputimer.h"
27 #include "str.h"
28 #include "stringutils.h"
30 #include <cmath>
31 #include <iostream>
32 #include <string>
33 #include <vector>
34 #include "safesysstat.h" // For mkdir().
36 using namespace std;
38 #include "testsuite.h"
39 #include "testutils.h"
41 struct test {
42 const char *query;
43 const char *expect;
46 static const test test_or_queries[] = {
47 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
48 { "time_t", "Ztime_t@1" },
49 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
50 { "foo -baz bar", "((Zfoo@1 OR Zbar@3) AND_NOT Zbaz@2)" },
51 { "d- school report", "(Zd@1 OR (Zschool@2 OR Zreport@3))" },
52 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
53 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
54 { "Mg2+ Cl-", "(mg2+@1 OR cl@2)" },
55 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
56 { "A&L A&RMCO AD&D", "(a&l@1 OR a&rmco@2 OR ad&d@3)" },
57 { "C# vs C++", "(c#@1 OR Zvs@2 OR c++@3)" },
58 { "j##", "Zj##@1" },
59 { "a#b", "(Za@1 OR Zb@2)" },
60 { "O.K. U.N.C.L.E XY.Z.", "((ok@1 OR uncle@2) OR (xy@3 PHRASE 2 z@4))" },
61 { "author:orwell animal farm", "(ZAorwel@1 OR Zanim@2 OR Zfarm@3)" },
62 { "author:Orwell Animal Farm", "(Aorwell@1 OR animal@2 OR farm@3)" },
63 // Regression test for bug reported in 0.9.6.
64 { "author:\"orwell\" title:\"animal\"", "(Aorwell@1 OR XTanimal@2)" },
65 // Regression test for bug related to one reported in 0.9.6.
66 { "author:(orwell) title:(animal)", "(ZAorwel@1 OR ZXTanim@2)" },
67 // Regression test for bug caused by fix for previous bug.
68 { "author:\"milne, a.a.\"", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
69 { "author:\"milne a.a.\"", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
70 // Regression test for bug reported in 0.9.7.
71 { "site:/path/name", "0 * H/path/name" },
72 // Regression test for bug introduced (and fixed) in SVN prior to 1.0.0.
73 { "author:/path/name", "(Apath@1 PHRASE 2 Aname@2)" },
74 // Feature tests for change to allow phrase generators after prefix in 1.2.4.
75 { "author:/path", "ZApath@1" },
76 { "author:-Foo", "Afoo@1" },
77 { "author:/", "Zauthor@1" },
78 { "author::", "Zauthor@1" },
79 { "author:/ foo", "(Zauthor@1 OR Zfoo@2)" },
80 { "author:: foo", "(Zauthor@1 OR Zfoo@2)" },
81 { "author::foo", "(author@1 PHRASE 2 foo@2)" },
82 { "author:/ AND foo", "(Zauthor@1 AND Zfoo@2)" },
83 { "author:: AND foo", "(Zauthor@1 AND Zfoo@2)" },
84 { "foo AND author:/", "(Zfoo@1 AND Zauthor@2)" },
85 { "foo AND author::", "(Zfoo@1 AND Zauthor@2)" },
86 // Regression test for bug introduced into (and fixed) in SVN prior to 1.0.0.
87 { "author:(title::case)", "(Atitle@1 PHRASE 2 Acase@2)" },
88 // Regression test for bug fixed in 1.0.4 - the '+' would be ignored there
89 // because the whitespace after the '"' wasn't noticed.
90 { "\"hello world\" +python", "(Zpython@3 AND_MAYBE (hello@1 PHRASE 2 world@2))" },
91 // In 1.1.0, NON_SPACING_MARK was added as a word character.
92 { "\xd8\xa7\xd9\x84\xd8\xb1\xd9\x91\xd8\xad\xd9\x85\xd9\x86", "Z\xd8\xa7\xd9\x84\xd8\xb1\xd9\x91\xd8\xad\xd9\x85\xd9\x86@1" },
93 // In 1.1.4, ENCLOSING_MARK and COMBINING_SPACING_MARK were added, and
94 // code to ignore several zero-width space characters was added.
95 { "\xe1\x80\x9d\xe1\x80\xae\xe2\x80\x8b\xe1\x80\x80\xe1\x80\xae\xe2\x80\x8b\xe1\x80\x95\xe1\x80\xad\xe2\x80\x8b\xe1\x80\x9e\xe1\x80\xaf\xe1\x80\xb6\xe1\x80\xb8\xe2\x80\x8b\xe1\x80\x85\xe1\x80\xbd\xe1\x80\xb2\xe2\x80\x8b\xe1\x80\x9e\xe1\x80\xb0\xe2\x80\x8b\xe1\x80\x99\xe1\x80\xbb\xe1\x80\xac\xe1\x80\xb8\xe1\x80\x80", "Z\xe1\x80\x9d\xe1\x80\xae\xe1\x80\x80\xe1\x80\xae\xe1\x80\x95\xe1\x80\xad\xe1\x80\x9e\xe1\x80\xaf\xe1\x80\xb6\xe1\x80\xb8\xe1\x80\x85\xe1\x80\xbd\xe1\x80\xb2\xe1\x80\x9e\xe1\x80\xb0\xe1\x80\x99\xe1\x80\xbb\xe1\x80\xac\xe1\x80\xb8\xe1\x80\x80@1" },
96 { "unmatched\"", "unmatched@1" },
97 { "unmatched \" \" ", "Zunmatch@1" },
98 { "hyphen-ated\" ", "(hyphen@1 PHRASE 2 ated@2)" },
99 { "hyphen-ated\" \"", "(hyphen@1 PHRASE 2 ated@2)" },
100 { "\"1.4\"", "1.4@1" },
101 { "\"1.\"", "1@1" },
102 { "\"A#.B.\"", "(a#@1 PHRASE 2 b@2)" },
103 { "\" Xapian QueryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
104 { "\" xapian queryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
105 { "h\xc3\xb6hle", "Zh\xc3\xb6hle@1" },
106 { "one +two three", "(Ztwo@2 AND_MAYBE (Zone@1 OR Zthree@3))" },
107 { "subject:test other", "(ZXTtest@1 OR Zother@2)" },
108 { "subject:\"space flight\"", "(XTspace@1 PHRASE 2 XTflight@2)" },
109 { "author:(twain OR poe) OR flight", "((ZAtwain@1 OR ZApoe@2) OR Zflight@3)" },
110 { "author:(twain OR title:pit OR poe)", "((ZAtwain@1 OR ZXTpit@2) OR ZApoe@3)" },
111 { "title:2001 title:space", "(XT2001@1 OR ZXTspace@2)" },
112 { "(title:help)", "ZXThelp@1" },
113 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
114 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
115 { "beer AND -lager", "(Zbeer@1 AND_NOT Zlager@2)" },
116 { "beer AND +lager", "(Zbeer@1 AND Zlager@2)" },
117 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
118 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
119 { "A OR B AND -C", "(a@1 OR (b@2 AND_NOT c@3))" },
120 { "A OR B AND +C", "(a@1 OR (b@2 AND c@3))" },
121 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
122 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
123 { "one AND two", "(Zone@1 AND Ztwo@2)" },
124 { "one A.N.D. two", "(Zone@1 OR and@2 OR Ztwo@3)" },
125 { "one \xc3\x81ND two", "(Zone@1 OR \xc3\xa1nd@2 OR Ztwo@3)" },
126 { "one author:AND two", "(Zone@1 OR Aand@2 OR Ztwo@3)" },
127 { "author:hyphen-ated", "(Ahyphen@1 PHRASE 2 Aated@2)" },
128 { "cvs site:xapian.org", "(Zcvs@1 FILTER Hxapian.org)" },
129 { "cvs -site:xapian.org", "(Zcvs@1 AND_NOT Hxapian.org)" },
130 { "foo -site:xapian.org bar", "((Zfoo@1 OR Zbar@2) AND_NOT Hxapian.org)" },
131 { "site:xapian.org mail", "(Zmail@1 FILTER Hxapian.org)" },
132 { "-site:xapian.org mail", "(Zmail@1 AND_NOT Hxapian.org)" },
133 { "mail AND -site:xapian.org", "(Zmail@1 AND_NOT 0 * Hxapian.org)" },
134 { "-Wredundant-decls", "(wredundant@1 PHRASE 2 decls@2)" },
135 { "site:xapian.org", "0 * Hxapian.org" },
136 { "mug +site:xapian.org -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT Hcvs.xapian.org)" },
137 { "mug -site:cvs.xapian.org +site:xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT Hcvs.xapian.org)" },
138 { "mug +site:xapian.org AND -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT 0 * Hcvs.xapian.org)" },
139 { "mug site:xapian.org AND -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT 0 * Hcvs.xapian.org)" },
140 { "mug site:xapian.org AND +site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND 0 * Hcvs.xapian.org)" },
141 { "NOT windows", "Syntax: <expression> NOT <expression>" },
142 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
143 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
144 { "AND -windows", "Syntax: <expression> AND <expression>" },
145 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
146 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
147 { "gordian AND -", "Syntax: <expression> AND <expression>" },
148 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
149 { "OR foo", "Syntax: <expression> OR <expression>" },
150 { "XOR", "Syntax: <expression> XOR <expression>" },
151 { "hard\xa0space", "(Zhard@1 OR Zspace@2)" },
152 { " white\r\nspace\ttest ", "(Zwhite@1 OR Zspace@2 OR Ztest@3)" },
153 { "one AND two three", "(Zone@1 AND (Ztwo@2 OR Zthree@3))" },
154 { "one two AND three", "((Zone@1 OR Ztwo@2) AND Zthree@3)" },
155 { "one AND two/three", "(Zone@1 AND (two@2 PHRASE 2 three@3))" },
156 { "one AND /two/three", "(Zone@1 AND (two@2 PHRASE 2 three@3))" },
157 { "one AND/two/three", "(Zone@1 AND (two@2 PHRASE 2 three@3))" },
158 { "one +/two/three", "((two@2 PHRASE 2 three@3) AND_MAYBE Zone@1)" },
159 { "one//two", "(one@1 PHRASE 2 two@2)" },
160 { "\"missing quote", "(missing@1 PHRASE 2 quote@2)" },
161 { "DVD+RW", "(dvd@1 OR rw@2)" }, // Would a phrase be better?
162 { "+\"must have\" optional", "((must@1 PHRASE 2 have@2) AND_MAYBE Zoption@3)" },
163 { "one NEAR two NEAR three", "(one@1 NEAR 12 two@2 NEAR 12 three@3)" },
164 { "something NEAR/3 else", "(something@1 NEAR 4 else@2)" },
165 { "a NEAR/6 b NEAR c", "(a@1 NEAR 8 b@2 NEAR 8 c@3)" },
166 { "something ADJ else", "(something@1 PHRASE 11 else@2)" },
167 { "something ADJ/3 else", "(something@1 PHRASE 4 else@2)" },
168 { "a ADJ/6 b ADJ c", "(a@1 PHRASE 8 b@2 PHRASE 8 c@3)" },
169 // Regression test - Unicode character values were truncated to 8 bits
170 // before testing C_isdigit(), so this rather artificial example parsed
171 // to: (a@1 NEAR 262 b@2)
172 { "a NEAR/\xc4\xb5 b", "((Za@1 OR (near@2 PHRASE 2 \xc4\xb5@3)) OR Zb@4)" },
173 { "a ADJ/\xc4\xb5 b", "((Za@1 OR (adj@2 PHRASE 2 \xc4\xb5@3)) OR Zb@4)" },
174 // Regression test - the first two cases were parsed as if the '/' were a
175 // space, which was inconsistent with the second two. Fixed in 1.2.5.
176 { "a NEAR/b", "(Za@1 OR (near@2 PHRASE 2 b@3))" },
177 { "a ADJ/b", "(Za@1 OR (adj@2 PHRASE 2 b@3))" },
178 { "a NEAR/b c", "((Za@1 OR (near@2 PHRASE 2 b@3)) OR Zc@4)" },
179 { "a ADJ/b c", "((Za@1 OR (adj@2 PHRASE 2 b@3)) OR Zc@4)" },
180 // Regression tests - + and - didn't work on bracketed subexpressions prior
181 // to 1.0.2.
182 { "+(one two) three", "((Zone@1 OR Ztwo@2) AND_MAYBE Zthree@3)" },
183 { "zero -(one two)", "(Zzero@1 AND_NOT (Zone@2 OR Ztwo@3))" },
184 // Feature tests that ':' is inserted between prefix and term correctly:
185 { "category:Foo", "0 * XCAT:Foo" },
186 { "category:foo", "0 * XCATfoo" },
187 { "category:\xc3\x96oo", "0 * XCAT\xc3\x96oo" },
188 // Feature tests for quoted boolean terms:
189 { "category:\"Hello world\"", "0 * XCAT:Hello world" },
190 { "category:\"literal \"\"\"", "0 * XCATliteral \"" },
191 { "category:\" \"", "0 * XCAT " },
192 { "category:\"\"", "0 * XCAT" },
193 { "category:\"(unterminated)", "0 * XCAT(unterminated)" },
194 // Feature tests for curly double quotes:
195 { "“curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
196 // Feature tests for implicitly closing brackets:
197 { "(foo", "Zfoo@1" },
198 { "(foo XOR bar", "(Zfoo@1 XOR Zbar@2)" },
199 { "(foo XOR (bar AND baz)", "(Zfoo@1 XOR (Zbar@2 AND Zbaz@3))" },
200 { "(foo XOR (bar AND baz", "(Zfoo@1 XOR (Zbar@2 AND Zbaz@3))" },
201 // Slightly arbitrarily we accept mismatched quotes.
202 { "\"curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
203 { "“curly quotes\"", "(curly@1 PHRASE 2 quotes@2)" },
204 { "“curly quotes“", "(curly@1 PHRASE 2 quotes@2)" },
205 { "”curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
206 { "author:“orwell” title:“animal\"", "(Aorwell@1 OR XTanimal@2)" },
207 { "author:\"orwell” title:“animal”", "(Aorwell@1 OR XTanimal@2)" },
208 { "author:“milne, a.a.”", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
209 { "author:“milne, a.a.\"", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
210 { "author:\"milne a.a.”", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
211 { "“hello world” +python", "(Zpython@3 AND_MAYBE (hello@1 PHRASE 2 world@2))" },
212 { "unmatched“", "Zunmatch@1" },
213 { "unmatched”", "Zunmatch@1" },
214 { "unmatched “ ” ", "Zunmatch@1" },
215 { "unmatched \" ” ", "Zunmatch@1" },
216 { "unmatched “ \" ", "Zunmatch@1" },
217 { "hyphen-ated“ ", "(hyphen@1 PHRASE 2 ated@2)" },
218 { "hyphen-ated” ", "(hyphen@1 PHRASE 2 ated@2)" },
219 { "hyphen-ated“ ”", "(hyphen@1 PHRASE 2 ated@2)" },
220 { "hyphen-ated“ \"", "(hyphen@1 PHRASE 2 ated@2)" },
221 { "hyphen-ated\" ”", "(hyphen@1 PHRASE 2 ated@2)" },
222 { "“1.4”", "1.4@1" },
223 { "“1.\"", "1@1" },
224 { "\"A#.B.”", "(a#@1 PHRASE 2 b@2)" },
225 { "“ Xapian QueryParser” parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
226 { "“ xapian queryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
227 { "beer NOT “orange juice”", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
228 { "“missing quote", "(missing@1 PHRASE 2 quote@2)" },
229 { "+“must have” optional", "((must@1 PHRASE 2 have@2) AND_MAYBE Zoption@3)" },
230 { "category:“Hello world”", "0 * XCAT:Hello world" },
231 { "category:“literal \"\"”", "0 * XCATliteral \"" },
232 { "category:“ ”", "0 * XCAT " },
233 { "category:\" \"", "0 * XCAT ”" },
234 { "category:\" ”", "0 * XCAT ”" },
235 { "category:“ \"", "0 * XCAT " },
236 { "category:“”", "0 * XCAT" },
237 { "category:\"\"", "0 * XCAT”" },
238 { "category:\"”", "0 * XCAT”" },
239 { "category:“\"", "0 * XCAT" },
240 { "category:“(unterminated)", "0 * XCAT(unterminated)" },
241 // Real world examples from tweakers.net:
242 { "Call to undefined function: imagecreate()", "(call@1 OR Zto@2 OR Zundefin@3 OR Zfunction@4 OR imagecreate@5)" },
243 { "mysql_fetch_row(): supplied argument is not a valid MySQL result resource", "(mysql_fetch_row@1 OR (Zsuppli@2 OR Zargument@3 OR Zis@4 OR Znot@5 OR Za@6 OR Zvalid@7 OR mysql@8 OR Zresult@9 OR Zresourc@10))" },
244 { "php date() nedelands", "((Zphp@1 OR date@2) OR Znedeland@3)" },
245 { "wget domein --http-user", "((Zwget@1 OR Zdomein@2) OR (http@3 PHRASE 2 user@4))" },
246 { "@home problemen", "(Zhome@1 OR Zproblemen@2)" },
247 { "'ipacsum'", "Zipacsum@1" },
248 { "canal + ", "Zcanal@1" },
249 { "/var/run/mysqld/mysqld.sock", "(var@1 PHRASE 5 run@2 PHRASE 5 mysqld@3 PHRASE 5 mysqld@4 PHRASE 5 sock@5)" },
250 { "\"QSI-161 drivers\"", "(qsi@1 PHRASE 3 161@2 PHRASE 3 drivers@3)" },
251 { "\"e-cube\" barebone", "((e@1 PHRASE 2 cube@2) OR Zbarebon@3)" },
252 { "\"./httpd: symbol not found: dlopen\"", "(httpd@1 PHRASE 5 symbol@2 PHRASE 5 not@3 PHRASE 5 found@4 PHRASE 5 dlopen@5)" },
253 { "ERROR 2003: Can't connect to MySQL server on 'localhost' (10061)", "(((error@1 OR 2003@2 OR can't@3 OR Zconnect@4 OR Zto@5 OR mysql@6 OR Zserver@7 OR Zon@8) OR Zlocalhost@9) OR 10061@10)" },
254 { "location.href = \"\"", "(location@1 PHRASE 2 href@2)" },
255 { "method=\"post\" action=\"\">", "((method@1 OR post@2) OR action@3)" },
256 { "behuizing 19\" inch", "((Zbehuiz@1 OR 19@2) OR inch@3)" },
257 { "19\" rack", "(19@1 OR rack@2)" },
258 { "3,5\" mainboard", "(3,5@1 OR mainboard@2)" },
259 { "553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)", "(((553@1 OR Zsorri@2) OR (Zthat@3 OR Zdomain@4 OR Zisn't@5 OR Zin@6 OR Zmy@7 OR Zlist@8 OR Zof@9 OR Zallow@10 OR Zrcpthost@11)) OR 5.7.1@12)" },
260 { "data error (clic redundancy check)", "((Zdata@1 OR Zerror@2) OR (Zclic@3 OR Zredund@4 OR Zcheck@5))" },
261 { "? mediaplayer 9\"", "(Zmediaplay@1 OR 9@2)" },
262 { "date(\"w\")", "(date@1 OR w@2)" },
263 { "Syntaxisfout (operator ontbreekt ASP", "(syntaxisfout@1 OR (Zoper@2 OR Zontbreekt@3 OR asp@4))" },
264 { "Request.ServerVariables(\"logon_user\")", "((request@1 PHRASE 2 servervariables@2) OR logon_user@3)" },
265 { "ASP \"request.form\" van \\\"enctype=\"MULTIPART/FORM-DATA\"\\\"", "((((asp@1 OR (request@2 PHRASE 2 form@3)) OR Zvan@4) OR enctype@5) OR (multipart@6 PHRASE 3 form@7 PHRASE 3 data@8))" },
266 { "USER ftp (Login failed): Invalid shell: /sbin/nologin", "((((user@1 OR Zftp@2) OR (login@3 OR Zfail@4)) OR (invalid@5 OR Zshell@6)) OR (sbin@7 PHRASE 2 nologin@8))" },
267 { "ip_masq_new(proto=TCP)", "((ip_masq_new@1 OR proto@2) OR tcp@3)" },
268 { "\"document.write(\"", "(document@1 PHRASE 2 write@2)" },
269 { "ERROR 1045: Access denied for user: 'root@localhost' (Using password: NO)", "(((error@1 OR 1045@2 OR access@3 OR Zdeni@4 OR Zfor@5 OR Zuser@6) OR (root@7 PHRASE 2 localhost@8)) OR (using@9 OR Zpassword@10 OR no@11))" },
270 { "TIP !! subtitles op TV-out (via DVD max g400)", "(((tip@1 OR (Zsubtitl@2 OR Zop@3)) OR (tv@4 PHRASE 2 out@5)) OR (Zvia@6 OR dvd@7 OR Zmax@8 OR Zg400@9))" },
271 { "Gigabyte 8PE667 (de Ultra versie) of Asus A7N8X Deluxe", "(((gigabyte@1 OR 8pe667@2) OR (Zde@3 OR ultra@4 OR Zversi@5)) OR (Zof@6 OR asus@7 OR a7n8x@8 OR deluxe@9))" },
272 { "\"1) Ze testen 8x AF op de GFFX tegen \"", "(1@1 PHRASE 9 ze@2 PHRASE 9 testen@3 PHRASE 9 8x@4 PHRASE 9 af@5 PHRASE 9 op@6 PHRASE 9 de@7 PHRASE 9 gffx@8 PHRASE 9 tegen@9)" },
273 { "\") Ze houden geen rekening met de kwaliteit van AF. Als ze dat gedaan hadden dan waren ze tot de conclusie gekomen dat Performance AF (dus Bilinear AF) op de 9700Pro goed te vergelijken is met Balanced AF op de GFFX. En dan hadden ze ook gezien dat de GFFX niet kan tippen aan de Quality AF van de 9700Pro.\"", "(ze@1 PHRASE 59 houden@2 PHRASE 59 geen@3 PHRASE 59 rekening@4 PHRASE 59 met@5 PHRASE 59 de@6 PHRASE 59 kwaliteit@7 PHRASE 59 van@8 PHRASE 59 af@9 PHRASE 59 als@10 PHRASE 59 ze@11 PHRASE 59 dat@12 PHRASE 59 gedaan@13 PHRASE 59 hadden@14 PHRASE 59 dan@15 PHRASE 59 waren@16 PHRASE 59 ze@17 PHRASE 59 tot@18 PHRASE 59 de@19 PHRASE 59 conclusie@20 PHRASE 59 gekomen@21 PHRASE 59 dat@22 PHRASE 59 performance@23 PHRASE 59 af@24 PHRASE 59 dus@25 PHRASE 59 bilinear@26 PHRASE 59 af@27 PHRASE 59 op@28 PHRASE 59 de@29 PHRASE 59 9700pro@30 PHRASE 59 goed@31 PHRASE 59 te@32 PHRASE 59 vergelijken@33 PHRASE 59 is@34 PHRASE 59 met@35 PHRASE 59 balanced@36 PHRASE 59 af@37 PHRASE 59 op@38 PHRASE 59 de@39 PHRASE 59 gffx@40 PHRASE 59 en@41 PHRASE 59 dan@42 PHRASE 59 hadden@43 PHRASE 59 ze@44 PHRASE 59 ook@45 PHRASE 59 gezien@46 PHRASE 59 dat@47 PHRASE 59 de@48 PHRASE 59 gffx@49 PHRASE 59 niet@50 PHRASE 59 kan@51 PHRASE 59 tippen@52 PHRASE 59 aan@53 PHRASE 59 de@54 PHRASE 59 quality@55 PHRASE 59 af@56 PHRASE 59 van@57 PHRASE 59 de@58 PHRASE 59 9700pro@59)" },
274 { "\"Ze houden geen rekening met de kwaliteit van AF. Als ze dat gedaan hadden dan waren ze tot de conclusie gekomen dat Performance AF (dus Bilinear AF) op de 9700Pro goed te vergelijken is met Balanced AF op de GFFX. En dan hadden ze ook gezien dat de GFFX niet kan tippen aan de Quality AF van de 9700Pro.\"", "(ze@1 PHRASE 59 houden@2 PHRASE 59 geen@3 PHRASE 59 rekening@4 PHRASE 59 met@5 PHRASE 59 de@6 PHRASE 59 kwaliteit@7 PHRASE 59 van@8 PHRASE 59 af@9 PHRASE 59 als@10 PHRASE 59 ze@11 PHRASE 59 dat@12 PHRASE 59 gedaan@13 PHRASE 59 hadden@14 PHRASE 59 dan@15 PHRASE 59 waren@16 PHRASE 59 ze@17 PHRASE 59 tot@18 PHRASE 59 de@19 PHRASE 59 conclusie@20 PHRASE 59 gekomen@21 PHRASE 59 dat@22 PHRASE 59 performance@23 PHRASE 59 af@24 PHRASE 59 dus@25 PHRASE 59 bilinear@26 PHRASE 59 af@27 PHRASE 59 op@28 PHRASE 59 de@29 PHRASE 59 9700pro@30 PHRASE 59 goed@31 PHRASE 59 te@32 PHRASE 59 vergelijken@33 PHRASE 59 is@34 PHRASE 59 met@35 PHRASE 59 balanced@36 PHRASE 59 af@37 PHRASE 59 op@38 PHRASE 59 de@39 PHRASE 59 gffx@40 PHRASE 59 en@41 PHRASE 59 dan@42 PHRASE 59 hadden@43 PHRASE 59 ze@44 PHRASE 59 ook@45 PHRASE 59 gezien@46 PHRASE 59 dat@47 PHRASE 59 de@48 PHRASE 59 gffx@49 PHRASE 59 niet@50 PHRASE 59 kan@51 PHRASE 59 tippen@52 PHRASE 59 aan@53 PHRASE 59 de@54 PHRASE 59 quality@55 PHRASE 59 af@56 PHRASE 59 van@57 PHRASE 59 de@58 PHRASE 59 9700pro@59)" },
275 { "$structure = imap_header($mbox, $tt);", "(((Zstructur@1 OR imap_header@2) OR Zmbox@3) OR Ztt@4)" },
276 { "\"ifup: Could not get a valid interface name: -> skipped\"", "(ifup@1 PHRASE 9 could@2 PHRASE 9 not@3 PHRASE 9 get@4 PHRASE 9 a@5 PHRASE 9 valid@6 PHRASE 9 interface@7 PHRASE 9 name@8 PHRASE 9 skipped@9)" },
277 { "Er kan geen combinatie van filters worden gevonden om de gegevensstroom te genereren. (Error=80040218)", "((er@1 OR Zkan@2 OR Zgeen@3 OR Zcombinati@4 OR Zvan@5 OR Zfilter@6 OR Zworden@7 OR Zgevonden@8 OR Zom@9 OR Zde@10 OR Zgegevensstroom@11 OR Zte@12 OR Zgenereren@13) OR (error@14 OR 80040218@15))" },
278 { "ereg_replace(\"\\\\\",\"\\/\"", "ereg_replace@1" },
279 { "\\\\\"divx+geen+geluid\\\\\"", "(divx@1 PHRASE 3 geen@2 PHRASE 3 geluid@3)" },
280 { "lcase(\"string\")", "(lcase@1 OR string@2)" },
281 { "isEmpty( ) functie in visual basic", "(isempty@1 OR (Zfuncti@2 OR Zin@3 OR Zvisual@4 OR Zbasic@5))" },
282 { "*** stop: 0x0000001E (0xC0000005,0x00000000,0x00000000,0x00000000)", "((Zstop@1 OR 0x0000001e@2) OR 0xc0000005,0x00000000,0x00000000,0x00000000@3)" },
283 { "\"ctrl+v+c+a fout\"", "(ctrl@1 PHRASE 5 v@2 PHRASE 5 c@3 PHRASE 5 a@4 PHRASE 5 fout@5)" },
284 { "Server.CreateObject(\"ADODB.connection\")", "((server@1 PHRASE 2 createobject@2) OR (adodb@3 PHRASE 2 connection@4))" },
285 { "Presario 6277EA-XP model P4/28 GHz-120GB-DVD-CDRW (512MBWXP) (470048-012)", "((((((presario@1 OR (6277ea@2 PHRASE 2 xp@3)) OR Zmodel@4) OR (p4@5 PHRASE 2 28@6)) OR (ghz@7 PHRASE 4 120gb@8 PHRASE 4 dvd@9 PHRASE 4 cdrw@10)) OR 512mbwxp@11) OR (470048@12 PHRASE 2 012@13))" },
286 { "Failed to connect agent. (AGENT=dbaxchg2, EC=UserId =NUll)", "((failed@1 OR Zto@2 OR Zconnect@3 OR Zagent@4) OR ((((agent@5 OR Zdbaxchg2@6) OR ec@7) OR userid@8) OR null@9))" },
287 { "delphi CreateOleObject(\"MSXML2.DomDocument\")", "((Zdelphi@1 OR createoleobject@2) OR (msxml2@3 PHRASE 2 domdocument@4))" },
288 { "Unhandled exeption in IEXPLORE.EXE (FTAPP.DLL)", "(((unhandled@1 OR Zexept@2 OR Zin@3) OR (iexplore@4 PHRASE 2 exe@5)) OR (ftapp@6 PHRASE 2 dll@7))" },
289 { "IBM High Rate Wireless LAN PCI Adapter (Low Profile Enabled)", "((ibm@1 OR high@2 OR rate@3 OR wireless@4 OR lan@5 OR pci@6 OR adapter@7) OR (low@8 OR profile@9 OR enabled@10))" },
290 { "asp ' en \"", "(Zasp@1 OR Zen@2)" },
291 { "Hercules 3D Prophet 8500 LE 64MB (OEM, Radeon 8500 LE)", "((hercules@1 OR 3d@2 OR prophet@3 OR 8500@4 OR le@5 OR 64mb@6) OR (oem@7 OR (radeon@8 OR 8500@9 OR le@10)))" },
292 { "session_set_cookie_params(echo \"hoi\")", "((session_set_cookie_params@1 OR Zecho@2) OR hoi@3)" },
293 { "windows update werkt niet (windows se", "((Zwindow@1 OR Zupdat@2 OR Zwerkt@3 OR Zniet@4) OR (Zwindow@5 OR Zse@6))" },
294 { "De statuscode van de fout is ( 0 x 4 , 0 , 0 , 0 )", "((de@1 OR Zstatuscod@2 OR Zvan@3 OR Zde@4 OR Zfout@5 OR Zis@6) OR ((((0@7 OR Zx@8 OR 4@9) OR 0@10) OR 0@11) OR 0@12))" },
295 { "sony +(u20 u-20)", "((Zu20@2 OR (u@3 PHRASE 2 20@4)) AND_MAYBE Zsoni@1)" },
296 { "[crit] (17)File exists: unable to create scoreboard (name-based shared memory failure)", "(((Zcrit@1 OR 17@2) OR (file@3 OR Zexist@4 OR Zunabl@5 OR Zto@6 OR Zcreat@7 OR Zscoreboard@8)) OR ((name@9 PHRASE 2 based@10) OR (Zshare@11 OR Zmemori@12 OR Zfailur@13)))" },
297 { "directories lokaal php (uitlezen OR inladen)", "((Zdirectori@1 OR Zlokaal@2 OR Zphp@3) OR (Zuitlezen@4 OR Zinladen@5))" },
298 { "(multi pc modem)+ (line sync)", "((Zmulti@1 OR Zpc@2 OR Zmodem@3) OR (Zline@4 OR Zsync@5))" },
299 { "xp 5.1.2600.0 (xpclient.010817-1148)", "((Zxp@1 OR 5.1.2600.0@2) OR (xpclient@3 PHRASE 3 010817@4 PHRASE 3 1148@5))" },
300 { "DirectDraw test results: Failure at step 5 (User verification of rectangles): HRESULT = 0x00000000 (error code) Direct3D 7 test results: Failure at step 32 (User verification of Direct3D rendering): HRESULT = 0x00000000 (error code) Direct3D 8 test results: Failure at step 32 (User verification of Direct3D rendering): HRESULT = 0x00000000 (error code) Direct3D 9 test results: Failure at step 32 (User verification of Direct3D rendering): HRESULT = 0x00000000 (error code)", "((((((((((((((((((((directdraw@1 OR Ztest@2 OR Zresult@3 OR failure@4 OR Zat@5 OR Zstep@6 OR 5@7) OR (user@8 OR Zverif@9 OR Zof@10 OR Zrectangl@11)) OR hresult@12) OR 0x00000000@13) OR (Zerror@14 OR Zcode@15)) OR (direct3d@16 OR 7@17 OR Ztest@18 OR Zresult@19 OR failure@20 OR Zat@21 OR Zstep@22 OR 32@23)) OR (user@24 OR Zverif@25 OR Zof@26 OR direct3d@27 OR Zrender@28)) OR hresult@29) OR 0x00000000@30) OR (Zerror@31 OR Zcode@32)) OR (direct3d@33 OR 8@34 OR Ztest@35 OR Zresult@36 OR failure@37 OR Zat@38 OR Zstep@39 OR 32@40)) OR (user@41 OR Zverif@42 OR Zof@43 OR direct3d@44 OR Zrender@45)) OR hresult@46) OR 0x00000000@47) OR (Zerror@48 OR Zcode@49)) OR (direct3d@50 OR 9@51 OR Ztest@52 OR Zresult@53 OR failure@54 OR Zat@55 OR Zstep@56 OR 32@57)) OR (user@58 OR Zverif@59 OR Zof@60 OR direct3d@61 OR Zrender@62)) OR hresult@63) OR 0x00000000@64) OR (Zerror@65 OR Zcode@66))" },
301 { "Thermaltake Aquarius II waterkoeling (kompleet voor P4 en XP)", "((thermaltake@1 OR aquarius@2 OR ii@3 OR Zwaterkoel@4) OR (Zkompleet@5 OR Zvoor@6 OR p4@7 OR Zen@8 OR xp@9))" },
302 { "E3501 unable to add job to database (EC=-2005)", "((e3501@1 OR Zunabl@2 OR Zto@3 OR Zadd@4 OR Zjob@5 OR Zto@6 OR Zdatabas@7) OR (ec@8 OR 2005@9))" },
303 { "\"arp -s\" ip veranderen", "((arp@1 PHRASE 2 s@2) OR (Zip@3 OR Zveranderen@4))" },
304 { "header(\"content-type: application/octet-stream\");", "((header@1 OR (content@2 PHRASE 2 type@3)) OR (application@4 PHRASE 3 octet@5 PHRASE 3 stream@6))" },
305 { "$datum = date(\"d-m-Y\");", "((Zdatum@1 OR date@2) OR (d@3 PHRASE 3 m@4 PHRASE 3 y@5))" },
306 { "\"'\" +asp", "Zasp@1" },
307 { "+session +[", "Zsession@1" },
308 { "Dit apparaat kan niet starten. (Code 10)", "((dit@1 OR Zapparaat@2 OR Zkan@3 OR Zniet@4 OR Zstarten@5) OR (code@6 OR 10@7))" },
309 { "\"You cannot use the Administration program while the Domino Server is running. Either shut down the Domino Server (but keep the file server running) or choose the ican labeled 'Lotus Notes' instead.\"", "(you@1 PHRASE 32 cannot@2 PHRASE 32 use@3 PHRASE 32 the@4 PHRASE 32 administration@5 PHRASE 32 program@6 PHRASE 32 while@7 PHRASE 32 the@8 PHRASE 32 domino@9 PHRASE 32 server@10 PHRASE 32 is@11 PHRASE 32 running@12 PHRASE 32 either@13 PHRASE 32 shut@14 PHRASE 32 down@15 PHRASE 32 the@16 PHRASE 32 domino@17 PHRASE 32 server@18 PHRASE 32 but@19 PHRASE 32 keep@20 PHRASE 32 the@21 PHRASE 32 file@22 PHRASE 32 server@23 PHRASE 32 running@24 PHRASE 32 or@25 PHRASE 32 choose@26 PHRASE 32 the@27 PHRASE 32 ican@28 PHRASE 32 labeled@29 PHRASE 32 lotus@30 PHRASE 32 notes@31 PHRASE 32 instead@32)" },
310 { "\"+irq +veranderen +xp\"", "(irq@1 PHRASE 3 veranderen@2 PHRASE 3 xp@3)" },
311 { "\"is not a member of 'operator``global namespace''' + c++", "(is@1 PHRASE 9 not@2 PHRASE 9 a@3 PHRASE 9 member@4 PHRASE 9 of@5 PHRASE 9 operator@6 PHRASE 9 global@7 PHRASE 9 namespace@8 PHRASE 9 c++@9)" },
312 { "mkdir() failed (File exists) php", "(((mkdir@1 OR Zfail@2) OR (file@3 OR Zexist@4)) OR Zphp@5)" },
313 { "laatsteIndex(int n)", "(laatsteindex@1 OR (Zint@2 OR Zn@3))" },
314 { "\"line+in\" OR \"c8783\"", "((line@1 PHRASE 2 in@2) OR c8783@3)" },
315 { "if ($_POST['Submit'])", "(Zif@1 OR (_post@2 OR submit@3))" },
316 { "NEC DVD+-RW ND-1300A", "((nec@1 OR (dvd+@2 PHRASE 2 rw@3)) OR (nd@4 PHRASE 2 1300a@5))" },
317 { "*String not found* (*String not found*.)", "((string@1 OR Znot@2 OR found@3) OR (string@4 OR Znot@5 OR found@6))" },
318 { "MSI G4Ti4200-TD 128MB (GeForce4 Ti4200)", "(((msi@1 OR (g4ti4200@2 PHRASE 2 td@3)) OR 128mb@4) OR (geforce4@5 OR ti4200@6))" },
319 { "href=\"#\"", "href@1" },
320 { "Request.ServerVariables(\"REMOTE_USER\") javascript", "(((request@1 PHRASE 2 servervariables@2) OR remote_user@3) OR Zjavascript@4)" },
321 { "XF86Config(-4) waar", "((xf86config@1 OR 4@2) OR Zwaar@3)" },
322 { "Unknown (tag 2000)", "(unknown@1 OR (Ztag@2 OR 2000@3))" },
323 { "KT4V(MS-6712)", "(kt4v@1 OR (ms@2 PHRASE 2 6712@3))" },
324 { "scheduled+AND+nieuwsgroepen+AND+updaten", "((Zschedul@1 AND Znieuwsgroepen@2) AND Zupdaten@3)" },
325 { "137(netbios-ns)", "(137@1 OR (netbios@2 PHRASE 2 ns@3))" },
326 { "HARWARE ERROR, TRACKING SERVO (4:0X09:0X01)", "(((harware@1 OR error@2) OR (tracking@3 OR servo@4)) OR (4@5 PHRASE 3 0x09@6 PHRASE 3 0x01@7))" },
327 { "Chr(10) wat is code van \" teken", "(((chr@1 OR 10@2) OR (Zwat@3 OR Zis@4 OR Zcode@5 OR Zvan@6)) OR Zteken@7)" },
328 { "wat is code van \" teken", "((Zwat@1 OR Zis@2 OR Zcode@3 OR Zvan@4) OR teken@5)" },
329 { "The Jet VBA file (VBAJET.dll for 16-bit version, VBAJET32.dll version", "((the@1 OR jet@2 OR vba@3 OR Zfile@4) OR ((((((vbajet@5 PHRASE 2 dll@6) OR Zfor@7) OR (16@8 PHRASE 2 bit@9)) OR Zversion@10) OR (vbajet32@11 PHRASE 2 dll@12)) OR Zversion@13))" },
330 { "Permission denied (publickey,password,keyboard-interactive).", "((permission@1 OR Zdeni@2) OR ((Zpublickey@3 OR Zpassword@4) OR (keyboard@5 PHRASE 2 interactive@6)))" },
331 { "De lees- of schrijfbewerking (\"written\") op het geheugen is mislukt", "(((de@1 OR Zlee@2 OR Zof@3 OR Zschrijfbewerk@4) OR written@5) OR (Zop@6 OR Zhet@7 OR Zgeheugen@8 OR Zis@9 OR Zmislukt@10))" },
332 { "Primary IDE channel no 80 conductor cable installed\"", "(primary@1 OR ide@2 OR Zchannel@3 OR Zno@4 OR 80@5 OR Zconductor@6 OR Zcabl@7 OR installed@8)" },
333 { "\"2020 NEAR zoom\"", "(2020@1 PHRASE 3 near@2 PHRASE 3 zoom@3)" },
334 { "setcookie(\"naam\",\"$user\");", "((setcookie@1 OR naam@2) OR user@3)" },
335 { "MSI 645 Ultra (MS-6547) Ver1", "(((msi@1 OR 645@2 OR ultra@3) OR (ms@4 PHRASE 2 6547@5)) OR ver1@6)" },
336 { "if ($HTTP", "(Zif@1 OR http@2)" },
337 { "data error(cyclic redundancy check)", "((Zdata@1 OR error@2) OR (Zcyclic@3 OR Zredund@4 OR Zcheck@5))" },
338 { "UObject::StaticAllocateObject <- (NULL None) <- UObject::StaticConstructObject <- InitEngine", "((((uobject@1 PHRASE 2 staticallocateobject@2) OR (null@3 OR none@4)) OR (uobject@5 PHRASE 2 staticconstructobject@6)) OR initengine@7)" },
339 { "Failure at step 8 (Creating 3D Device)", "((failure@1 OR Zat@2 OR Zstep@3 OR 8@4) OR (creating@5 OR 3d@6 OR device@7))" },
340 { "Call Shell(\"notepad.exe\",", "((call@1 OR shell@2) OR (notepad@3 PHRASE 2 exe@4))" },
341 { "2.5\" harddisk converter", "(2.5@1 OR (harddisk@2 PHRASE 2 converter@3))" },
342 { "creative labs \"dvd+rw\"", "((Zcreativ@1 OR Zlab@2) OR (dvd@3 PHRASE 2 rw@4))" },
343 { "\"het beleid van deze computer staat u niet toe interactief", "(het@1 PHRASE 10 beleid@2 PHRASE 10 van@3 PHRASE 10 deze@4 PHRASE 10 computer@5 PHRASE 10 staat@6 PHRASE 10 u@7 PHRASE 10 niet@8 PHRASE 10 toe@9 PHRASE 10 interactief@10)" },
344 { "ati radeon \"driver cleaner", "((Zati@1 OR Zradeon@2) OR (driver@3 PHRASE 2 cleaner@4))" },
345 { "\"../\" path", "Zpath@1" },
346 { "(novell client) workstation only", "((Znovel@1 OR Zclient@2) OR (Zworkstat@3 OR Zonli@4))" },
347 { "Unable to find libgd.(a|so) anywhere", "((((unable@1 OR Zto@2 OR Zfind@3 OR Zlibgd@4) OR Za@5) OR Zso@6) OR Zanywher@7)" },
348 { "\"libstdc++-libc6.1-1.so.2\"", "(libstdc++@1 PHRASE 5 libc6.1@2 PHRASE 5 1@3 PHRASE 5 so@4 PHRASE 5 2@5)" },
349 { "ipsec_setup (/etc/ipsec.conf, line 1) cannot open configuration file \"/etc/ipsec.conf\" -- `' aborted", "((((Zipsec_setup@1 OR ((etc@2 PHRASE 3 ipsec@3 PHRASE 3 conf@4) OR (Zline@5 OR 1@6))) OR (Zcannot@7 OR Zopen@8 OR Zconfigur@9 OR Zfile@10)) OR (etc@11 PHRASE 3 ipsec@12 PHRASE 3 conf@13)) OR Zabort@14)" },
350 { "Forwarden van domeinnaam (naar HTTP adres)", "((forwarden@1 OR Zvan@2 OR Zdomeinnaam@3) OR (Znaar@4 OR http@5 OR Zadr@6))" },
351 { "Compaq HP, 146.8 GB (MPN-286716-B22) Hard Drives", "((((compaq@1 OR hp@2) OR (146.8@3 OR gb@4)) OR (mpn@5 PHRASE 3 286716@6 PHRASE 3 b22@7)) OR (hard@8 OR drives@9))" },
352 { "httpd (no pid file) not running", "((Zhttpd@1 OR (Zno@2 OR Zpid@3 OR Zfile@4)) OR (Znot@5 OR Zrun@6))" },
353 { "apache httpd (pid file) not running", "(((Zapach@1 OR Zhttpd@2) OR (Zpid@3 OR Zfile@4)) OR (Znot@5 OR Zrun@6))" },
354 { "Klasse is niet geregistreerd (Fout=80040154).", "((klasse@1 OR Zis@2 OR Zniet@3 OR Zgeregistreerd@4) OR (fout@5 OR 80040154@6))" },
355 { "\"dvd+r\" \"dvd-r\"", "((dvd@1 PHRASE 2 r@2) OR (dvd@3 PHRASE 2 r@4))" },
356 { "\"=\" tekens uit csvfile", "(Zteken@1 OR Zuit@2 OR Zcsvfile@3)" },
357 { "libc.so.6(GLIBC_2.3)", "((libc@1 PHRASE 3 so@2 PHRASE 3 6@3) OR glibc_2.3@4)" },
358 { "Sitecom Broadband xDSL / Cable Router 4S (DC-202)", "(((sitecom@1 OR broadband@2 OR Zxdsl@3) OR (cable@4 OR router@5 OR 4s@6)) OR (dc@7 PHRASE 2 202@8))" },
359 { "(t-mobile) bereik", "((t@1 PHRASE 2 mobile@2) OR Zbereik@3)" },
360 { "error LNK2001: unresolved external symbol \"public", "((Zerror@1 OR lnk2001@2 OR Zunresolv@3 OR Zextern@4 OR Zsymbol@5) OR public@6)" },
361 { "patch linux exploit -p)", "((Zpatch@1 OR Zlinux@2 OR Zexploit@3) OR Zp@4)" },
362 { "MYD not found (Errcode: 2)", "((myd@1 OR Znot@2 OR Zfound@3) OR (errcode@4 OR 2@5))" },
363 { "ob_start(\"ob_gzhandler\"); file download", "((ob_start@1 OR ob_gzhandler@2) OR (Zfile@3 OR Zdownload@4))" },
364 { "ECS Elitegroup K7VZA (VIA VT8363/VT8363A)", "((ecs@1 OR elitegroup@2 OR k7vza@3) OR (via@4 OR (vt8363@5 PHRASE 2 vt8363a@6)))" },
365 { "ASUS A7V8X (LAN + Serial-ATA + Firewire + Raid + Audio)", "((asus@1 OR a7v8x@2) OR ((((lan@3 OR (serial@4 PHRASE 2 ata@5)) OR firewire@6) OR raid@7) OR audio@8))" },
366 { "Javascript:history.go(-1)", "((javascript@1 PHRASE 3 history@2 PHRASE 3 go@3) OR 1@4)" },
367 { "java :) als icon", "(Zjava@1 OR (Zal@2 OR Zicon@3))" },
368 { "onmouseover=setPointer(this", "((onmouseover@1 OR setpointer@2) OR Zthis@3)" },
369 { "\" in vbscript", "(in@1 PHRASE 2 vbscript@2)" },
370 { "IRC (FAQ OR (hulp NEAR bij))", "(irc@1 OR (faq@2 OR (hulp@3 NEAR 11 bij@4)))" },
371 { "setProperty(\"McSquare\"+i, _xscale, _xscale++);", "((((setproperty@1 OR mcsquare@2) OR Zi@3) OR _xscale@4) OR _xscale++@5)" },
372 { "[warn] Apache does not support line-end comments. Consider using quotes around argument: \"#-1\"", "(((((Zwarn@1 OR (apache@2 OR Zdoe@3 OR Znot@4 OR Zsupport@5)) OR (line@6 PHRASE 2 end@7)) OR Zcomment@8) OR (consider@9 OR Zuse@10 OR Zquot@11 OR Zaround@12 OR Zargument@13)) OR 1@14)" },
373 { "(php.ini) (memory_limit)", "((php@1 PHRASE 2 ini@2) OR Zmemory_limit@3)" },
374 { "line 8: syntax error near unexpected token `kernel_thread(f'", "(((Zline@1 OR 8@2 OR Zsyntax@3 OR Zerror@4 OR Znear@5 OR Zunexpect@6 OR Ztoken@7) OR kernel_thread@8) OR Zf@9)" },
375 { "VXD NAVEX()@)", "(vxd@1 OR navex@2)" },
376 { "\"Iiyama AS4314UT 17\" \"", "(iiyama@1 PHRASE 3 as4314ut@2 PHRASE 3 17@3)" },
377 { "include (\"$id.html\");", "(Zinclud@1 OR (id@2 PHRASE 2 html@3))" },
378 { "include id.Today's date is: <? print (date (\"M d, Y\")); ?>hp", "(((((Zinclud@1 OR (id@2 PHRASE 2 today's@3)) OR (Zdate@4 OR Zis@5)) OR Zprint@6) OR (Zdate@7 OR (m@8 PHRASE 3 d@9 PHRASE 3 y@10))) OR Zhp@11)" },
379 { "(program files\\common) opstarten", "((Zprogram@1 OR (files@2 PHRASE 2 common@3)) OR Zopstarten@4)" },
380 { "java \" string", "(Zjava@1 OR string@2)" },
381 { "+=", "" },
382 { "php +=", "Zphp@1" },
383 { "[php] ereg_replace(\".\"", "(Zphp@1 OR ereg_replace@2)" },
384 { "\"echo -e\" kleur", "((echo@1 PHRASE 2 e@2) OR Zkleur@3)" },
385 { "adobe premiere \"-1\"", "((Zadob@1 OR Zpremier@2) OR 1@3)" },
386 { "DVD brander \"+\" en \"-\"", "((dvd@1 OR Zbrander@2) OR Zen@3)" },
387 { "inspirion \"dvd+R\"", "(Zinspirion@1 OR (dvd@2 PHRASE 2 r@3))" },
388 { "asp 0x80040E14)", "(Zasp@1 OR 0x80040e14@2)" },
389 { "\"e-tech motorola router", "(e@1 PHRASE 4 tech@2 PHRASE 4 motorola@3 PHRASE 4 router@4)" },
390 { "bluetooth '1.3.2.19\"", "(Zbluetooth@1 OR 1.3.2.19@2)" },
391 { "ms +-connect", "(Zms@1 OR Zconnect@2)" },
392 { "php+print+\"", "(Zphp@1 OR print+@2)" },
393 { "athlon 1400 :welke videokaart\"", "((Zathlon@1 OR 1400@2) OR (Zwelk@3 OR videokaart@4))" },
394 { "+-dvd", "Zdvd@1" },
395 { "glftpd \"-new-\"", "(Zglftpd@1 OR new@2)" },
396 { "\"scandisk + dos5.0", "(scandisk@1 PHRASE 2 dos5.0@2)" },
397 { "socket\\(\\)", "socket@1" },
398 { "msn (e-tech) router", "((Zmsn@1 OR (e@2 PHRASE 2 tech@3)) OR Zrouter@4)" },
399 { "Het grote Epox 8k3a+ ervaring/prob topic\"", "(((het@1 OR Zgrote@2 OR epox@3 OR 8k3a+@4) OR (ervaring@5 PHRASE 2 prob@6)) OR topic@7)" },
400 { "\"CF+bluetooth\"", "(cf@1 PHRASE 2 bluetooth@2)" },
401 { "kwaliteit (s-video) composite verschil tv out", "((Zkwaliteit@1 OR (s@2 PHRASE 2 video@3)) OR (Zcomposit@4 OR Zverschil@5 OR Ztv@6 OR Zout@7))" },
402 { "Wie kan deze oude hardware nog gebruiken\" Deel", "((wie@1 OR Zkan@2 OR Zdeze@3 OR Zoud@4 OR Zhardwar@5 OR Znog@6 OR gebruiken@7) OR deel@8)" },
403 { "Public Declare Sub Sleep Lib \"kernel32\" (ByVal dwMilliseconds As Long)", "(((public@1 OR declare@2 OR sub@3 OR sleep@4 OR lib@5) OR kernel32@6) OR (byval@7 OR Zdwmillisecond@8 OR as@9 OR long@10))" },
404 { "for inclusion (include_path='.:/usr/share/php')", "((Zfor@1 OR Zinclus@2) OR (include_path@3 OR (usr@4 PHRASE 3 share@5 PHRASE 3 php@6)))" },
405 { "\"muziek 2x zo snel\"\"", "(muziek@1 PHRASE 4 2x@2 PHRASE 4 zo@3 PHRASE 4 snel@4)" },
406 { "execCommand('inserthorizontalrule'", "(execcommand@1 OR Zinserthorizontalrul@2)" },
407 { "specs: IBM PS/2, Intel 8086 @ 25 mhz!!, 2 mb intern, 50 mb hd, 5.5\" floppy drive, toetsenbord en geen muis", "((((((((Zspec@1 OR ibm@2) OR (ps@3 PHRASE 2 2@4)) OR (intel@5 OR 8086@6)) OR (25@7 OR Zmhz@8)) OR (2@9 OR Zmb@10 OR Zintern@11)) OR (50@12 OR Zmb@13 OR Zhd@14)) OR 5.5@15) OR (floppy@16 PHRASE 6 drive@17 PHRASE 6 toetsenbord@18 PHRASE 6 en@19 PHRASE 6 geen@20 PHRASE 6 muis@21))" },
408 { "History: GetEventTool <- GetMusicManager <- GetMusicScript <- DMCallRoutine <- AMusicScriptEvent::execCallRoutine <- UObject::execClassContext <- (U2GameInfo M08A1.U2GameInfo0 @ Function U2.U2GameInfo.NotifyLevelChangeEnd : 0075 line 744) <- UObject::ProcessEvent <- (U2GameInfo M08A1.U2GameInfo0, Function U2.U2GameInfo.NotifyLevelChangeEnd) <- UGameEngine::LoadMap <- LocalMapURL <- UGameEngine::Browse <- ServerTravel <- UGameEngine::Tick <- UpdateWorld <- MainLoop", "((((((((((((((((history@1 OR geteventtool@2) OR getmusicmanager@3) OR getmusicscript@4) OR dmcallroutine@5) OR (amusicscriptevent@6 PHRASE 2 execcallroutine@7)) OR (uobject@8 PHRASE 2 execclasscontext@9)) OR ((((u2gameinfo@10 OR (m08a1@11 PHRASE 2 u2gameinfo0@12)) OR function@13) OR (u2@14 PHRASE 3 u2gameinfo@15 PHRASE 3 notifylevelchangeend@16)) OR (0075@17 OR Zline@18 OR 744@19))) OR (uobject@20 PHRASE 2 processevent@21)) OR (((u2gameinfo@22 OR (m08a1@23 PHRASE 2 u2gameinfo0@24)) OR function@25) OR (u2@26 PHRASE 3 u2gameinfo@27 PHRASE 3 notifylevelchangeend@28))) OR (ugameengine@29 PHRASE 2 loadmap@30)) OR localmapurl@31) OR (ugameengine@32 PHRASE 2 browse@33)) OR servertravel@34) OR (ugameengine@35 PHRASE 2 tick@36)) OR updateworld@37) OR mainloop@38)" },
409 { "Support AMD XP 2400+ & 2600+ (K7T Turbo2 only)", "(((support@1 OR amd@2 OR xp@3 OR 2400+@4) OR 2600+@5) OR (k7t@6 OR turbo2@7 OR Zonli@8))" },
410 { "'\"><br>bla</br>", "(br@1 PHRASE 3 bla@2 PHRASE 3 br@3)" },
411 { "The instruction at \"0x30053409\" referenced memory at \"0x06460504\". The memory could not be \"read'. Click OK to terminate the application.", "((((((the@1 OR Zinstruct@2 OR Zat@3) OR 0x30053409@4) OR (Zreferenc@5 OR Zmemori@6 OR Zat@7)) OR 0x06460504@8) OR (the@9 OR Zmemori@10 OR Zcould@11 OR Znot@12 OR Zbe@13)) OR (read@14 PHRASE 7 click@15 PHRASE 7 ok@16 PHRASE 7 to@17 PHRASE 7 terminate@18 PHRASE 7 the@19 PHRASE 7 application@20))" },
412 { "\"(P5A-b)\"", "(p5a@1 PHRASE 2 b@2)" },
413 { "(13,5 > 13) == no-go!", "((13,5@1 OR 13@2) OR (no@3 PHRASE 2 go@4))" },
414 { "eth not found \"ifconfig -a\"", "((Zeth@1 OR Znot@2 OR Zfound@3) OR (ifconfig@4 PHRASE 2 a@5))" },
415 { "<META NAME=\"ROBOTS", "((meta@1 OR name@2) OR robots@3)" },
416 { "lp0: using parport0 (interrupt-driven)", "((Zlp0@1 OR (Zuse@2 OR Zparport0@3)) OR (interrupt@4 PHRASE 2 driven@5))" },
417 { "ULTRA PC-TUNING, COOLING & MODDING (4,6)", "((((ultra@1 OR (pc@2 PHRASE 2 tuning@3)) OR cooling@4) OR modding@5) OR 4,6@6)" },
418 { "512MB PC2700 DDR SDRAM Rood (Dane-Elec)", "((512mb@1 OR pc2700@2 OR ddr@3 OR sdram@4 OR rood@5) OR (dane@6 PHRASE 2 elec@7))" },
419 { "header(\"Content Type: text/html\");", "((header@1 OR (content@2 OR type@3)) OR (text@4 PHRASE 2 html@5))" },
420 { "\"-RW\" \"+RW\"", "(rw@1 OR rw@2)" },
421 { "\"cresta digital answering machine", "(cresta@1 PHRASE 4 digital@2 PHRASE 4 answering@3 PHRASE 4 machine@4)" },
422 { "Arctic Super Silent PRO TC (Athlon/P3 - 2,3 GHz)", "((arctic@1 OR super@2 OR silent@3 OR pro@4 OR tc@5) OR ((athlon@6 PHRASE 2 p3@7) OR (2,3@8 OR ghz@9)))" },
423 { "c++ fopen \"r+t\"", "((Zc++@1 OR Zfopen@2) OR (r@3 PHRASE 2 t@4))" },
424 { "c++ fopen (r+t)", "((Zc++@1 OR Zfopen@2) OR (Zr@3 OR Zt@4))" },
425 { "\"DVD+R\"", "(dvd@1 PHRASE 2 r@2)" },
426 { "Class.forName(\"jdbc.odbc.JdbcOdbcDriver\");", "((class@1 PHRASE 2 forname@2) OR (jdbc@3 PHRASE 3 odbc@4 PHRASE 3 jdbcodbcdriver@5))" },
427 { "perl(find.pl)", "(perl@1 OR (find@2 PHRASE 2 pl@3))" },
428 { "\"-5v\" voeding", "(5v@1 OR Zvoed@2)" },
429 { "\"-5v\" power supply", "(5v@1 OR (Zpower@2 OR Zsuppli@3))" },
430 { "An Error occurred whie attempting to initialize the Borland Database Engine (error $2108)", "((an@1 OR error@2 OR Zoccur@3 OR Zwhie@4 OR Zattempt@5 OR Zto@6 OR Ziniti@7 OR Zthe@8 OR borland@9 OR database@10 OR engine@11) OR (Zerror@12 OR 2108@13))" },
431 { "(error $2108) Borland", "((Zerror@1 OR 2108@2) OR borland@3)" },
432 { "On Friday 04 April 2003 09:32, Edwin van Eersel wrote: > ik voel me eigenlijk wel behoorlijk kut :)", "((((on@1 OR friday@2 OR 04@3 OR april@4 OR 2003@5) OR (09@6 PHRASE 2 32@7)) OR (edwin@8 OR Zvan@9 OR eersel@10 OR Zwrote@11)) OR (Zik@12 OR Zvoel@13 OR Zme@14 OR Zeigenlijk@15 OR Zwel@16 OR Zbehoorlijk@17 OR Zkut@18))" },
433 { "Elektrotechniek + \"hoe bevalt het?\"\"", "(elektrotechniek@1 OR (hoe@2 PHRASE 3 bevalt@3 PHRASE 3 het@4))" },
434 { "Shortcuts in menu (java", "((shortcuts@1 OR Zin@2 OR Zmenu@3) OR Zjava@4)" },
435 { "detonator+settings\"", "(Zdeton@1 OR settings@2)" },
436 { "(ez-bios) convert", "((ez@1 PHRASE 2 bios@2) OR Zconvert@3)" },
437 { "Sparkle 7100M4 64MB (GeForce4 MX440)", "((sparkle@1 OR 7100m4@2 OR 64mb@3) OR (geforce4@4 OR mx440@5))" },
438 { "freebsd \"boek OR newbie\"", "(Zfreebsd@1 OR (boek@2 PHRASE 3 or@3 PHRASE 3 newbie@4))" },
439 { "for (;;) c++", "(Zfor@1 OR Zc++@2)" },
440 { "1700+-2100+", "(1700+@1 PHRASE 2 2100+@2)" },
441 { "PHP Warning: Invalid library (maybe not a PHP library) 'libmysqlclient.so'", "(((php@1 OR warning@2 OR invalid@3 OR Zlibrari@4) OR (Zmayb@5 OR Znot@6 OR Za@7 OR php@8 OR Zlibrari@9)) OR (libmysqlclient@10 PHRASE 2 so@11))" },
442 { "NEC DV-5800B (Bul", "((nec@1 OR (dv@2 PHRASE 2 5800b@3)) OR bul@4)" },
443 { "org.jdom.input.SAXBuilder.<init>(SAXBuilder.java)", "(((org@1 PHRASE 4 jdom@2 PHRASE 4 input@3 PHRASE 4 saxbuilder@4) OR init@5) OR (saxbuilder@6 PHRASE 2 java@7))" },
444 { "AMD Athlon XP 2500+ (1,83GHz, 512KB)", "((amd@1 OR athlon@2 OR xp@3 OR 2500+@4) OR (1,83ghz@5 OR 512kb@6))" },
445 { "'q ben\"", "(Zq@1 OR ben@2)" },
446 { "getsmbfilepwent: malformed password entry (uid not number)", "((Zgetsmbfilepw@1 OR (Zmalform@2 OR Zpassword@3 OR Zentri@4)) OR (Zuid@5 OR Znot@6 OR Znumber@7))" },
447 { "\xc3\xb6ude onderdelen\"", "(Z\xc3\xb6ude@1 OR onderdelen@2)" },
448 { "Heeft iemand enig idee waarom de pioneer (zelf met originele firmware van pioneer) bij mij niet wil flashen ??", "(((heeft@1 OR Ziemand@2 OR Zenig@3 OR Zide@4 OR Zwaarom@5 OR Zde@6 OR Zpioneer@7) OR (Zzelf@8 OR Zmet@9 OR Zoriginel@10 OR Zfirmwar@11 OR Zvan@12 OR Zpioneer@13)) OR (Zbij@14 OR Zmij@15 OR Zniet@16 OR Zwil@17 OR Zflashen@18))" },
449 { "asus a7v266 bios nieuw -(a7v266-e)", "((Zasus@1 OR Za7v266@2 OR Zbio@3 OR Znieuw@4) AND_NOT (a7v266@5 PHRASE 2 e@6))" },
450 { "cybercom \"dvd+r\"", "(Zcybercom@1 OR (dvd@2 PHRASE 2 r@3))" },
451 { "AMD PCNET Family Ethernet Adapter (PCI-ISA)", "((amd@1 OR pcnet@2 OR family@3 OR ethernet@4 OR adapter@5) OR (pci@6 PHRASE 2 isa@7))" },
452 { "relais +/-", "Zrelai@1" },
453 { "formules (slepen OR doortrekken) excel", "((Zformul@1 OR (Zslepen@2 OR Zdoortrekken@3)) OR Zexcel@4)" },
454 { "\"%English", "english@1" },
455 { "select max( mysql", "((Zselect@1 OR max@2) OR Zmysql@3)" },
456 { "leejow(saait", "(leejow@1 OR Zsaait@2)" },
457 { "'Windows 2000 Advanced Server\" netwerkverbinding valt steeds weg", "((windows@1 OR 2000@2 OR advanced@3 OR server@4) OR (netwerkverbinding@5 PHRASE 4 valt@6 PHRASE 4 steeds@7 PHRASE 4 weg@8))" },
458 { "K7T Turbo 2 (MS-6330)", "((k7t@1 OR turbo@2 OR 2@3) OR (ms@4 PHRASE 2 6330@5))" },
459 { "failed to receive data from the client agent. (ec=1)", "((Zfail@1 OR Zto@2 OR Zreceiv@3 OR Zdata@4 OR Zfrom@5 OR Zthe@6 OR Zclient@7 OR Zagent@8) OR (ec@9 OR 1@10))" },
460 { "\"cannot find -lz\"", "(cannot@1 PHRASE 3 find@2 PHRASE 3 lz@3)" },
461 { "undefined reference to `mysql_drop_db'\"", "((Zundefin@1 OR Zrefer@2 OR Zto@3) OR Zmysql_drop_db@4)" },
462 { "search form asp \"%'", "(Zsearch@1 OR Zform@2 OR Zasp@3)" },
463 { "(dvd+r) kwaliteit", "((Zdvd@1 OR Zr@2) OR Zkwaliteit@3)" },
464 { "Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 35 bytes)", "((fatal@1 OR Zerror@2 OR allowed@3 OR Zmemori@4 OR Zsize@5 OR Zof@6 OR 8388608@7 OR Zbyte@8 OR Zexhaust@9) OR (Ztri@10 OR Zto@11 OR Zalloc@12 OR 35@13 OR Zbyte@14))" },
465 { "geluid (schokt OR hapert)", "(Zgeluid@1 OR (Zschokt@2 OR Zhapert@3))" },
466 { "Het wordt pas echt leuk als het hard staat!! >:)", "(het@1 OR Zwordt@2 OR Zpas@3 OR Zecht@4 OR Zleuk@5 OR Zal@6 OR Zhet@7 OR Zhard@8 OR Zstaat@9)" },
467 { "Uw configuratie bestand bevat instellingen (root zonder wachtwoord) die betrekking hebben tot de standaard MySQL account. Uw MySQL server draait met deze standaard waardes, en is open voor ongewilde toegang, het wordt dus aangeraden dit op te lossen", "(((((uw@1 OR Zconfigurati@2 OR Zbestand@3 OR Zbevat@4 OR Zinstellingen@5) OR (Zroot@6 OR Zzonder@7 OR Zwachtwoord@8)) OR (Zdie@9 OR Zbetrekk@10 OR Zhebben@11 OR Ztot@12 OR Zde@13 OR Zstandaard@14 OR mysql@15 OR Zaccount@16 OR uw@17 OR mysql@18 OR Zserver@19 OR Zdraait@20 OR Zmet@21 OR Zdeze@22 OR Zstandaard@23 OR Zwaard@24)) OR (Zen@25 OR Zis@26 OR Zopen@27 OR Zvoor@28 OR Zongewild@29 OR Ztoegang@30)) OR (Zhet@31 OR Zwordt@32 OR Zdus@33 OR Zaangeraden@34 OR Zdit@35 OR Zop@36 OR Zte@37 OR Zlossen@38))" },
468 { "(library qt-mt) not found", "((Zlibrari@1 OR (qt@2 PHRASE 2 mt@3)) OR (Znot@4 OR Zfound@5))" },
469 { "Qt (>= Qt 3.0.3) (library qt-mt) not found", "(((qt@1 OR (qt@2 OR 3.0.3@3)) OR (Zlibrari@4 OR (qt@5 PHRASE 2 mt@6))) OR (Znot@7 OR Zfound@8))" },
470 { "setup was unable to find (or could not read) the language specific setup resource dll, unable to continue. Please reboot and try again.", "((((Zsetup@1 OR Zwas@2 OR Zunabl@3 OR Zto@4 OR Zfind@5) OR (Zor@6 OR Zcould@7 OR Znot@8 OR Zread@9)) OR (Zthe@10 OR Zlanguag@11 OR Zspecif@12 OR Zsetup@13 OR Zresourc@14 OR Zdll@15)) OR (Zunabl@16 OR Zto@17 OR Zcontinu@18 OR please@19 OR Zreboot@20 OR Zand@21 OR Ztri@22 OR Zagain@23))" },
471 { "Titan TTC-D5TB(4/CU35)", "((titan@1 OR (ttc@2 PHRASE 2 d5tb@3)) OR (4@4 PHRASE 2 cu35@5))" },
472 { "[php] date( min", "((Zphp@1 OR date@2) OR Zmin@3)" },
473 { "EPOX EP-8RDA+ (nForce2 SPP+MCP-T) Rev. 1.1", "((((epox@1 OR (ep@2 PHRASE 2 8rda+@3)) OR ((Znforce2@4 OR spp@5) OR (mcp@6 PHRASE 2 t@7))) OR rev@8) OR 1.1@9)" },
474 { "554 5.4.6 Too many hops 53 (25 max)", "((554@1 OR 5.4.6@2 OR too@3 OR Zmani@4 OR Zhop@5 OR 53@6) OR (25@7 OR Zmax@8))" },
475 { "ik had toch nog een vraagje: er zijn nu eigenlijk alleen maar schijfjes van 4.7GB alleen straks zullen er vast schijfjes van meer dan 4.7GB komen. Zal deze brander dit wel kunnen schijven?""?(na bijvoorbeeld een firmware update?) ben erg benieuwd", "(((Zik@1 OR Zhad@2 OR Ztoch@3 OR Znog@4 OR Zeen@5 OR Zvraagj@6 OR Zer@7 OR Zzijn@8 OR Znu@9 OR Zeigenlijk@10 OR Zalleen@11 OR Zmaar@12 OR Zschijfj@13 OR Zvan@14 OR 4.7gb@15 OR Zalleen@16 OR Zstrak@17 OR Zzullen@18 OR Zer@19 OR Zvast@20 OR Zschijfj@21 OR Zvan@22 OR Zmeer@23 OR Zdan@24 OR 4.7gb@25 OR Zkomen@26 OR zal@27 OR Zdeze@28 OR Zbrander@29 OR Zdit@30 OR Zwel@31 OR Zkunnen@32 OR Zschijven@33) OR (Zna@34 OR Zbijvoorbeeld@35 OR Zeen@36 OR Zfirmwar@37 OR Zupdat@38)) OR (Zben@39 OR Zerg@40 OR Zbenieuwd@41))" },
476 { "ati linux drivers (4.3.0)", "((Zati@1 OR Zlinux@2 OR Zdriver@3) OR 4.3.0@4)" },
477 { "ENCAPSED_AND_WHITESPACE", "encapsed_and_whitespace@1" },
478 { "lpadmin: add-printer (set device) failed: client-error-not-possible", "((((Zlpadmin@1 OR (add@2 PHRASE 2 printer@3)) OR (Zset@4 OR Zdevic@5)) OR Zfail@6) OR (client@7 PHRASE 4 error@8 PHRASE 4 not@9 PHRASE 4 possible@10))" },
479 { "welke dvd \"+r\" media", "(((Zwelk@1 OR Zdvd@2) OR r@3) OR Zmedia@4)" },
480 { "Warning: stat failed for fotos(errno=2 - No such file or directory)", "((((warning@1 OR (Zstat@2 OR Zfail@3 OR Zfor@4 OR fotos@5)) OR errno@6) OR 2@7) OR (no@8 OR Zsuch@9 OR Zfile@10 OR Zor@11 OR Zdirectori@12))" },
481 { "dvd +/-", "Zdvd@1" },
482 { "7vaxp +voltage mod\"", "(Zvoltag@2 AND_MAYBE (7vaxp@1 OR mod@3))" },
483 { "lpt port (SPP/EPP) is enabled", "(((Zlpt@1 OR Zport@2) OR (spp@3 PHRASE 2 epp@4)) OR (Zis@5 OR Zenabl@6))" },
484 { "getenv(\"HTTP_REFERER\")", "(getenv@1 OR http_referer@2)" },
485 { "Error setting display mode: CreateDevice failed (D3DERR_DRIVERINTERNALERROR)", "((error@1 OR Zset@2 OR Zdisplay@3 OR Zmode@4 OR createdevice@5 OR Zfail@6) OR d3derr_driverinternalerror@7)" },
486 { "Exception number: c0000005 (access violation)", "((exception@1 OR Znumber@2 OR Zc0000005@3) OR (Zaccess@4 OR Zviolat@5))" },
487 { "header(\"Content-type:application/octetstream\");", "(header@1 OR (content@2 PHRASE 4 type@3 PHRASE 4 application@4 PHRASE 4 octetstream@5))" },
488 { "java.security.AccessControlException: access denied (java.lang.RuntimePermission accessClassInPackage.sun.jdbc.odbc)", "(((java@1 PHRASE 3 security@2 PHRASE 3 accesscontrolexception@3) OR (Zaccess@4 OR Zdeni@5)) OR ((java@6 PHRASE 3 lang@7 PHRASE 3 runtimepermission@8) OR (accessclassinpackage@9 PHRASE 4 sun@10 PHRASE 4 jdbc@11 PHRASE 4 odbc@12)))" },
489 { "(001.part.met", "(001@1 PHRASE 3 part@2 PHRASE 3 met@3)" },
490 { "Warning: mail(): Use the -f option (5th param) to include valid reply-to address ! in /usr/home/vdb/www/mail.php on line 79", "((((((((((warning@1 OR mail@2) OR (use@3 OR Zthe@4)) OR (Zf@5 OR Zoption@6)) OR (5th@7 OR Zparam@8)) OR (Zto@9 OR Zinclud@10 OR Zvalid@11)) OR (reply@12 PHRASE 2 to@13)) OR Zaddress@14) OR Zin@15) OR (usr@16 PHRASE 6 home@17 PHRASE 6 vdb@18 PHRASE 6 www@19 PHRASE 6 mail@20 PHRASE 6 php@21)) OR (Zon@22 OR Zline@23 OR 79@24))" },
491 { "PHP Use the -f option (5th param)", "((((php@1 OR use@2 OR Zthe@3) OR Zoption@5) OR (5th@6 OR Zparam@7)) AND_NOT Zf@4)" },
492 { "dvd \"+\" \"-\"", "Zdvd@1" },
493 { "bericht ( %)", "Zbericht@1" },
494 { "2500+ of 2600+ (niett OC)", "((2500+@1 OR Zof@2 OR 2600+@3) OR (Zniett@4 OR oc@5))" },
495 { "maxtor windows xp werkt The drivers for this device are not installed. (Code 28)", "((Zmaxtor@1 OR Zwindow@2 OR Zxp@3 OR Zwerkt@4 OR the@5 OR Zdriver@6 OR Zfor@7 OR Zthis@8 OR Zdevic@9 OR Zare@10 OR Znot@11 OR Zinstal@12) OR (code@13 OR 28@14))" },
496 { "Warning: stat failed for /mnt/web/react/got/react/board/non-www/headlines/tnet-headlines.txt (errno=2 - No such file or directory) in /mnt/web/react/got/react/global/non-www/templates/got/functions.inc.php on line 303", "((((((warning@1 OR (Zstat@2 OR Zfail@3 OR Zfor@4)) OR (mnt@5 PHRASE 12 web@6 PHRASE 12 react@7 PHRASE 12 got@8 PHRASE 12 react@9 PHRASE 12 board@10 PHRASE 12 non@11 PHRASE 12 www@12 PHRASE 12 headlines@13 PHRASE 12 tnet@14 PHRASE 12 headlines@15 PHRASE 12 txt@16)) OR ((errno@17 OR 2@18) OR (no@19 OR Zsuch@20 OR Zfile@21 OR Zor@22 OR Zdirectori@23))) OR Zin@24) OR (mnt@25 PHRASE 13 web@26 PHRASE 13 react@27 PHRASE 13 got@28 PHRASE 13 react@29 PHRASE 13 global@30 PHRASE 13 non@31 PHRASE 13 www@32 PHRASE 13 templates@33 PHRASE 13 got@34 PHRASE 13 functions@35 PHRASE 13 inc@36 PHRASE 13 php@37)) OR (Zon@38 OR Zline@39 OR 303@40))" },
497 { "apm: BIOS version 1.2 Flags 0x03 (Driver version 1.16)", "((Zapm@1 OR (bios@2 OR Zversion@3 OR 1.2@4 OR flags@5 OR 0x03@6)) OR (driver@7 OR Zversion@8 OR 1.16@9))" },
498 { "GA-8IHXP(3.0)", "((ga@1 PHRASE 2 8ihxp@2) OR 3.0@3)" },
499 { "8IHXP(3.0)", "(8ihxp@1 OR 3.0@2)" },
500 { "na\xc2\xb7si (de ~ (m.))", "(Zna\xc2\xb7si@1 OR (Zde@2 OR Zm@3))" },
501 { "header(\"Content-Disposition: attachment;", "(header@1 OR (content@2 PHRASE 3 disposition@3 PHRASE 3 attachment@4))" },
502 { "\"header(\"Content-Disposition: attachment;\"", "((header@1 OR (content@2 PHRASE 2 disposition@3)) OR Zattach@4)" },
503 { "\"Beep -f\"", "(beep@1 PHRASE 2 f@2)" },
504 { "kraan NEAR (Elektrisch OR Electrisch)", "((Zkraan@1 OR near@2) OR (elektrisch@3 OR or@4 OR electrisch@5))" },
505 { "checking for Qt... configure: error: Qt (>= Qt 3.0.2) (headers and libraries) not found. Please check your installation!", "((((Zcheck@1 OR Zfor@2 OR qt@3 OR Zconfigur@4 OR Zerror@5 OR qt@6) OR (qt@7 OR 3.0.2@8)) OR (Zheader@9 OR Zand@10 OR Zlibrari@11)) OR (Znot@12 OR Zfound@13 OR please@14 OR Zcheck@15 OR Zyour@16 OR Zinstal@17))" },
506 { "parse error, unexpected '\\\"', expecting T_STRING or T_VARIABLE or T_NUM_STRING", "(((Zpars@1 OR Zerror@2) OR Zunexpect@3) OR (expecting@4 PHRASE 6 t_string@5 PHRASE 6 or@6 PHRASE 6 t_variable@7 PHRASE 6 or@8 PHRASE 6 t_num_string@9))" },
507 { "ac3 (0x2000) \"Dolby Laboratories,", "((Zac3@1 OR 0x2000@2) OR (dolby@3 PHRASE 2 laboratories@4))" },
508 { "Movie.FileName=(\"../../../~animations/\"+lesson1.recordset.fields('column3')+\"Intro.avi\")", "(((((movie@1 PHRASE 2 filename@2) OR animations@3) OR (lesson1@4 PHRASE 3 recordset@5 PHRASE 3 fields@6)) OR Zcolumn3@7) OR (intro@8 PHRASE 2 avi@9))" },
509 { "502 Permission Denied - Permission Denied - news.chello.nl -- http://www.chello.nl/ (Typhoon v1.2.3)", "(((((502@1 OR permission@2 OR denied@3) OR (permission@4 OR denied@5)) OR (news@6 PHRASE 3 chello@7 PHRASE 3 nl@8)) OR (http@9 PHRASE 4 www@10 PHRASE 4 chello@11 PHRASE 4 nl@12)) OR (typhoon@13 OR Zv1.2.3@14))" },
510 { "Motion JPEG (MJPEG codec)", "((motion@1 OR jpeg@2) OR (mjpeg@3 OR Zcodec@4))" },
511 { ": zoomtext\"", "zoomtext@1" },
512 { "Your SORT command does not seem to support the \"-r -n -k 7\"", "((your@1 OR sort@2 OR Zcommand@3 OR Zdoe@4 OR Znot@5 OR Zseem@6 OR Zto@7 OR Zsupport@8 OR Zthe@9) OR (r@10 PHRASE 4 n@11 PHRASE 4 k@12 PHRASE 4 7@13))" },
513 { "Geef de naam van de MSDOS prompt op C:\\\\WINDOWS.COM\\\"", "((geef@1 OR Zde@2 OR Znaam@3 OR Zvan@4 OR Zde@5 OR msdos@6 OR Zprompt@7 OR Zop@8) OR (c@9 PHRASE 3 windows@10 PHRASE 3 com@11))" },
514 { "\"\"wa is fase\"", "(Zwa@1 OR Zis@2 OR fase@3)" },
515 { "<v:imagedata src=\"", "((v@1 PHRASE 2 imagedata@2) OR src@3)" },
516 { "system(play ringin.wav); ?>", "((system@1 OR Zplay@2) OR (ringin@3 PHRASE 2 wav@4))" },
517 { "\"perfect NEAR systems\"", "(perfect@1 PHRASE 3 near@2 PHRASE 3 systems@3)" },
518 { "LoadLibrary(\"mainta/gamex86.dll\") failed", "((loadlibrary@1 OR (mainta@2 PHRASE 3 gamex86@3 PHRASE 3 dll@4)) OR Zfail@5)" },
519 { "DATE_FORMAT('1997-10-04 22:23:00', '%W %M %Y');", "(((((date_format@1 OR (1997@2 PHRASE 3 10@3 PHRASE 3 04@4)) OR (22@5 PHRASE 3 23@6 PHRASE 3 00@7)) OR w@8) OR m@9) OR y@10)" },
520 { "secundaire IDE-controller (dubbele fifo)", "((Zsecundair@1 OR (ide@2 PHRASE 2 controller@3)) OR (Zdubbel@4 OR Zfifo@5))" },
521 { "\"Postal2+Explorer.exe\"", "(postal2@1 PHRASE 3 explorer@2 PHRASE 3 exe@3)" },
522 { "COUNT(*)", "count@1" },
523 { "Nuttige Windows progs (1/11)", "((nuttige@1 OR windows@2 OR Zprog@3) OR (1@4 PHRASE 2 11@5))" },
524 { "if(usercode==passcode==)", "((if@1 OR usercode@2) OR passcode@3)" },
525 { "lg 8160b (dvd+r)", "((Zlg@1 OR 8160b@2) OR (Zdvd@3 OR Zr@4))" },
526 { "iPAQ Pocket PC 2002 End User Update (EUU - Service Pack)", "((Zipaq@1 OR pocket@2 OR pc@3 OR 2002@4 OR end@5 OR user@6 OR update@7) OR (euu@8 OR (service@9 OR pack@10)))" },
527 { "'ipod pakt tags niet\"", "(Zipod@1 OR Zpakt@2 OR Ztag@3 OR niet@4)" },
528 { "\"DVD+/-R\"", "(dvd+@1 PHRASE 2 r@2)" },
529 { "\"DVD+R DVD-R\"", "(dvd@1 PHRASE 4 r@2 PHRASE 4 dvd@3 PHRASE 4 r@4)" },
530 { "php ;) in een array zetten", "(Zphp@1 OR (Zin@2 OR Zeen@3 OR Zarray@4 OR Zzetten@5))" },
531 { "De inhoud van uw advertentie is niet geschikt voor plaatsing op marktplaats! (001", "((de@1 OR Zinhoud@2 OR Zvan@3 OR Zuw@4 OR Zadvertenti@5 OR Zis@6 OR Zniet@7 OR Zgeschikt@8 OR Zvoor@9 OR Zplaats@10 OR Zop@11 OR Zmarktplaat@12) OR 001@13)" },
532 { "creative (soundblaster OR sb) 128", "((Zcreativ@1 OR (Zsoundblast@2 OR Zsb@3)) OR 128@4)" },
533 { "Can't open file: (errno: 145)", "((can't@1 OR Zopen@2 OR Zfile@3) OR (Zerrno@4 OR 145@5))" },
534 { "Formateren lukt niet(98,XP)", "(((formateren@1 OR Zlukt@2 OR niet@3) OR 98@4) OR xp@5)" },
535 { "access denied (java.io.", "((Zaccess@1 OR Zdeni@2) OR (java@3 PHRASE 2 io@4))" },
536 { "(access denied (java.io.)", "((Zaccess@1 OR Zdeni@2) OR (java@3 PHRASE 2 io@4))" },
537 { "wil niet installeren ( crc fouten)", "((Zwil@1 OR Zniet@2 OR Zinstalleren@3) OR (Zcrc@4 OR Zfouten@5))" },
538 { "(DVD+RW) brandsoftware meerdere", "((dvd@1 OR rw@2) OR (Zbrandsoftwar@3 OR Zmeerder@4))" },
539 { "(database OF databases) EN geheugen", "((Zdatabas@1 OR of@2 OR Zdatabas@3) OR (en@4 OR Zgeheugen@5))" },
540 { "(server 2003) winroute", "((Zserver@1 OR 2003@2) OR Zwinrout@3)" },
541 { "54MHz (kanaal 2 VHF) tot tenminste 806 MHz (kanaal 69 UHF)", "(((54mhz@1 OR (Zkanaal@2 OR 2@3 OR vhf@4)) OR (Ztot@5 OR Ztenminst@6 OR 806@7 OR mhz@8)) OR (Zkanaal@9 OR 69@10 OR uhf@11))" },
542 { "(draadloos OR wireless) netwerk", "((Zdraadloo@1 OR Zwireless@2) OR Znetwerk@3)" },
543 { "localtime(time(NULL));", "((localtime@1 OR time@2) OR null@3)" },
544 { "ob_start(\"ob_gzhandler\");", "(ob_start@1 OR ob_gzhandler@2)" },
545 { "PPP Closed : LCP Time-out (VPN-0)", "((((ppp@1 OR closed@2) OR lcp@3) OR (time@4 PHRASE 2 out@5)) OR (vpn@6 PHRASE 2 0@7))" },
546 { "COM+-gebeurtenissysteem", "(com+@1 PHRASE 2 gebeurtenissysteem@2)" },
547 { "rcpthosts (#5.7.1)", "(Zrcpthost@1 OR 5.7.1@2)" },
548 { "Dit apparaat werkt niet goed omdat Windows de voor dit apparaat vereiste stuurprogramma's niet kan laden. (Code 31)", "((dit@1 OR Zapparaat@2 OR Zwerkt@3 OR Zniet@4 OR Zgo@5 OR Zomdat@6 OR windows@7 OR Zde@8 OR Zvoor@9 OR Zdit@10 OR Zapparaat@11 OR Zvereist@12 OR Zstuurprogramma@13 OR Zniet@14 OR Zkan@15 OR Zladen@16) OR (code@17 OR 31@18))" },
549 { "window.open( scrollbar", "((window@1 PHRASE 2 open@2) OR Zscrollbar@3)" },
550 { "T68i truc ->", "(t68i@1 OR Ztruc@2)" },
551 { "T68i ->", "t68i@1" },
552 { "\"de lijn is bezet\"\"", "(de@1 PHRASE 4 lijn@2 PHRASE 4 is@3 PHRASE 4 bezet@4)" },
553 { "if (eregi(\"", "(Zif@1 OR eregi@2)" },
554 { "This device is not working properly because Windows cannot load the drivers required for this device. (Code 31)", "((this@1 OR Zdevic@2 OR Zis@3 OR Znot@4 OR Zwork@5 OR Zproper@6 OR Zbecaus@7 OR windows@8 OR Zcannot@9 OR Zload@10 OR Zthe@11 OR Zdriver@12 OR Zrequir@13 OR Zfor@14 OR Zthis@15 OR Zdevic@16) OR (code@17 OR 31@18))" },
555 { "execCommand(\"Paste\");", "(execcommand@1 OR paste@2)" },
556 { "\"-1 unread\"", "(1@1 PHRASE 2 unread@2)" },
557 { "\"www.historical-fire-engines", "(www@1 PHRASE 4 historical@2 PHRASE 4 fire@3 PHRASE 4 engines@4)" },
558 { "\"DVD+RW\" erase", "((dvd@1 PHRASE 2 rw@2) OR Zeras@3)" },
559 { "[showjekamer)", "Zshowjekam@1" },
560 { "The description for Event ID 1 in Source True Vector Engine ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURC", "(((the@1 OR Zdescript@2 OR Zfor@3 OR event@4 OR id@5 OR 1@6 OR Zin@7 OR source@8 OR true@9 OR vector@10 OR engine@11) OR (Zcannot@12 OR Zbe@13 OR Zfound@14 OR the@15 OR Zlocal@16 OR Zcomput@17 OR Zmay@18 OR Znot@19 OR Zhave@20 OR Zthe@21 OR Znecessari@22 OR Zregistri@23 OR Zinform@24 OR Zor@25 OR Zmessag@26 OR dll@27 OR Zfile@28 OR Zto@29 OR Zdisplay@30 OR Zmessag@31 OR Zfrom@32 OR Za@33 OR Zremot@34 OR Zcomput@35 OR you@36 OR Zmay@37 OR Zbe@38 OR Zabl@39 OR Zto@40 OR Zuse@41 OR Zthe@42)) OR auxsourc@43)" },
561 { "org.apache.jasper.JasperException: This absolute uri (http://java.sun.com/jstl/core) cannot be resolved in either web.xml or the jar files deployed with this application", "((((((org@1 PHRASE 4 apache@2 PHRASE 4 jasper@3 PHRASE 4 jasperexception@4) OR (this@5 OR Zabsolut@6 OR Zuri@7)) OR (http@8 PHRASE 6 java@9 PHRASE 6 sun@10 PHRASE 6 com@11 PHRASE 6 jstl@12 PHRASE 6 core@13)) OR (Zcannot@14 OR Zbe@15 OR Zresolv@16 OR Zin@17 OR Zeither@18)) OR (web@19 PHRASE 2 xml@20)) OR (Zor@21 OR Zthe@22 OR Zjar@23 OR Zfile@24 OR Zdeploy@25 OR Zwith@26 OR Zthis@27 OR Zapplic@28))" },
562 { "This absolute uri (http://java.sun.com/jstl/core) cannot be resolved in either web.xml or the jar files deployed with this application", "(((((this@1 OR Zabsolut@2 OR Zuri@3) OR (http@4 PHRASE 6 java@5 PHRASE 6 sun@6 PHRASE 6 com@7 PHRASE 6 jstl@8 PHRASE 6 core@9)) OR (Zcannot@10 OR Zbe@11 OR Zresolv@12 OR Zin@13 OR Zeither@14)) OR (web@15 PHRASE 2 xml@16)) OR (Zor@17 OR Zthe@18 OR Zjar@19 OR Zfile@20 OR Zdeploy@21 OR Zwith@22 OR Zthis@23 OR Zapplic@24))" },
563 { "vervangen # \"/", "Zvervangen@1" },
564 { "vervangen # /\"", "Zvervangen@1" },
565 { "while(list($key, $val) = each($HTTP_POST_VARS))", "(((((while@1 OR list@2) OR Zkey@3) OR Zval@4) OR each@5) OR http_post_vars@6)" },
566 { "PowerDVD does not support the current display mode. (DDraw Overlay mode is recommended)", "((powerdvd@1 OR Zdoe@2 OR Znot@3 OR Zsupport@4 OR Zthe@5 OR Zcurrent@6 OR Zdisplay@7 OR Zmode@8) OR (ddraw@9 OR overlay@10 OR Zmode@11 OR Zis@12 OR Zrecommend@13))" },
567 { "Warning: Unexpected character in input: '' (ASCII=92) state=1 highlight", "((((warning@1 OR (unexpected@2 OR Zcharact@3 OR Zin@4 OR Zinput@5)) OR (ascii@6 OR 92@7)) OR state@8) OR (1@9 OR Zhighlight@10))" },
568 { "error: Qt-1.4 (headers and libraries) not found. Please check your installation!", "(((Zerror@1 OR (qt@2 PHRASE 2 1.4@3)) OR (Zheader@4 OR Zand@5 OR Zlibrari@6)) OR (Znot@7 OR Zfound@8 OR please@9 OR Zcheck@10 OR Zyour@11 OR Zinstal@12))" },
569 { "Error while initializing the sound driver: device /dev/dsp can't be opened (No such device) The sound server will continue, using the null output device.", "((((((error@1 OR Zwhile@2 OR Ziniti@3 OR Zthe@4 OR Zsound@5 OR Zdriver@6 OR Zdevic@7) OR (dev@8 PHRASE 2 dsp@9)) OR (Zcan't@10 OR Zbe@11 OR Zopen@12)) OR (no@13 OR Zsuch@14 OR Zdevic@15)) OR (the@16 OR Zsound@17 OR Zserver@18 OR Zwill@19 OR Zcontinu@20)) OR (Zuse@21 OR Zthe@22 OR Znull@23 OR Zoutput@24 OR Zdevic@25))" },
570 { "mag mijn waarschuwing nu weg ? ;)", "(Zmag@1 OR Zmijn@2 OR Zwaarschuw@3 OR Znu@4 OR Zweg@5)" },
571 { "Abit NF7-S (nForce 2 Chipset) Rev 2.0", "(((abit@1 OR (nf7@2 PHRASE 2 s@3)) OR (Znforc@4 OR 2@5 OR chipset@6)) OR (rev@7 OR 2.0@8))" },
572 { "Setup Could Not Verify the Integrity of the File\" Error Message Occurs When You Try to Install Windows XP Service Pack 1", "((setup@1 OR could@2 OR not@3 OR verify@4 OR Zthe@5 OR integrity@6 OR Zof@7 OR Zthe@8 OR file@9) OR (error@10 PHRASE 13 message@11 PHRASE 13 occurs@12 PHRASE 13 when@13 PHRASE 13 you@14 PHRASE 13 try@15 PHRASE 13 to@16 PHRASE 13 install@17 PHRASE 13 windows@18 PHRASE 13 xp@19 PHRASE 13 service@20 PHRASE 13 pack@21 PHRASE 13 1@22))" },
573 { "(browser 19) citrix", "((Zbrowser@1 OR 19@2) OR Zcitrix@3)" },
574 { "preg_replace (.*?)", "Zpreg_replac@1" },
575 { "formule excel #naam\"?\"", "((Zformul@1 OR Zexcel@2) OR naam@3)" },
576 { "->", "" },
577 { "De instructie op 0x77f436f7 verwijst naar geheugen op 0x007f4778. De lees-of schrijfbewerking (\"written\") op het geheugen is mislukt", "(((((de@1 OR Zinstructi@2 OR Zop@3 OR 0x77f436f7@4 OR Zverwijst@5 OR Znaar@6 OR Zgeheugen@7 OR Zop@8 OR 0x007f4778@9 OR de@10) OR (lees@11 PHRASE 2 of@12)) OR Zschrijfbewerk@13) OR written@14) OR (Zop@15 OR Zhet@16 OR Zgeheugen@17 OR Zis@18 OR Zmislukt@19))" },
578 { "<iframe src=\"www.tweakers.net></iframe>", "((Zifram@1 OR src@2) OR (www@3 PHRASE 4 tweakers@4 PHRASE 4 net@5 PHRASE 4 iframe@6))" },
579 { "\"rpm -e httpd\"", "(rpm@1 PHRASE 3 e@2 PHRASE 3 httpd@3)" },
580 { "automatisch op All Flis (*.*)", "(Zautomatisch@1 OR Zop@2 OR all@3 OR flis@4)" },
581 { "(Windows; U; Windows NT 5.1; en-US; rv:1.3b) Gecko/20030210", "(((((windows@1 OR u@2) OR (windows@3 OR nt@4 OR 5.1@5)) OR (en@6 PHRASE 2 us@7)) OR (rv@8 PHRASE 2 1.3b@9)) OR (gecko@10 PHRASE 2 20030210@11))" },
582 { "en-US; rv:1.3b) Gecko/20030210", "(((en@1 PHRASE 2 us@2) OR (rv@3 PHRASE 2 1.3b@4)) OR (gecko@5 PHRASE 2 20030210@6))" },
583 { "\"en-US; rv:1.3b) Gecko/20030210\"", "(en@1 PHRASE 6 us@2 PHRASE 6 rv@3 PHRASE 6 1.3b@4 PHRASE 6 gecko@5 PHRASE 6 20030210@6)" },
584 { "(./) chmod.sh", "(chmod@1 PHRASE 2 sh@2)" },
585 { "document.write(ssg(\" html", "(((document@1 PHRASE 2 write@2) OR ssg@3) OR html@4)" },
586 { "superstack \"mac+adressen\"", "(Zsuperstack@1 OR (mac@2 PHRASE 2 adressen@3))" },
587 { "IIS getenv(REMOTE_HOST)_", "(((iis@1 OR getenv@2) OR remote_host@3) OR _@4)" },
588 { "IIS en getenv(REMOTE_HOST)", "((iis@1 OR Zen@2 OR getenv@3) OR remote_host@4)" },
589 { "php getenv(\"HTTP_REFERER\")", "((Zphp@1 OR getenv@2) OR http_referer@3)" },
590 { "nec+-1300", "(nec+@1 PHRASE 2 1300@2)" },
591 { "smbpasswd script \"-s\"", "((Zsmbpasswd@1 OR Zscript@2) OR s@3)" },
592 { "leestekens \" \xc3\xb6 \xc3\xab", "(Zleesteken@1 OR (\xc3\xb6@2 PHRASE 2 \xc3\xab@3))" },
593 { "freesco and (all seeing eye)", "((Zfreesco@1 OR Zand@2) OR (Zall@3 OR Zsee@4 OR Zeye@5))" },
594 { "('all seeing eye') and freesco", "((Zall@1 OR Zsee@2 OR Zeye@3) OR (Zand@4 OR Zfreesco@5))" },
595 { "\"[......\"", "" },
596 { "Error = 11004 (500 No Data (Winsock error #11004))", "((error@1 OR 11004@2) OR ((500@3 OR no@4 OR data@5) OR ((winsock@6 OR Zerror@7) OR 11004@8)))" },
597 { "gegevensfout (cyclishe redundantiecontrole)", "(Zgegevensfout@1 OR (Zcyclish@2 OR Zredundantiecontrol@3))" },
598 { "firmware versie waar NEC\"", "(Zfirmwar@1 OR Zversi@2 OR Zwaar@3 OR nec@4)" },
599 { "nu.nl \"-1\"", "((nu@1 PHRASE 2 nl@2) OR 1@3)" },
600 { "provider+-webspace", "(provider+@1 PHRASE 2 webspace@2)" },
601 { "verschil \"dvd+rw\" \"dvd-rw\"", "((Zverschil@1 OR (dvd@2 PHRASE 2 rw@3)) OR (dvd@4 PHRASE 2 rw@5))" },
602 { "(dhcp client) + hangt", "((Zdhcp@1 OR Zclient@2) OR Zhangt@3)" },
603 { "MSI 875P Neo-FIS2R (Intel 875P)", "(((msi@1 OR 875p@2) OR (neo@3 PHRASE 2 fis2r@4)) OR (intel@5 OR 875p@6))" },
604 { "voeding passief gekoeld\"", "(Zvoed@1 OR Zpassief@2 OR gekoeld@3)" },
605 { "if (mysql_num_rows($resultaat)==1)", "(((Zif@1 OR mysql_num_rows@2) OR Zresultaat@3) OR 1@4)" },
606 { "Server.CreateObject(\"Persits.Upload.1\")", "((server@1 PHRASE 2 createobject@2) OR (persits@3 PHRASE 3 upload@4 PHRASE 3 1@5))" },
607 { "if(cod>9999999)cod=parseInt(cod/64)", "(((((if@1 OR cod@2) OR 9999999@3) OR cod@4) OR parseint@5) OR (cod@6 PHRASE 2 64@7))" },
608 { "if (cod>9999999", "(Zif@1 OR (cod@2 OR 9999999@3))" },
609 { "\"rm -rf /bin/laden\"", "(rm@1 PHRASE 4 rf@2 PHRASE 4 bin@3 PHRASE 4 laden@4)" },
610 { "\">>> 0) & 0xFF\"", "(0@1 PHRASE 2 0xff@2)" },
611 { "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"> document.body.scrollHeight", "(((doctype@1 OR html@2 OR public@3) OR (w3c@4 PHRASE 5 dtd@5 PHRASE 5 html@6 PHRASE 5 4.01@7 PHRASE 5 en@8)) OR (document@9 PHRASE 3 body@10 PHRASE 3 scrollheight@11))" },
612 { "<BR>window.resizeBy(offsetX,offsetY)<P>kweet", "(((((br@1 OR (window@2 PHRASE 2 resizeby@3)) OR Zoffsetx@4) OR Zoffseti@5) OR p@6) OR Zkweet@7)" },
613 { "linux humor :)", "(Zlinux@1 OR Zhumor@2)" },
614 { "ClassFactory kan aangevraagde klasse niet leveren (Fout=80040111)", "((classfactory@1 OR Zkan@2 OR Zaangevraagd@3 OR Zklass@4 OR Zniet@5 OR Zleveren@6) OR (fout@7 OR 80040111@8))" },
615 { "remote_smtp defer (-44)", "((Zremote_smtp@1 OR Zdefer@2) OR 44@3)" },
616 { "txtlogin.getText().trim().toUpperCase().intern() == inuser[2 * (i - 1) + 2].trim().toUpperCase().intern() && txtpass.getText().trim().toUpperCase().intern() == inuser[2 * (i - 1) + 3].trim().toUpperCase().intern())", "((((((((((((((((((((((((txtlogin@1 PHRASE 2 gettext@2) OR trim@3) OR touppercase@4) OR intern@5) OR inuser@6) OR 2@7) OR Zi@8) OR 1@9) OR 2@10) OR trim@11) OR touppercase@12) OR intern@13) OR (txtpass@14 PHRASE 2 gettext@15)) OR trim@16) OR touppercase@17) OR intern@18) OR inuser@19) OR 2@20) OR Zi@21) OR 1@22) OR 3@23) OR trim@24) OR touppercase@25) OR intern@26)" },
617 { "Koper + amoniak (NH2", "((koper@1 OR Zamoniak@2) OR nh2@3)" },
618 { "nec dvd -/+r", "((Znec@1 OR Zdvd@2) AND_NOT Zr@3)" },
619 { "er is een gereserveerde fout (-1104) opgetreden", "(((Zer@1 OR Zis@2 OR Zeen@3 OR Zgereserveerd@4 OR Zfout@5) OR 1104@6) OR Zopgetreden@7)" },
620 { "Cor \\(CCN\\)'\" <cor.kloet@ccn.controlec.nl>", "((cor@1 OR ccn@2) OR (cor@3 PHRASE 5 kloet@4 PHRASE 5 ccn@5 PHRASE 5 controlec@6 PHRASE 5 nl@7))" },
621 { "Warning: Failed opening for inclusion (include_path='') in Unknown on line 0", "(((warning@1 OR (failed@2 OR Zopen@3 OR Zfor@4 OR Zinclus@5)) OR include_path@6) OR (Zin@7 OR unknown@8 OR Zon@9 OR Zline@10 OR 0@11))" },
622 { "\"~\" + \"c:\\\"", "Zc@1" },
623 { "mysql count(*)", "(Zmysql@1 OR count@2)" },
624 { "for %f in (*.*) do", "((Zfor@1 OR (Zf@2 OR Zin@3)) OR Zdo@4)" },
625 { "raar \"~\" bestand", "(Zraar@1 OR Zbestand@2)" },
626 { "NEC DVD +-R/RW 1300", "(((nec@1 OR dvd@2) OR (r@3 PHRASE 2 rw@4)) OR 1300@5)" },
627 { "approved (ref: 38446-263)", "(Zapprov@1 OR (Zref@2 OR (38446@3 PHRASE 2 263@4)))" },
628 { "GA-7VRXP(2.0)", "((ga@1 PHRASE 2 7vrxp@2) OR 2.0@3)" },
629 { "~ Could not retrieve directory listing for \"/\"", "(could@1 OR Znot@2 OR Zretriev@3 OR Zdirectori@4 OR Zlist@5 OR Zfor@6)" },
630 { "asp CreateObject(\"Word.Document\")", "((Zasp@1 OR createobject@2) OR (word@3 PHRASE 2 document@4))" },
631 { "De lees- of schrijfbewerking (\"written\") op het geheugen is mislukt.", "(((de@1 OR Zlee@2 OR Zof@3 OR Zschrijfbewerk@4) OR written@5) OR (Zop@6 OR Zhet@7 OR Zgeheugen@8 OR Zis@9 OR Zmislukt@10))" },
632 { "putStr (map (\\x -> chr (round (21/2 * x^3 - 92 * x^2 + 503/2 * x - 105))) [1..4])", "(Zputstr@1 OR ((Zmap@2 OR ((Zx@3 OR (Zround@5 OR (((((((((21@6 PHRASE 2 2@7) OR Zx@8) OR 3@9) OR 92@10) OR Zx@11) OR 2@12) OR (503@13 PHRASE 2 2@14)) OR Zx@15) OR 105@16))) AND_NOT Zchr@4)) OR (1@17 PHRASE 2 4@18)))" },
633 { "parent.document.getElementById(\\\"leftmenu\\\").cols", "(((parent@1 PHRASE 3 document@2 PHRASE 3 getelementbyid@3) OR leftmenu@4) OR Zcol@5)" },
634 { "<% if not isEmpty(Request.QueryString) then", "(((Zif@1 OR Znot@2 OR isempty@3) OR (request@4 PHRASE 2 querystring@5)) OR Zthen@6)" },
635 { "Active Desktop (Hier issie)", "((active@1 OR desktop@2) OR (hier@3 OR Zissi@4))" },
636 { "Asus A7V8X (LAN + Sound)", "((asus@1 OR a7v8x@2) OR (lan@3 OR sound@4))" },
637 { "Novell This pentium class machine (or greater) lacks some required CPU feature(s", "((((novell@1 OR this@2 OR Zpentium@3 OR Zclass@4 OR Zmachin@5) OR (Zor@6 OR Zgreater@7)) OR (Zlack@8 OR Zsome@9 OR Zrequir@10 OR cpu@11 OR feature@12)) OR Zs@13)" },
638 { "sql server install fails error code (-1)", "((Zsql@1 OR Zserver@2 OR Zinstal@3 OR Zfail@4 OR Zerror@5 OR Zcode@6) OR 1@7)" },
639 { "session_register(\"login\");", "(session_register@1 OR login@2)" },
640 { "\"kylix+ndmb\"", "(kylix@1 PHRASE 2 ndmb@2)" },
641 { "Cannot find imap library (libc-client.a).", "((cannot@1 OR Zfind@2 OR Zimap@3 OR Zlibrari@4) OR (libc@5 PHRASE 3 client@6 PHRASE 3 a@7))" },
642 { "If ($_SESSION[\"Login\"] == 1)", "(if@1 OR ((_session@2 OR login@3) OR 1@4))" },
643 { "You have an error in your SQL syntax near '1')' at line 1", "(((you@1 OR Zhave@2 OR Zan@3 OR Zerror@4 OR Zin@5 OR Zyour@6 OR sql@7 OR Zsyntax@8 OR Znear@9) OR 1@10) OR (Zat@11 OR Zline@12 OR 1@13))" },
644 { "ASRock K7VT2 (incl. LAN)", "((asrock@1 OR k7vt2@2) OR (Zincl@3 OR lan@4))" },
645 { "+windows98 +(geen communicatie) +ie5", "((Zwindows98@1 AND (Zgeen@2 OR Zcommunicati@3)) AND Zie5@4)" },
646 { "\"xterm -fn\"", "(xterm@1 PHRASE 2 fn@2)" },
647 { "IRQL_NOT_LESS_OR_EQUAL", "irql_not_less_or_equal@1" },
648 { "access query \"NOT IN\"", "((Zaccess@1 OR Zqueri@2) OR (not@3 PHRASE 2 in@4))" },
649 { "\"phrase one \"phrase two\"", "((phrase@1 PHRASE 2 one@2) OR (Zphrase@3 OR two@4))" },
650 { "NEAR 207 46 249 27", "(near@1 OR 207@2 OR 46@3 OR 249@4 OR 27@5)" },
651 { "- NEAR 12V voeding", "(near@1 OR 12v@2 OR Zvoed@3)" },
652 { "waarom \"~\" in directorynaam", "(Zwaarom@1 OR (Zin@2 OR Zdirectorynaam@3))" },
653 { "cd'r NEAR toebehoren", "(cd'r@1 NEAR 11 toebehoren@2)" },
654 { "site:1 site:2", "0 * (H1 OR H2)" },
655 { "site:1 site2:2", "0 * (H1 AND J2)" },
656 { "site:1 site:2 site2:2", "0 * ((H1 OR H2) AND J2)" },
657 { "site:1 OR site:2", "(0 * H1 OR 0 * H2)" },
658 { "site:1 AND site:2", "(0 * H1 AND 0 * H2)" },
659 { "foo AND site:2", "(Zfoo@1 AND 0 * H2)" },
660 // Non-exclusive boolean prefixes feature tests (ticket#402):
661 { "category:1 category:2", "0 * (XCAT1 AND XCAT2)" },
662 { "category:1 site2:2", "0 * (J2 AND XCAT1)" },
663 { "category:1 category:2 site2:2", "0 * (J2 AND (XCAT1 AND XCAT2))" },
664 { "category:1 OR category:2", "(0 * XCAT1 OR 0 * XCAT2)" },
665 { "category:1 AND category:2", "(0 * XCAT1 AND 0 * XCAT2)" },
666 { "foo AND category:2", "(Zfoo@1 AND 0 * XCAT2)" },
667 { "A site:1 site:2", "(a@1 FILTER (H1 OR H2))" },
668 #if 0
669 { "A (site:1 OR site:2)", "(a@1 FILTER (H1 OR H2))" },
670 #endif
671 { "A site:1 site2:2", "(a@1 FILTER (H1 AND J2))" },
672 { "A site:1 site:2 site2:2", "(a@1 FILTER ((H1 OR H2) AND J2))" },
673 #if 0
674 { "A site:1 OR site:2", "(a@1 FILTER (H1 OR H2))" },
675 #endif
676 { "A site:1 AND site:2", "((a@1 FILTER H1) AND 0 * H2)" },
677 { "site:xapian.org OR site:www.xapian.org", "(0 * Hxapian.org OR 0 * Hwww.xapian.org)" },
678 { "site:xapian.org site:www.xapian.org", "0 * (Hxapian.org OR Hwww.xapian.org)" },
679 { "site:xapian.org AND site:www.xapian.org", "(0 * Hxapian.org AND 0 * Hwww.xapian.org)" },
680 { "Xapian site:xapian.org site:www.xapian.org", "(xapian@1 FILTER (Hxapian.org OR Hwww.xapian.org))" },
681 { "author:richard author:olly writer:charlie", "(ZArichard@1 OR ZAolli@2 OR ZAcharli@3)"},
682 { "author:richard NEAR title:book", "(Arichard@1 NEAR 11 XTbook@2)"},
683 { "authortitle:richard NEAR title:book", "((Arichard@1 OR XTrichard@1) NEAR 11 XTbook@2)" },
684 { "multisite:xapian.org", "0 * (Hxapian.org OR Jxapian.org)"},
685 { "authortitle:richard", "(ZArichard@1 OR ZXTrichard@1)"},
686 { "multisite:xapian.org site:www.xapian.org author:richard authortitle:richard", "((ZArichard@1 OR (ZArichard@2 OR ZXTrichard@2)) FILTER (Hwww.xapian.org AND (Hxapian.org OR Jxapian.org)))" },
687 { "authortitle:richard-boulton", "((Arichard@1 PHRASE 2 Aboulton@2) OR (XTrichard@1 PHRASE 2 XTboulton@2))"},
688 { "authortitle:\"richard boulton\"", "((Arichard@1 PHRASE 2 Aboulton@2) OR (XTrichard@1 PHRASE 2 XTboulton@2))"},
689 // Test FLAG_CJK_NGRAM isn't on by default:
690 { "久有归天愿", "Z久有归天愿@1" },
691 { NULL, "CJK" }, // Enable FLAG_CJK_NGRAM
692 // Test non-CJK queries still parse the same:
693 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
694 { "“curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
695 // Test n-gram generation:
696 { "久有归天愿", "(久@1 AND 久有@1 AND 有@1 AND 有归@1 AND 归@1 AND 归天@1 AND 天@1 AND 天愿@1 AND 愿@1)" },
697 { "久有 归天愿", "((久@1 AND 久有@1 AND 有@1) OR (归@2 AND 归天@2 AND 天@2 AND 天愿@2 AND 愿@2))" },
698 { "久有!归天愿", "((久@1 AND 久有@1 AND 有@1) OR (归@2 AND 归天@2 AND 天@2 AND 天愿@2 AND 愿@2))" },
699 { "title:久有 归 天愿", "(((XT久@1 AND XT久有@1 AND XT有@1) OR 归@2) OR (天@3 AND 天愿@3 AND 愿@3))" },
700 { "h众ello万众", "(((Zh@1 OR 众@2) OR Zello@3) OR (万@4 AND 万众@4 AND 众@4))" },
701 { "世(の中)TEST_tm", "((世@1 OR (の@2 AND の中@2 AND 中@2)) OR test_tm@3)" },
702 { "다녀 AND 와야", "((다@1 AND 다녀@1 AND 녀@1) AND (와@2 AND 와야@2 AND 야@2))" },
703 { "authortitle:학술 OR 연구를", "((A학@1 AND XT학@1 AND A학술@1 AND XT학술@1 AND A술@1 AND XT술@1) OR (연@2 AND 연구@2 AND 구@2 AND 구를@2 AND 를@2))" },
704 // FIXME: These should really filter by bigrams to accelerate:
705 { "\"久有归\"", "(久@1 PHRASE 3 有@1 PHRASE 3 归@1)" },
706 { "\"久有test归\"", "(久@1 PHRASE 4 有@1 PHRASE 4 test@2 PHRASE 4 归@3)" },
707 // FIXME: this should work: { "久 NEAR 有", "(久@1 NEAR 11 有@2)" },
708 { NULL, NULL }
711 static bool test_queryparser1()
713 Xapian::QueryParser queryparser;
714 queryparser.set_stemmer(Xapian::Stem("english"));
715 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
716 queryparser.add_prefix("author", "A");
717 queryparser.add_prefix("writer", "A");
718 queryparser.add_prefix("title", "XT");
719 queryparser.add_prefix("subject", "XT");
720 queryparser.add_prefix("authortitle", "A");
721 queryparser.add_prefix("authortitle", "XT");
722 queryparser.add_boolean_prefix("site", "H");
723 queryparser.add_boolean_prefix("site2", "J");
724 queryparser.add_boolean_prefix("multisite", "H");
725 queryparser.add_boolean_prefix("multisite", "J");
726 queryparser.add_boolean_prefix("category", "XCAT", false);
727 TEST_EXCEPTION(Xapian::InvalidOperationError,
728 queryparser.add_boolean_prefix("authortitle", "B");
730 TEST_EXCEPTION(Xapian::InvalidOperationError,
731 queryparser.add_prefix("multisite", "B");
733 unsigned flags = queryparser.FLAG_DEFAULT;
734 for (const test *p = test_or_queries; ; ++p) {
735 if (!p->query) {
736 if (!p->expect) break;
737 if (strcmp(p->expect, "CJK") == 0) {
738 flags = queryparser.FLAG_DEFAULT|queryparser.FLAG_CJK_NGRAM;
739 continue;
741 FAIL_TEST(string("Unknown flag code: ") + p->expect);
743 string expect, parsed;
744 if (p->expect)
745 expect = p->expect;
746 else
747 expect = "parse error";
748 try {
749 Xapian::Query qobj = queryparser.parse_query(p->query, flags);
750 parsed = qobj.get_description();
751 expect = string("Query(") + expect + ')';
752 } catch (const Xapian::QueryParserError &e) {
753 parsed = e.get_msg();
754 } catch (const Xapian::Error &e) {
755 parsed = e.get_description();
756 } catch (...) {
757 parsed = "Unknown exception!";
759 tout << "Query: " << p->query << '\n';
760 TEST_STRINGS_EQUAL(parsed, expect);
762 return true;
765 static const test test_and_queries[] = {
766 { "internet explorer title:(http www)", "((Zinternet@1 AND Zexplor@2) AND (ZXThttp@3 AND ZXTwww@4))" },
767 // Regression test for bug in 0.9.2 and earlier - this would give
768 // (two@2 AND_MAYBE (one@1 AND three@3))
769 { "one +two three", "((Zone@1 AND Ztwo@2) AND Zthree@3)" },
770 { "hello -title:\"hello world\"", "(Zhello@1 AND_NOT (XThello@2 PHRASE 2 XTworld@3))" },
771 // Regression test for bug fixed in 1.0.4 - the '-' would be ignored there
772 // because the whitespace after the '"' wasn't noticed.
773 { "\"hello world\" -C++", "((hello@1 PHRASE 2 world@2) AND_NOT c++@3)" },
774 // Regression tests for bug fixed in 1.0.4 - queries with only boolean
775 // filter and HATE terms weren't accepted.
776 { "-cup site:world", "(0 * Hworld AND_NOT Zcup@1)" },
777 { "site:world -cup", "(0 * Hworld AND_NOT Zcup@1)" },
778 // Regression test for bug fixed in 1.0.4 - the KET token for ')' was lost.
779 { "(site:world) -cup", "(0 * Hworld AND_NOT Zcup@1)" },
780 // Regression test for bug fixed in 1.0.4 - a boolean filter term between
781 // probabilistic terms caused a parse error (probably broken during the
782 // addition of synonym support in 1.0.2).
783 { "foo site:xapian.org bar", "((Zfoo@1 AND Zbar@2) FILTER Hxapian.org)" },
784 // Add coverage for other cases similar to the above.
785 { "a b site:xapian.org", "((Za@1 AND Zb@2) FILTER Hxapian.org)" },
786 { "site:xapian.org a b", "((Za@1 AND Zb@2) FILTER Hxapian.org)" },
787 { NULL, "CJK" }, // Enable FLAG_CJK_NGRAM
788 // Test n-gram generation:
789 { "author:험가 OR subject:万众 hello world!", "((A험@1 AND A험가@1 AND A가@1) OR ((XT万@2 AND XT万众@2 AND XT众@2) AND (Zhello@3 AND Zworld@4)))" },
790 { "洛伊one儿差点two脸three", "((((((洛@1 AND 洛伊@1 AND 伊@1) AND Zone@2) AND (儿@3 AND 儿差@3 AND 差@3 AND 差点@3 AND 点@3)) AND Ztwo@4) AND 脸@5) AND Zthree@6)" },
791 { NULL, NULL }
794 // With default_op = OP_AND.
795 static bool test_qp_default_op1()
797 Xapian::QueryParser queryparser;
798 queryparser.set_stemmer(Xapian::Stem("english"));
799 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
800 queryparser.add_prefix("author", "A");
801 queryparser.add_prefix("title", "XT");
802 queryparser.add_prefix("subject", "XT");
803 queryparser.add_boolean_prefix("site", "H");
804 queryparser.set_default_op(Xapian::Query::OP_AND);
805 unsigned flags = queryparser.FLAG_DEFAULT;
806 for (const test *p = test_and_queries; ; ++p) {
807 if (!p->query) {
808 if (!p->expect) break;
809 if (strcmp(p->expect, "CJK") == 0) {
810 flags = queryparser.FLAG_DEFAULT|queryparser.FLAG_CJK_NGRAM;
811 continue;
813 FAIL_TEST(string("Unknown flag code: ") + p->expect);
815 string expect, parsed;
816 if (p->expect)
817 expect = p->expect;
818 else
819 expect = "parse error";
820 try {
821 Xapian::Query qobj = queryparser.parse_query(p->query, flags);
822 parsed = qobj.get_description();
823 expect = string("Query(") + expect + ')';
824 } catch (const Xapian::QueryParserError &e) {
825 parsed = e.get_msg();
826 } catch (const Xapian::Error &e) {
827 parsed = e.get_description();
828 } catch (...) {
829 parsed = "Unknown exception!";
831 tout << "Query: " << p->query << '\n';
832 TEST_STRINGS_EQUAL(parsed, expect);
834 return true;
837 // Feature test for specify the default prefix (new in Xapian 1.0.0).
838 static bool test_qp_default_prefix1()
840 Xapian::QueryParser qp;
841 qp.set_stemmer(Xapian::Stem("english"));
842 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
843 qp.add_prefix("title", "XT");
845 Xapian::Query qobj;
846 qobj = qp.parse_query("hello world", 0, "A");
847 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAhello@1 OR ZAworld@2))");
848 qobj = qp.parse_query("me title:stuff", 0, "A");
849 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAme@1 OR ZXTstuff@2))");
850 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "A");
851 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZAme@2))");
852 qobj = qp.parse_query("英国 title:文森hello", qp.FLAG_CJK_NGRAM, "A");
853 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((((A英@1 AND A英国@1 AND A国@1) OR (XT文@2 AND XT文森@2 AND XT森@2)) OR ZAhello@3))");
854 return true;
857 // Feature test for setting the default prefix with add_prefix()
858 // (new in Xapian 1.0.3).
859 static bool test_qp_default_prefix2()
861 Xapian::QueryParser qp;
862 qp.set_stemmer(Xapian::Stem("english"));
863 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
865 // test that default prefixes can only be set with add_prefix().
866 TEST_EXCEPTION(Xapian::UnimplementedError,
867 qp.add_boolean_prefix("", "B");
870 qp.add_prefix("title", "XT");
871 qp.add_prefix("", "A");
873 Xapian::Query qobj;
874 qobj = qp.parse_query("hello world", 0);
875 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAhello@1 OR ZAworld@2))");
876 qobj = qp.parse_query("me title:stuff", 0);
877 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAme@1 OR ZXTstuff@2))");
878 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN);
879 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZAme@2))");
881 qobj = qp.parse_query("hello world", 0, "B");
882 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZBhello@1 OR ZBworld@2))");
883 qobj = qp.parse_query("me title:stuff", 0, "B");
884 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZBme@1 OR ZXTstuff@2))");
885 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "B");
886 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZBme@2))");
888 qp.add_prefix("", "B");
889 qobj = qp.parse_query("me-us title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN);
890 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((((Ame@1 PHRASE 2 Aus@2) OR (Bme@1 PHRASE 2 Bus@2)) OR ZXTstuff@3) OR (ZAme@4 OR ZBme@4)))");
891 qobj = qp.parse_query("me-us title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "C");
892 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((((Cme@1 PHRASE 2 Cus@2) OR ZXTstuff@3) OR ZCme@4))");
894 qobj = qp.parse_query("me-us title:\"not-me\"", Xapian::QueryParser::FLAG_PHRASE);
895 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((((Ame@1 PHRASE 2 Aus@2) OR (Bme@1 PHRASE 2 Bus@2)) OR (XTnot@3 PHRASE 2 XTme@4)))");
896 return true;
899 // Test query with odd characters in.
900 static bool test_qp_odd_chars1()
902 Xapian::QueryParser qp;
903 string query("\x01weird\x00stuff\x7f", 13);
904 Xapian::Query qobj = qp.parse_query(query);
905 tout << "Query: " << query << '\n';
906 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((weird@1 OR stuff@2))"); // FIXME: should these be stemmed?
907 return true;
910 // Test right truncation.
911 static bool test_qp_flag_wildcard1()
913 #ifndef XAPIAN_HAS_INMEMORY_BACKEND
914 SKIP_TEST("Testcase requires the InMemory backend which is disabled");
915 #else
916 Xapian::WritableDatabase db(Xapian::InMemory::open());
917 Xapian::Document doc;
918 doc.add_term("abc");
919 doc.add_term("main");
920 doc.add_term("muscat");
921 doc.add_term("muscle");
922 doc.add_term("musclebound");
923 doc.add_term("muscular");
924 doc.add_term("mutton");
925 db.add_document(doc);
926 Xapian::QueryParser qp;
927 qp.set_database(db);
928 Xapian::Query qobj = qp.parse_query("ab*", Xapian::QueryParser::FLAG_WILDCARD);
929 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR ab))");
930 qobj = qp.parse_query("muscle*", Xapian::QueryParser::FLAG_WILDCARD);
931 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR muscle))");
932 qobj = qp.parse_query("meat*", Xapian::QueryParser::FLAG_WILDCARD);
933 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR meat))");
934 qobj = qp.parse_query("musc*", Xapian::QueryParser::FLAG_WILDCARD);
935 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR musc))");
936 qobj = qp.parse_query("mutt*", Xapian::QueryParser::FLAG_WILDCARD);
937 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR mutt))");
938 // Regression test (we weren't lowercasing terms before checking if they
939 // were in the database or not):
940 qobj = qp.parse_query("mUTTON++");
941 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(mutton@1)");
942 // Regression test: check that wildcards work with +terms.
943 unsigned flags = Xapian::QueryParser::FLAG_WILDCARD |
944 Xapian::QueryParser::FLAG_LOVEHATE;
945 qobj = qp.parse_query("+mai* main", flags);
946 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR mai) AND_MAYBE main@2))");
947 // Regression test (if we had a +term which was a wildcard and wasn't
948 // present, the query could still match documents).
949 qobj = qp.parse_query("foo* main", flags);
950 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) OR main@2))");
951 qobj = qp.parse_query("main foo*", flags);
952 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 OR (SYNONYM WILDCARD OR foo)))");
953 qobj = qp.parse_query("+foo* main", flags);
954 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE main@2))");
955 qobj = qp.parse_query("main +foo*", flags);
956 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE main@1))");
957 qobj = qp.parse_query("foo* +main", flags);
958 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@2 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
959 qobj = qp.parse_query("+main foo*", flags);
960 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
961 qobj = qp.parse_query("+foo* +main", flags);
962 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND main@2))");
963 qobj = qp.parse_query("+main +foo*", flags);
964 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND (SYNONYM WILDCARD OR foo)))");
965 qobj = qp.parse_query("foo* mai", flags);
966 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) OR mai@2))");
967 qobj = qp.parse_query("mai foo*", flags);
968 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@1 OR (SYNONYM WILDCARD OR foo)))");
969 qobj = qp.parse_query("+foo* mai", flags);
970 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE mai@2))");
971 qobj = qp.parse_query("mai +foo*", flags);
972 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE mai@1))");
973 qobj = qp.parse_query("foo* +mai", flags);
974 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@2 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
975 qobj = qp.parse_query("+mai foo*", flags);
976 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@1 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
977 qobj = qp.parse_query("+foo* +mai", flags);
978 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND mai@2))");
979 qobj = qp.parse_query("+mai +foo*", flags);
980 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@1 AND (SYNONYM WILDCARD OR foo)))");
981 qobj = qp.parse_query("-foo* main", flags);
982 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@2 AND_NOT (SYNONYM WILDCARD OR foo)))");
983 qobj = qp.parse_query("main -foo*", flags);
984 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT (SYNONYM WILDCARD OR foo)))");
985 qobj = qp.parse_query("main -foo* -bar", flags);
986 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT ((SYNONYM WILDCARD OR foo) OR bar@3)))");
987 qobj = qp.parse_query("main -bar -foo*", flags);
988 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT (bar@2 OR (SYNONYM WILDCARD OR foo))))");
989 // Check with OP_AND too.
990 qp.set_default_op(Xapian::Query::OP_AND);
991 qobj = qp.parse_query("foo* main", flags);
992 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND main@2))");
993 qobj = qp.parse_query("main foo*", flags);
994 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND (SYNONYM WILDCARD OR foo)))");
995 qp.set_default_op(Xapian::Query::OP_AND);
996 qobj = qp.parse_query("+foo* main", flags);
997 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND main@2))");
998 qobj = qp.parse_query("main +foo*", flags);
999 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND (SYNONYM WILDCARD OR foo)))");
1000 qobj = qp.parse_query("-foo* main", flags);
1001 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@2 AND_NOT (SYNONYM WILDCARD OR foo)))");
1002 qobj = qp.parse_query("main -foo*", flags);
1003 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT (SYNONYM WILDCARD OR foo)))");
1004 // Check empty wildcard followed by negation.
1005 qobj = qp.parse_query("foo* -main", Xapian::QueryParser::FLAG_LOVEHATE|Xapian::QueryParser::FLAG_WILDCARD);
1006 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_NOT main@2))");
1007 // Regression test for bug#484 fixed in 1.2.1 and 1.0.21.
1008 qobj = qp.parse_query("abc muscl* main", flags);
1009 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((abc@1 AND (SYNONYM WILDCARD OR muscl)) AND main@3))");
1010 return true;
1011 #endif
1014 // Test right truncation with prefixes.
1015 static bool test_qp_flag_wildcard2()
1017 #ifndef XAPIAN_HAS_INMEMORY_BACKEND
1018 SKIP_TEST("Testcase requires the InMemory backend which is disabled");
1019 #else
1020 Xapian::WritableDatabase db(Xapian::InMemory::open());
1021 Xapian::Document doc;
1022 doc.add_term("Aheinlein");
1023 doc.add_term("Ahuxley");
1024 doc.add_term("hello");
1025 db.add_document(doc);
1026 Xapian::QueryParser qp;
1027 qp.set_database(db);
1028 qp.add_prefix("author", "A");
1029 Xapian::Query qobj;
1030 qobj = qp.parse_query("author:h*", Xapian::QueryParser::FLAG_WILDCARD);
1031 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR Ah))");
1032 qobj = qp.parse_query("author:h* test", Xapian::QueryParser::FLAG_WILDCARD);
1033 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR Ah) OR test@2))");
1034 return true;
1035 #endif
1038 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
1039 static void
1040 test_qp_flag_wildcard1_helper(const Xapian::Database &db,
1041 Xapian::termcount max_expansion,
1042 const string & query_string)
1044 Xapian::QueryParser qp;
1045 qp.set_database(db);
1046 qp.set_max_expansion(max_expansion);
1047 Xapian::Enquire e(db);
1048 e.set_query(qp.parse_query(query_string, Xapian::QueryParser::FLAG_WILDCARD));
1049 // The exception for expanding too much may happen at parse time or later
1050 // so we need to calculate the MSet too.
1051 e.get_mset(0, 10);
1053 #endif
1055 // Test right truncation with a limit on expansion.
1056 static bool test_qp_flag_wildcard3()
1058 #ifndef XAPIAN_HAS_INMEMORY_BACKEND
1059 SKIP_TEST("Testcase requires the InMemory backend which is disabled");
1060 #else
1061 Xapian::WritableDatabase db(Xapian::InMemory::open());
1062 Xapian::Document doc;
1063 doc.add_term("abc");
1064 doc.add_term("main");
1065 doc.add_term("muscat");
1066 doc.add_term("muscle");
1067 doc.add_term("musclebound");
1068 doc.add_term("muscular");
1069 doc.add_term("mutton");
1070 db.add_document(doc);
1072 // Test that a max of 0 doesn't set a limit.
1073 test_qp_flag_wildcard1_helper(db, 0, "z*");
1074 test_qp_flag_wildcard1_helper(db, 0, "m*");
1076 // These cases should expand to the limit given.
1077 test_qp_flag_wildcard1_helper(db, 1, "z*");
1078 test_qp_flag_wildcard1_helper(db, 1, "ab*");
1079 test_qp_flag_wildcard1_helper(db, 2, "muscle*");
1080 test_qp_flag_wildcard1_helper(db, 4, "musc*");
1081 test_qp_flag_wildcard1_helper(db, 4, "mus*");
1082 test_qp_flag_wildcard1_helper(db, 5, "mu*");
1083 test_qp_flag_wildcard1_helper(db, 6, "m*");
1085 // These cases should expand to one more than the limit.
1086 TEST_EXCEPTION(Xapian::WildcardError,
1087 test_qp_flag_wildcard1_helper(db, 1, "muscle*"));
1088 TEST_EXCEPTION(Xapian::WildcardError,
1089 test_qp_flag_wildcard1_helper(db, 3, "musc*"));
1090 TEST_EXCEPTION(Xapian::WildcardError,
1091 test_qp_flag_wildcard1_helper(db, 3, "mus*"));
1092 TEST_EXCEPTION(Xapian::WildcardError,
1093 test_qp_flag_wildcard1_helper(db, 4, "mu*"));
1094 TEST_EXCEPTION(Xapian::WildcardError,
1095 test_qp_flag_wildcard1_helper(db, 5, "m*"));
1097 return true;
1098 #endif
1101 // Test partial queries.
1102 static bool test_qp_flag_partial1()
1104 #ifndef XAPIAN_HAS_INMEMORY_BACKEND
1105 SKIP_TEST("Testcase requires the InMemory backend which is disabled");
1106 #else
1107 Xapian::WritableDatabase db(Xapian::InMemory::open());
1108 Xapian::Document doc;
1109 Xapian::Stem stemmer("english");
1110 doc.add_term("abc");
1111 doc.add_term("main");
1112 doc.add_term("muscat");
1113 doc.add_term("muscle");
1114 doc.add_term("musclebound");
1115 doc.add_term("muscular");
1116 doc.add_term("mutton");
1117 doc.add_term("Z" + stemmer("outside"));
1118 doc.add_term("Z" + stemmer("out"));
1119 doc.add_term("outside");
1120 doc.add_term("out");
1121 doc.add_term("XTcove");
1122 doc.add_term("XTcows");
1123 doc.add_term("XTcowl");
1124 doc.add_term("XTcox");
1125 doc.add_term("ZXTcow");
1126 doc.add_term("XONEpartial");
1127 doc.add_term("XONEpartial2");
1128 doc.add_term("XTWOpartial3");
1129 doc.add_term("XTWOpartial4");
1130 db.add_document(doc);
1131 Xapian::QueryParser qp;
1132 qp.set_database(db);
1133 qp.set_stemmer(stemmer);
1134 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1135 qp.add_prefix("title", "XT");
1136 qp.add_prefix("double", "XONE");
1137 qp.add_prefix("double", "XTWO");
1139 // Check behaviour with unstemmed terms
1140 Xapian::Query qobj = qp.parse_query("a", Xapian::QueryParser::FLAG_PARTIAL);
1141 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR a) OR Za@1))");
1142 qobj = qp.parse_query("ab", Xapian::QueryParser::FLAG_PARTIAL);
1143 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR ab) OR Zab@1))");
1144 qobj = qp.parse_query("muscle", Xapian::QueryParser::FLAG_PARTIAL);
1145 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR muscle) OR Zmuscl@1))");
1146 qobj = qp.parse_query("meat", Xapian::QueryParser::FLAG_PARTIAL);
1147 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR meat) OR Zmeat@1))");
1148 qobj = qp.parse_query("musc", Xapian::QueryParser::FLAG_PARTIAL);
1149 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR musc) OR Zmusc@1))");
1150 qobj = qp.parse_query("mutt", Xapian::QueryParser::FLAG_PARTIAL);
1151 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR mutt) OR Zmutt@1))");
1152 qobj = qp.parse_query("abc musc", Xapian::QueryParser::FLAG_PARTIAL);
1153 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((Zabc@1 OR ((SYNONYM WILDCARD OR musc) OR Zmusc@2)))");
1154 qobj = qp.parse_query("a* mutt", Xapian::QueryParser::FLAG_PARTIAL | Xapian::QueryParser::FLAG_WILDCARD);
1155 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR a) OR ((SYNONYM WILDCARD OR mutt) OR Zmutt@2)))");
1157 // Check behaviour with stemmed terms, and stem strategy STEM_SOME.
1158 qobj = qp.parse_query("o", Xapian::QueryParser::FLAG_PARTIAL);
1159 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR o) OR Zo@1))");
1160 qobj = qp.parse_query("ou", Xapian::QueryParser::FLAG_PARTIAL);
1161 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR ou) OR Zou@1))");
1162 qobj = qp.parse_query("out", Xapian::QueryParser::FLAG_PARTIAL);
1163 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR Zout@1))");
1164 qobj = qp.parse_query("outs", Xapian::QueryParser::FLAG_PARTIAL);
1165 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR Zout@1))");
1166 qobj = qp.parse_query("outsi", Xapian::QueryParser::FLAG_PARTIAL);
1167 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outsi) OR Zoutsi@1))");
1168 qobj = qp.parse_query("outsid", Xapian::QueryParser::FLAG_PARTIAL);
1169 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outsid) OR Zoutsid@1))");
1170 qobj = qp.parse_query("outside", Xapian::QueryParser::FLAG_PARTIAL);
1171 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR Zoutsid@1))");
1173 // Check behaviour with capitalised terms, and stem strategy STEM_SOME.
1174 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1175 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1176 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1177 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR outs@1))");
1178 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1179 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outside@1))");
1180 // FIXME: Used to be this, but we aren't currently doing this change:
1181 // TEST_STRINGS_EQUAL(qobj.get_description(), "Query(outside@1#2)");
1183 // And now with stemming strategy STEM_ALL.
1184 qp.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
1185 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1186 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1187 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1188 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR out@1))");
1189 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1190 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outsid@1))");
1192 // And now with stemming strategy STEM_ALL_Z.
1193 qp.set_stemming_strategy(Xapian::QueryParser::STEM_ALL_Z);
1194 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1195 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR Zout@1))");
1196 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1197 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR Zout@1))");
1198 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1199 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR Zoutsid@1))");
1201 // Check handling of a case with a prefix.
1202 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1203 qobj = qp.parse_query("title:cow", Xapian::QueryParser::FLAG_PARTIAL);
1204 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcow) OR ZXTcow@1))");
1205 qobj = qp.parse_query("title:cows", Xapian::QueryParser::FLAG_PARTIAL);
1206 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcows) OR ZXTcow@1))");
1207 qobj = qp.parse_query("title:Cow", Xapian::QueryParser::FLAG_PARTIAL);
1208 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcow) OR XTcow@1))");
1209 qobj = qp.parse_query("title:Cows", Xapian::QueryParser::FLAG_PARTIAL);
1210 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcows) OR XTcows@1))");
1211 // FIXME: Used to be this, but we aren't currently doing this change:
1212 // TEST_STRINGS_EQUAL(qobj.get_description(), "Query(XTcows@1#2)");
1214 // Regression test - the initial version of the multi-prefix code would
1215 // inflate the wqf of the "parsed as normal" version of a partial term
1216 // by multiplying it by the number of prefixes mapped to.
1217 qobj = qp.parse_query("double:vision", Xapian::QueryParser::FLAG_PARTIAL);
1218 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEvision SYNONYM WILDCARD OR XTWOvision) OR (ZXONEvision@1 SYNONYM ZXTWOvision@1)))");
1220 // Test handling of FLAG_PARTIAL when there's more than one prefix.
1221 qobj = qp.parse_query("double:part", Xapian::QueryParser::FLAG_PARTIAL);
1222 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpart SYNONYM WILDCARD OR XTWOpart) OR (ZXONEpart@1 SYNONYM ZXTWOpart@1)))");
1224 // Test handling of FLAG_PARTIAL when there's more than one prefix, without
1225 // stemming.
1226 qp.set_stemming_strategy(Xapian::QueryParser::STEM_NONE);
1227 qobj = qp.parse_query("double:part", Xapian::QueryParser::FLAG_PARTIAL);
1228 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpart SYNONYM WILDCARD OR XTWOpart) OR (XONEpart@1 SYNONYM XTWOpart@1)))");
1229 qobj = qp.parse_query("double:partial", Xapian::QueryParser::FLAG_PARTIAL);
1230 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpartial SYNONYM WILDCARD OR XTWOpartial) OR (XONEpartial@1 SYNONYM XTWOpartial@1)))");
1232 return true;
1233 #endif
1236 static bool test_qp_flag_bool_any_case1()
1238 using Xapian::QueryParser;
1239 Xapian::QueryParser qp;
1240 Xapian::Query qobj;
1241 qobj = qp.parse_query("to and fro", QueryParser::FLAG_BOOLEAN | QueryParser::FLAG_BOOLEAN_ANY_CASE);
1242 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 AND fro@2))");
1243 qobj = qp.parse_query("to and fro", QueryParser::FLAG_BOOLEAN);
1244 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 OR and@2 OR fro@3))");
1245 // Regression test for bug in 0.9.4 and earlier.
1246 qobj = qp.parse_query("to And fro", QueryParser::FLAG_BOOLEAN | QueryParser::FLAG_BOOLEAN_ANY_CASE);
1247 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 AND fro@2))");
1248 qobj = qp.parse_query("to And fro", QueryParser::FLAG_BOOLEAN);
1249 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 OR and@2 OR fro@3))");
1250 return true;
1253 static const test test_stop_queries[] = {
1254 { "test the queryparser", "(test@1 AND queryparser@3)" },
1255 // Regression test for bug in 0.9.6 and earlier. This would fail to
1256 // parse.
1257 { "test AND the AND queryparser", "((test@1 AND the@2) AND queryparser@3)" },
1258 // 0.9.6 and earlier ignored a stopword even if it was the only term.
1259 // More recent versions don't ever treat a single term as a stopword.
1260 { "the", "the@1" },
1261 // 1.2.2 and earlier ignored an all-stopword query with multiple terms,
1262 // which prevents 'to be or not to be' for being searchable unless the
1263 // user made it into a phrase query or prefixed all terms with '+'
1264 // (ticket#245).
1265 { "an the a", "(an@1 AND the@2 AND a@3)" },
1266 // Regression test for bug in initial version of the patch for the
1267 // "all-stopword" case.
1268 { "the AND a an", "(the@1 AND (a@2 AND an@3))" },
1269 { NULL, NULL }
1272 static bool test_qp_stopper1()
1274 Xapian::QueryParser qp;
1275 const char * stopwords[] = { "a", "an", "the" };
1276 Xapian::SimpleStopper stop(stopwords, stopwords + 3);
1277 qp.set_stopper(&stop);
1278 qp.set_default_op(Xapian::Query::OP_AND);
1279 for (const test *p = test_stop_queries; p->query; ++p) {
1280 string expect, parsed;
1281 if (p->expect)
1282 expect = p->expect;
1283 else
1284 expect = "parse error";
1285 try {
1286 Xapian::Query qobj = qp.parse_query(p->query);
1287 parsed = qobj.get_description();
1288 expect = string("Query(") + expect + ')';
1289 } catch (const Xapian::QueryParserError &e) {
1290 parsed = e.get_msg();
1291 } catch (const Xapian::Error &e) {
1292 parsed = e.get_description();
1293 } catch (...) {
1294 parsed = "Unknown exception!";
1296 tout << "Query: " << p->query << '\n';
1297 TEST_STRINGS_EQUAL(parsed, expect);
1299 return true;
1302 static const test test_pure_not_queries[] = {
1303 { "NOT windows", "(<alldocuments> AND_NOT Zwindow@1)" },
1304 { "a AND (NOT b)", "(Za@1 AND (<alldocuments> AND_NOT Zb@2))" },
1305 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
1306 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
1307 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
1308 { NULL, NULL }
1311 static bool test_qp_flag_pure_not1()
1313 using Xapian::QueryParser;
1314 Xapian::QueryParser qp;
1315 qp.set_stemmer(Xapian::Stem("english"));
1316 qp.set_stemming_strategy(QueryParser::STEM_SOME);
1317 for (const test *p = test_pure_not_queries; p->query; ++p) {
1318 string expect, parsed;
1319 if (p->expect)
1320 expect = p->expect;
1321 else
1322 expect = "parse error";
1323 try {
1324 Xapian::Query qobj = qp.parse_query(p->query,
1325 QueryParser::FLAG_BOOLEAN |
1326 QueryParser::FLAG_PURE_NOT);
1327 parsed = qobj.get_description();
1328 expect = string("Query(") + expect + ')';
1329 } catch (const Xapian::QueryParserError &e) {
1330 parsed = e.get_msg();
1331 } catch (const Xapian::Error &e) {
1332 parsed = e.get_description();
1333 } catch (...) {
1334 parsed = "Unknown exception!";
1336 tout << "Query: " << p->query << '\n';
1337 TEST_STRINGS_EQUAL(parsed, expect);
1339 return true;
1342 // Debatable if this is a regression test or a feature test, as it's not
1343 // obvious is this was a bug fix or a new feature. Either way, it first
1344 // appeared in Xapian 1.0.0.
1345 static bool test_qp_unstem_boolean_prefix()
1347 Xapian::QueryParser qp;
1348 qp.add_boolean_prefix("test", "XTEST");
1349 Xapian::Query q = qp.parse_query("hello test:foo");
1350 TEST_STRINGS_EQUAL(q.get_description(), "Query((hello@1 FILTER XTESTfoo))");
1351 Xapian::TermIterator u = qp.unstem_begin("XTESTfoo");
1352 TEST(u != qp.unstem_end("XTESTfoo"));
1353 TEST_EQUAL(*u, "test:foo");
1354 ++u;
1355 TEST(u == qp.unstem_end("XTESTfoo"));
1356 return true;
1359 static const test test_value_range1_queries[] = {
1360 { "a..b", "0 * VALUE_RANGE 1 a b" },
1361 { "$50..100", "0 * VALUE_RANGE 1 $50 100" },
1362 { "$50..$99", "0 * VALUE_RANGE 1 $50 $99" },
1363 { "$50..$100", "" }, // start > range
1364 { "02/03/1979..10/12/1980", "0 * VALUE_RANGE 1 02/03/1979 10/12/1980" },
1365 { "a..b hello", "(hello@1 FILTER VALUE_RANGE 1 a b)" },
1366 { "hello a..b", "(hello@1 FILTER VALUE_RANGE 1 a b)" },
1367 { "hello a..b world", "((hello@1 OR world@2) FILTER VALUE_RANGE 1 a b)" },
1368 { "hello a..b test:foo", "(hello@1 FILTER (VALUE_RANGE 1 a b AND XTESTfoo))" },
1369 { "hello a..b test:foo test:bar", "(hello@1 FILTER (VALUE_RANGE 1 a b AND (XTESTfoo OR XTESTbar)))" },
1370 { "hello a..b c..d test:foo", "(hello@1 FILTER ((VALUE_RANGE 1 a b OR VALUE_RANGE 1 c d) AND XTESTfoo))" },
1371 { "hello a..b c..d test:foo test:bar", "(hello@1 FILTER ((VALUE_RANGE 1 a b OR VALUE_RANGE 1 c d) AND (XTESTfoo OR XTESTbar)))" },
1372 { "-5..7", "0 * VALUE_RANGE 1 -5 7" },
1373 { "hello -5..7", "(hello@1 FILTER VALUE_RANGE 1 -5 7)" },
1374 { "-5..7 hello", "(hello@1 FILTER VALUE_RANGE 1 -5 7)" },
1375 { "\"time flies\" 09:00..12:30", "((time@1 PHRASE 2 flies@2) FILTER VALUE_RANGE 1 09:00 12:30)" },
1376 // Feature test for single-ended ranges (ticket#480):
1377 { "..b", "0 * VALUE_LE 1 b" },
1378 { "a..", "0 * VALUE_GE 1 a" },
1379 // Test for expanded set of characters allowed in range start:
1380 { "10:30+1300..11:00+1300", "0 * VALUE_RANGE 1 10:30+1300 11:00+1300" },
1381 { NULL, NULL }
1384 // Simple test of ValueRangeProcessor class.
1385 static bool test_qp_value_range1()
1387 Xapian::QueryParser qp;
1388 qp.add_boolean_prefix("test", "XTEST");
1389 Xapian::StringValueRangeProcessor vrp(1);
1390 qp.add_valuerangeprocessor(&vrp);
1391 for (const test *p = test_value_range1_queries; p->query; ++p) {
1392 string expect, parsed;
1393 if (p->expect)
1394 expect = p->expect;
1395 else
1396 expect = "parse error";
1397 try {
1398 Xapian::Query qobj = qp.parse_query(p->query);
1399 parsed = qobj.get_description();
1400 expect = string("Query(") + expect + ')';
1401 } catch (const Xapian::QueryParserError &e) {
1402 parsed = e.get_msg();
1403 } catch (const Xapian::Error &e) {
1404 parsed = e.get_description();
1405 } catch (...) {
1406 parsed = "Unknown exception!";
1408 tout << "Query: " << p->query << '\n';
1409 TEST_STRINGS_EQUAL(parsed, expect);
1411 return true;
1414 static const test test_value_range2_queries[] = {
1415 { "a..b", "0 * VALUE_RANGE 3 a b" },
1416 { "1..12", "0 * VALUE_RANGE 2 \\xa0 \\xae" },
1417 { "20070201..20070228", "0 * VALUE_RANGE 1 20070201 20070228" },
1418 { "$10..20", "0 * VALUE_RANGE 4 \\xad \\xb1" },
1419 { "$10..$20", "0 * VALUE_RANGE 4 \\xad \\xb1" },
1420 // Feature test for single-ended ranges (ticket#480):
1421 { "$..20", "0 * VALUE_LE 4 \\xb1" },
1422 { "..$20", "0 * VALUE_LE 3 $20" }, // FIXME: probably should parse as $..20
1423 { "$10..", "0 * VALUE_GE 4 \\xad" },
1424 { "12..42kg", "0 * VALUE_RANGE 5 \\xae \\xb5@" },
1425 { "12kg..42kg", "0 * VALUE_RANGE 5 \\xae \\xb5@" },
1426 { "12kg..42", "0 * VALUE_RANGE 3 12kg 42" },
1427 { "10..$20", "" }, // start > end
1428 { "1999-03-12..2020-12-30", "0 * VALUE_RANGE 1 19990312 20201230" },
1429 { "1999/03/12..2020/12/30", "0 * VALUE_RANGE 1 19990312 20201230" },
1430 { "1999.03.12..2020.12.30", "0 * VALUE_RANGE 1 19990312 20201230" },
1431 // Feature test for single-ended ranges (ticket#480):
1432 { "..2020.12.30", "0 * VALUE_LE 1 20201230" },
1433 { "1999.03.12..", "0 * VALUE_GE 1 19990312" },
1434 { "12/03/99..12/04/01", "0 * VALUE_RANGE 1 19990312 20010412" },
1435 { "03-12-99..04-14-01", "0 * VALUE_RANGE 1 19990312 20010414" },
1436 { "(test:a..test:b hello)", "(hello@1 FILTER VALUE_RANGE 3 test:a test:b)" },
1437 { "12..42kg 5..6kg 1..12", "0 * (VALUE_RANGE 2 \\xa0 \\xae AND (VALUE_RANGE 5 \\xae \\xb5@ OR VALUE_RANGE 5 \\xa9 \\xaa))" },
1438 // Check that a VRP which fails to match doesn't remove a prefix or suffix.
1439 // 1.0.13/1.1.1 and earlier got this wrong in some cases.
1440 { "$12a..13", "0 * VALUE_RANGE 3 $12a 13" },
1441 { "$12..13b", "0 * VALUE_RANGE 3 $12 13b" },
1442 { "$12..12kg", "0 * VALUE_RANGE 3 $12 12kg" },
1443 { "12..b12kg", "0 * VALUE_RANGE 3 12 b12kg" },
1444 { NULL, NULL }
1447 // Test chaining of ValueRangeProcessor classes.
1448 static bool test_qp_value_range2()
1450 Xapian::QueryParser qp;
1451 qp.add_boolean_prefix("test", "XTEST");
1452 Xapian::DateValueRangeProcessor vrp_date(1);
1453 Xapian::NumberValueRangeProcessor vrp_num(2);
1454 Xapian::StringValueRangeProcessor vrp_str(3);
1455 Xapian::NumberValueRangeProcessor vrp_cash(4, "$");
1456 Xapian::NumberValueRangeProcessor vrp_weight(5, "kg", false);
1457 qp.add_valuerangeprocessor(&vrp_date);
1458 qp.add_valuerangeprocessor(&vrp_num);
1459 qp.add_valuerangeprocessor(&vrp_cash);
1460 qp.add_valuerangeprocessor(&vrp_weight);
1461 qp.add_valuerangeprocessor(&vrp_str);
1462 for (const test *p = test_value_range2_queries; p->query; ++p) {
1463 string expect, parsed;
1464 if (p->expect)
1465 expect = p->expect;
1466 else
1467 expect = "parse error";
1468 try {
1469 Xapian::Query qobj = qp.parse_query(p->query);
1470 parsed = qobj.get_description();
1471 expect = string("Query(") + expect + ')';
1472 } catch (const Xapian::QueryParserError &e) {
1473 parsed = e.get_msg();
1474 } catch (const Xapian::Error &e) {
1475 parsed = e.get_description();
1476 } catch (...) {
1477 parsed = "Unknown exception!";
1479 tout << "Query: " << p->query << '\n';
1480 TEST_STRINGS_EQUAL(parsed, expect);
1482 return true;
1485 // Test NumberValueRangeProcessors with actual data.
1486 static bool test_qp_value_range3()
1488 #ifndef XAPIAN_HAS_INMEMORY_BACKEND
1489 SKIP_TEST("Testcase requires the InMemory backend which is disabled");
1490 #else
1491 Xapian::WritableDatabase db(Xapian::InMemory::open());
1492 double low = -10;
1493 int steps = 60;
1494 double step = 0.5;
1496 for (int i = 0; i <= steps; ++i) {
1497 double v = low + i * step;
1498 Xapian::Document doc;
1499 doc.add_value(1, Xapian::sortable_serialise(v));
1500 db.add_document(doc);
1503 Xapian::NumberValueRangeProcessor vrp_num(1);
1504 Xapian::QueryParser qp;
1505 qp.add_valuerangeprocessor(&vrp_num);
1507 for (int j = 0; j <= steps; ++j) {
1508 double start = low + j * step;
1509 for (int k = 0; k <= steps; ++k) {
1510 double end = low + k * step;
1511 string query = str(start) + ".." + str(end);
1512 tout << "Query: " << query << '\n';
1513 Xapian::Query qobj = qp.parse_query(query);
1514 Xapian::Enquire enq(db);
1515 enq.set_query(qobj);
1516 Xapian::MSet mset = enq.get_mset(0, steps + 1);
1517 if (end < start) {
1518 TEST_EQUAL(mset.size(), 0);
1519 } else {
1520 TEST_EQUAL(mset.size(), 1u + (k - j));
1521 for (unsigned int m = 0; m != mset.size(); ++m) {
1522 double v = start + m * step;
1523 TEST_EQUAL(mset[m].get_document().get_value(1),
1524 Xapian::sortable_serialise(v));
1529 return true;
1530 #endif
1533 static const test test_value_range4_queries[] = {
1534 { "id:19254@foo..example.com", "0 * Q19254@foo..example.com" },
1535 { "hello:world", "0 * XHELLOworld" },
1536 { "hello:mum..world", "0 * VALUE_RANGE 1 mum world" },
1537 { NULL, NULL }
1540 /** Test a boolean filter which happens to contain "..".
1542 * Regression test for bug fixed in 1.2.3.
1544 * Also test that the same prefix can be set for a valuerange and filter.
1546 static bool test_qp_value_range4()
1548 Xapian::QueryParser qp;
1549 qp.add_boolean_prefix("id", "Q");
1550 qp.add_boolean_prefix("hello", "XHELLO");
1551 Xapian::StringValueRangeProcessor vrp_str(1, "hello:");
1552 qp.add_valuerangeprocessor(&vrp_str);
1553 for (const test *p = test_value_range4_queries; p->query; ++p) {
1554 string expect, parsed;
1555 if (p->expect)
1556 expect = p->expect;
1557 else
1558 expect = "parse error";
1559 try {
1560 Xapian::Query qobj = qp.parse_query(p->query);
1561 parsed = qobj.get_description();
1562 expect = string("Query(") + expect + ')';
1563 } catch (const Xapian::QueryParserError &e) {
1564 parsed = e.get_msg();
1565 } catch (const Xapian::Error &e) {
1566 parsed = e.get_description();
1567 } catch (...) {
1568 parsed = "Unknown exception!";
1570 tout << "Query: " << p->query << '\n';
1571 TEST_STRINGS_EQUAL(parsed, expect);
1573 return true;
1577 static const test test_value_daterange1_queries[] = {
1578 { "12/03/99..12/04/01", "0 * VALUE_RANGE 1 19991203 20011204" },
1579 { "03-12-99..04-14-01", "0 * VALUE_RANGE 1 19990312 20010414" },
1580 { "01/30/60..02/02/59", "0 * VALUE_RANGE 1 19600130 20590202" },
1581 { "1999-03-12..2001-04-14", "0 * VALUE_RANGE 1 19990312 20010414" },
1582 { "12/03/99..02", "Unknown range operation" },
1583 { "1999-03-12..2001", "Unknown range operation" },
1584 { NULL, NULL }
1587 // Test DateValueRangeProcessor
1588 static bool test_qp_value_daterange1()
1590 Xapian::QueryParser qp;
1591 Xapian::DateValueRangeProcessor vrp_date(1, true, 1960);
1592 qp.add_valuerangeprocessor(&vrp_date);
1593 for (const test *p = test_value_daterange1_queries; p->query; ++p) {
1594 string expect, parsed;
1595 if (p->expect)
1596 expect = p->expect;
1597 else
1598 expect = "parse error";
1599 try {
1600 Xapian::Query qobj = qp.parse_query(p->query);
1601 parsed = qobj.get_description();
1602 expect = string("Query(") + expect + ')';
1603 } catch (const Xapian::QueryParserError &e) {
1604 parsed = e.get_msg();
1605 } catch (const Xapian::Error &e) {
1606 parsed = e.get_description();
1607 } catch (...) {
1608 parsed = "Unknown exception!";
1610 tout << "Query: " << p->query << '\n';
1611 TEST_STRINGS_EQUAL(parsed, expect);
1613 return true;
1616 static const test test_value_daterange2_queries[] = {
1617 { "created:12/03/99..12/04/01", "0 * VALUE_RANGE 1 19991203 20011204" },
1618 { "modified:03-12-99..04-14-01", "0 * VALUE_RANGE 2 19990312 20010414" },
1619 { "accessed:01/30/70..02/02/69", "0 * VALUE_RANGE 3 19700130 20690202" },
1620 // In <=1.2.12, and in 1.3.0, this gave "Unknown range operation":
1621 { "deleted:12/03/99..12/04/01", "0 * VALUE_RANGE 4 19990312 20010412" },
1622 { "1999-03-12..2001-04-14", "Unknown range operation" },
1623 { "12/03/99..created:12/04/01", "Unknown range operation" },
1624 { "12/03/99created:..12/04/01", "Unknown range operation" },
1625 { "12/03/99..12/04/01created:", "Unknown range operation" },
1626 { "12/03/99..02", "Unknown range operation" },
1627 { "1999-03-12..2001", "Unknown range operation" },
1628 { NULL, NULL }
1631 // Feature test DateValueRangeProcessor with prefixes (added in 1.1.2).
1632 static bool test_qp_value_daterange2()
1634 Xapian::QueryParser qp;
1635 Xapian::DateValueRangeProcessor vrp_cdate(1, "created:", true, true, 1970);
1636 Xapian::DateValueRangeProcessor vrp_mdate(2, "modified:", true, true, 1970);
1637 Xapian::DateValueRangeProcessor vrp_adate(3, "accessed:", true, true, 1970);
1638 // Regression test - here a const char * was taken as a bool rather than a
1639 // std::string when resolving the overloaded forms. Fixed in 1.2.13 and
1640 // 1.3.1.
1641 Xapian::DateValueRangeProcessor vrp_ddate(4, "deleted:");
1642 qp.add_valuerangeprocessor(&vrp_cdate);
1643 qp.add_valuerangeprocessor(&vrp_mdate);
1644 qp.add_valuerangeprocessor(&vrp_adate);
1645 qp.add_valuerangeprocessor(&vrp_ddate);
1646 for (const test *p = test_value_daterange2_queries; p->query; ++p) {
1647 string expect, parsed;
1648 if (p->expect)
1649 expect = p->expect;
1650 else
1651 expect = "parse error";
1652 try {
1653 Xapian::Query qobj = qp.parse_query(p->query);
1654 parsed = qobj.get_description();
1655 expect = string("Query(") + expect + ')';
1656 } catch (const Xapian::QueryParserError &e) {
1657 parsed = e.get_msg();
1658 } catch (const Xapian::Error &e) {
1659 parsed = e.get_description();
1660 } catch (...) {
1661 parsed = "Unknown exception!";
1663 tout << "Query: " << p->query << '\n';
1664 TEST_STRINGS_EQUAL(parsed, expect);
1666 return true;
1669 static const test test_value_stringrange1_queries[] = {
1670 { "tag:bar..foo", "0 * VALUE_RANGE 1 bar foo" },
1671 { "bar..foo", "0 * VALUE_RANGE 0 bar foo" },
1672 { NULL, NULL }
1675 // Feature test StringValueRangeProcessor with prefixes (added in 1.1.2).
1676 static bool test_qp_value_stringrange1()
1678 Xapian::QueryParser qp;
1679 Xapian::StringValueRangeProcessor vrp_default(0);
1680 Xapian::StringValueRangeProcessor vrp_tag(1, "tag:", true);
1681 qp.add_valuerangeprocessor(&vrp_tag);
1682 qp.add_valuerangeprocessor(&vrp_default);
1683 for (const test *p = test_value_stringrange1_queries; p->query; ++p) {
1684 string expect, parsed;
1685 if (p->expect)
1686 expect = p->expect;
1687 else
1688 expect = "parse error";
1689 try {
1690 Xapian::Query qobj = qp.parse_query(p->query);
1691 parsed = qobj.get_description();
1692 expect = string("Query(") + expect + ')';
1693 } catch (const Xapian::QueryParserError &e) {
1694 parsed = e.get_msg();
1695 } catch (const Xapian::Error &e) {
1696 parsed = e.get_description();
1697 } catch (...) {
1698 parsed = "Unknown exception!";
1700 tout << "Query: " << p->query << '\n';
1701 TEST_STRINGS_EQUAL(parsed, expect);
1703 return true;
1706 struct AuthorValueRangeProcessor : public Xapian::ValueRangeProcessor {
1707 AuthorValueRangeProcessor() {}
1709 Xapian::valueno operator()(std::string &begin, std::string &end) {
1710 if (!startswith(begin, "author:"))
1711 return Xapian::BAD_VALUENO;
1712 begin.erase(0, 7);
1713 begin = Xapian::Unicode::tolower(begin);
1714 end = Xapian::Unicode::tolower(end);
1715 return 4;
1719 static const test test_value_customrange1_queries[] = {
1720 { "mars author:Asimov..Bradbury", "(mars@1 FILTER VALUE_RANGE 4 asimov bradbury)" },
1721 { NULL, NULL }
1724 // Test custom ValueRangeProcessor subclass.
1725 static bool test_qp_value_customrange1()
1727 Xapian::QueryParser qp;
1728 AuthorValueRangeProcessor vrp_author;
1729 qp.add_valuerangeprocessor(&vrp_author);
1730 for (const test *p = test_value_customrange1_queries; p->query; ++p) {
1731 string expect, parsed;
1732 if (p->expect)
1733 expect = p->expect;
1734 else
1735 expect = "parse error";
1736 try {
1737 Xapian::Query qobj = qp.parse_query(p->query);
1738 parsed = qobj.get_description();
1739 expect = string("Query(") + expect + ')';
1740 } catch (const Xapian::QueryParserError &e) {
1741 parsed = e.get_msg();
1742 } catch (const Xapian::Error &e) {
1743 parsed = e.get_description();
1744 } catch (...) {
1745 parsed = "Unknown exception!";
1747 tout << "Query: " << p->query << '\n';
1748 TEST_STRINGS_EQUAL(parsed, expect);
1750 return true;
1753 class TitleFieldProcessor : public Xapian::FieldProcessor {
1754 Xapian::Query operator()(const std::string & str) {
1755 if (str == "all")
1756 return Xapian::Query::MatchAll;
1757 return Xapian::Query("S" + str);
1761 class HostFieldProcessor : public Xapian::FieldProcessor {
1762 Xapian::Query operator()(const std::string & str) {
1763 if (str == "*")
1764 return Xapian::Query::MatchAll;
1765 string res = "H";
1766 for (string::const_iterator i = str.begin(); i != str.end(); ++i)
1767 res += tolower((unsigned char)*i);
1768 return Xapian::Query(res);
1772 static const test test_fieldproc1_queries[] = {
1773 { "title:test", "Stest" },
1774 { "title:all", "<alldocuments>" },
1775 { "host:Xapian.org", "0 * Hxapian.org" },
1776 { "host:*", "0 * <alldocuments>" },
1777 { "host:\"Space Station.Example.Org\"", "0 * Hspace station.example.org" },
1778 { NULL, NULL }
1781 // FieldProcessor test.
1782 static bool test_qp_fieldproc1()
1784 Xapian::QueryParser qp;
1785 TitleFieldProcessor title_fproc;
1786 HostFieldProcessor host_fproc;
1787 qp.add_prefix("title", &title_fproc);
1788 qp.add_boolean_prefix("host", &host_fproc);
1789 for (const test *p = test_fieldproc1_queries; p->query; ++p) {
1790 string expect, parsed;
1791 if (p->expect)
1792 expect = p->expect;
1793 else
1794 expect = "parse error";
1795 try {
1796 Xapian::Query qobj = qp.parse_query(p->query);
1797 parsed = qobj.get_description();
1798 expect = string("Query(") + expect + ')';
1799 } catch (const Xapian::QueryParserError &e) {
1800 parsed = e.get_msg();
1801 } catch (const Xapian::Error &e) {
1802 parsed = e.get_description();
1803 } catch (...) {
1804 parsed = "Unknown exception!";
1806 tout << "Query: " << p->query << '\n';
1807 TEST_STRINGS_EQUAL(parsed, expect);
1809 return true;
1812 class DateRangeFieldProcessor : public Xapian::FieldProcessor {
1813 Xapian::Query operator()(const std::string & str) {
1814 // In reality, these would be built from the current date, but for
1815 // testing it is much simpler to fix the date.
1816 if (str == "today")
1817 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120725");
1818 if (str == "this week")
1819 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120723");
1820 if (str == "this month")
1821 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120701");
1822 if (str == "this year")
1823 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120101");
1824 if (str == "this decade")
1825 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20100101");
1826 if (str == "this century")
1827 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20000101");
1828 throw Xapian::QueryParserError("Didn't understand date specification '" + str + "'");
1832 static const test test_fieldproc2_queries[] = {
1833 { "date:\"this week\"", "0 * VALUE_GE 1 20120723" },
1834 { "date:23/7/2012..25/7/2012", "0 * VALUE_RANGE 1 20120723 20120725" },
1835 { NULL, NULL }
1838 // Test using FieldProcessor and ValueRangeProcessor together.
1839 static bool test_qp_fieldproc2()
1841 Xapian::QueryParser qp;
1842 DateRangeFieldProcessor date_fproc;
1843 qp.add_boolean_prefix("date", &date_fproc);
1844 Xapian::DateValueRangeProcessor vrp_date(1, "date:");
1845 qp.add_valuerangeprocessor(&vrp_date);
1846 for (const test *p = test_fieldproc2_queries; p->query; ++p) {
1847 string expect, parsed;
1848 if (p->expect)
1849 expect = p->expect;
1850 else
1851 expect = "parse error";
1852 try {
1853 Xapian::Query qobj = qp.parse_query(p->query);
1854 parsed = qobj.get_description();
1855 expect = string("Query(") + expect + ')';
1856 } catch (const Xapian::QueryParserError &e) {
1857 parsed = e.get_msg();
1858 } catch (const Xapian::Error &e) {
1859 parsed = e.get_description();
1860 } catch (...) {
1861 parsed = "Unknown exception!";
1863 tout << "Query: " << p->query << '\n';
1864 TEST_STRINGS_EQUAL(parsed, expect);
1866 return true;
1869 static bool test_qp_stoplist1()
1871 Xapian::QueryParser qp;
1872 const char * stopwords[] = { "a", "an", "the" };
1873 Xapian::SimpleStopper stop(stopwords, stopwords + 3);
1874 qp.set_stopper(&stop);
1876 Xapian::TermIterator i;
1878 Xapian::Query query1 = qp.parse_query("some mice");
1879 i = qp.stoplist_begin();
1880 TEST(i == qp.stoplist_end());
1882 Xapian::Query query2 = qp.parse_query("the cat");
1883 i = qp.stoplist_begin();
1884 TEST(i != qp.stoplist_end());
1885 TEST_EQUAL(*i, "the");
1886 ++i;
1887 TEST(i == qp.stoplist_end());
1889 // Regression test - prior to Xapian 1.0.0 the stoplist wasn't being cleared
1890 // when a new query was parsed.
1891 Xapian::Query query3 = qp.parse_query("an aardvark");
1892 i = qp.stoplist_begin();
1893 TEST(i != qp.stoplist_end());
1894 TEST_EQUAL(*i, "an");
1895 ++i;
1896 TEST(i == qp.stoplist_end());
1898 return true;
1901 static const test test_mispelled_queries[] = {
1902 { "doucment search", "document search" },
1903 { "doucment seeacrh", "document search" },
1904 { "docment seeacrh test", "document search test" },
1905 { "\"paragahp pineapple\"", "\"paragraph pineapple\"" },
1906 { "\"paragahp pineapple\"", "\"paragraph pineapple\"" },
1907 { "test S.E.A.R.C.", "" },
1908 { "this AND that", "" },
1909 { "documento", "document" },
1910 { "documento-documento", "document-document" },
1911 { "documento-searcho", "document-search" },
1912 { "test saerch", "test search" },
1913 { "paragraf search", "paragraph search" },
1914 { NULL, NULL }
1917 // Test spelling correction in the QueryParser.
1918 static bool test_qp_spell1()
1920 mkdir(".chert", 0755);
1921 string dbdir = ".chert/qp_spell1";
1922 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1924 Xapian::Document doc;
1925 doc.add_term("document", 6);
1926 doc.add_term("search", 7);
1927 doc.add_term("saerch", 1);
1928 doc.add_term("paragraph", 8);
1929 doc.add_term("paragraf", 2);
1930 db.add_document(doc);
1932 db.add_spelling("document");
1933 db.add_spelling("search");
1934 db.add_spelling("paragraph");
1935 db.add_spelling("band");
1937 Xapian::QueryParser qp;
1938 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1939 qp.set_database(db);
1941 for (const test *p = test_mispelled_queries; p->query; ++p) {
1942 Xapian::Query q;
1943 q = qp.parse_query(p->query,
1944 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
1945 Xapian::QueryParser::FLAG_BOOLEAN );
1946 tout << "Query: " << p->query << endl;
1947 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
1950 return true;
1953 // Test spelling correction in the QueryParser with multiple databases.
1954 static bool test_qp_spell2()
1956 mkdir(".chert", 0755);
1957 string dbdir = ".chert/qp_spell2";
1958 Xapian::WritableDatabase db1(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1960 db1.add_spelling("document");
1961 db1.add_spelling("search");
1962 db1.commit();
1964 dbdir = ".chert/qp_spell2b";
1965 Xapian::WritableDatabase db2(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
1967 db2.add_spelling("document");
1968 db2.add_spelling("paragraph");
1969 db2.add_spelling("band");
1971 Xapian::Database db;
1972 db.add_database(db1);
1973 db.add_database(db2);
1975 Xapian::QueryParser qp;
1976 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1977 qp.set_database(db);
1979 for (const test *p = test_mispelled_queries; p->query; ++p) {
1980 Xapian::Query q;
1981 q = qp.parse_query(p->query,
1982 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
1983 Xapian::QueryParser::FLAG_BOOLEAN );
1984 tout << "Query: " << p->query << endl;
1985 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
1988 return true;
1991 static const test test_mispelled_wildcard_queries[] = {
1992 { "doucment", "document" },
1993 { "doucment*", "" },
1994 { "doucment* seearch", "doucment* search" },
1995 { "doucment* search", "" },
1996 { NULL, NULL }
1999 // Test spelling correction in the QueryParser with wildcards.
2000 // Regression test for bug fixed in 1.1.3 and 1.0.17.
2001 static bool test_qp_spellwild1()
2003 mkdir(".chert", 0755);
2004 string dbdir = ".chert/qp_spellwild1";
2005 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
2007 db.add_spelling("document");
2008 db.add_spelling("search");
2009 db.add_spelling("paragraph");
2010 db.add_spelling("band");
2012 Xapian::QueryParser qp;
2013 qp.set_database(db);
2015 const test *p;
2016 for (p = test_mispelled_queries; p->query; ++p) {
2017 Xapian::Query q;
2018 q = qp.parse_query(p->query,
2019 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2020 Xapian::QueryParser::FLAG_BOOLEAN |
2021 Xapian::QueryParser::FLAG_WILDCARD);
2022 tout << "Query: " << p->query << endl;
2023 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2025 for (p = test_mispelled_wildcard_queries; p->query; ++p) {
2026 Xapian::Query q;
2027 q = qp.parse_query(p->query,
2028 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2029 Xapian::QueryParser::FLAG_BOOLEAN |
2030 Xapian::QueryParser::FLAG_WILDCARD);
2031 tout << "Query: " << p->query << endl;
2032 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2035 return true;
2038 static const test test_mispelled_partial_queries[] = {
2039 { "doucment", "" },
2040 { "doucment ", "document " },
2041 { "documen", "" },
2042 { "documen ", "document " },
2043 { "seearch documen", "search documen" },
2044 { "search documen", "" },
2045 { NULL, NULL }
2048 // Test spelling correction in the QueryParser with FLAG_PARTIAL.
2049 // Regression test for bug fixed in 1.1.3 and 1.0.17.
2050 static bool test_qp_spellpartial1()
2052 mkdir(".chert", 0755);
2053 string dbdir = ".chert/qp_spellpartial1";
2054 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
2056 db.add_spelling("document");
2057 db.add_spelling("search");
2058 db.add_spelling("paragraph");
2059 db.add_spelling("band");
2061 Xapian::QueryParser qp;
2062 qp.set_database(db);
2064 for (const test *p = test_mispelled_partial_queries; p->query; ++p) {
2065 Xapian::Query q;
2066 q = qp.parse_query(p->query,
2067 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2068 Xapian::QueryParser::FLAG_PARTIAL);
2069 tout << "Query: " << p->query << endl;
2070 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2073 return true;
2076 static const test test_synonym_queries[] = {
2077 { "searching", "(Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1)" },
2078 { "search", "(Zsearch@1 SYNONYM find@1)" },
2079 { "Search", "(search@1 SYNONYM find@1)" },
2080 { "Searching", "searching@1" },
2081 { "searching OR terms", "((Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1) OR Zterm@2)" },
2082 { "search OR terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2083 { "search +terms", "(Zterm@2 AND_MAYBE (Zsearch@1 SYNONYM find@1))" },
2084 { "search -terms", "((Zsearch@1 SYNONYM find@1) AND_NOT Zterm@2)" },
2085 { "+search terms", "((Zsearch@1 SYNONYM find@1) AND_MAYBE Zterm@2)" },
2086 { "-search terms", "(Zterm@2 AND_NOT (Zsearch@1 SYNONYM find@1))" },
2087 { "search terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2088 // Shouldn't trigger synonyms:
2089 { "\"search terms\"", "(search@1 PHRASE 2 terms@2)" },
2090 // Check that setting FLAG_AUTO_SYNONYMS doesn't enable multi-word
2091 // synonyms. Regression test for bug fixed in 1.3.0 and 1.2.9.
2092 { "regression test", "(Zregress@1 OR Ztest@2)" },
2093 { NULL, NULL }
2096 // Test single term synonyms in the QueryParser.
2097 static bool test_qp_synonym1()
2099 mkdir(".chert", 0755);
2100 string dbdir = ".chert/qp_synonym1";
2101 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
2103 db.add_synonym("Zsearch", "Zfind");
2104 db.add_synonym("Zsearch", "Zlocate");
2105 db.add_synonym("search", "find");
2106 db.add_synonym("Zseek", "Zsearch");
2107 db.add_synonym("regression test", "magic");
2109 db.commit();
2111 Xapian::QueryParser qp;
2112 qp.set_stemmer(Xapian::Stem("english"));
2113 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2114 qp.set_database(db);
2116 for (const test *p = test_synonym_queries; p->query; ++p) {
2117 string expect = "Query(";
2118 expect += p->expect;
2119 expect += ')';
2120 Xapian::Query q;
2121 q = qp.parse_query(p->query, qp.FLAG_AUTO_SYNONYMS|qp.FLAG_DEFAULT);
2122 tout << "Query: " << p->query << endl;
2123 TEST_STRINGS_EQUAL(q.get_description(), expect);
2126 return true;
2129 static const test test_multi_synonym_queries[] = {
2130 { "sun OR tan OR cream", "((Zsun@1 OR Ztan@2) OR Zcream@3)" },
2131 { "sun tan", "((Zsun@1 OR Ztan@2) SYNONYM bathe@1)" },
2132 { "sun tan cream", "((Zsun@1 OR Ztan@2 OR Zcream@3) SYNONYM lotion@1)" },
2133 { "beach sun tan holiday", "(Zbeach@1 OR ((Zsun@2 OR Ztan@3) SYNONYM bathe@2) OR Zholiday@4)" },
2134 { "sun tan sun tan cream", "(((Zsun@1 OR Ztan@2) SYNONYM bathe@1) OR ((Zsun@3 OR Ztan@4 OR Zcream@5) SYNONYM lotion@3))" },
2135 { "single", "(Zsingl@1 SYNONYM record@1)" },
2136 { NULL, NULL }
2139 // Test multi term synonyms in the QueryParser.
2140 static bool test_qp_synonym2()
2142 mkdir(".chert", 0755);
2143 string dbdir = ".chert/qp_synonym2";
2144 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
2146 db.add_synonym("sun tan cream", "lotion");
2147 db.add_synonym("sun tan", "bathe");
2148 db.add_synonym("single", "record");
2150 db.commit();
2152 Xapian::QueryParser qp;
2153 qp.set_stemmer(Xapian::Stem("english"));
2154 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2155 qp.set_database(db);
2157 for (const test *p = test_multi_synonym_queries; p->query; ++p) {
2158 string expect = "Query(";
2159 expect += p->expect;
2160 expect += ')';
2161 Xapian::Query q;
2162 q = qp.parse_query(p->query,
2163 Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS |
2164 Xapian::QueryParser::FLAG_DEFAULT);
2165 tout << "Query: " << p->query << endl;
2166 TEST_STRINGS_EQUAL(q.get_description(), expect);
2169 return true;
2172 static const test test_synonym_op_queries[] = {
2173 { "searching", "Zsearch@1" },
2174 { "~searching", "(Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1)" },
2175 { "~search", "(Zsearch@1 SYNONYM find@1)" },
2176 { "~Search", "(search@1 SYNONYM find@1)" },
2177 { "~Searching", "searching@1" },
2178 { "~searching OR terms", "((Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1) OR Zterm@2)" },
2179 { "~search OR terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2180 { "~search +terms", "(Zterm@2 AND_MAYBE (Zsearch@1 SYNONYM find@1))" },
2181 { "~search -terms", "((Zsearch@1 SYNONYM find@1) AND_NOT Zterm@2)" },
2182 { "+~search terms", "((Zsearch@1 SYNONYM find@1) AND_MAYBE Zterm@2)" },
2183 { "-~search terms", "(Zterm@2 AND_NOT (Zsearch@1 SYNONYM find@1))" },
2184 { "~search terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2185 { "~foo:search", "(ZXFOOsearch@1 SYNONYM prefixated@1)" },
2186 // FIXME: should look for multi-term synonym...
2187 { "~\"search terms\"", "(search@1 PHRASE 2 terms@2)" },
2188 { NULL, NULL }
2191 // Test the synonym operator in the QueryParser.
2192 static bool test_qp_synonym3()
2194 mkdir(".chert", 0755);
2195 string dbdir = ".chert/qp_synonym3";
2196 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
2198 db.add_synonym("Zsearch", "Zfind");
2199 db.add_synonym("Zsearch", "Zlocate");
2200 db.add_synonym("search", "find");
2201 db.add_synonym("Zseek", "Zsearch");
2202 db.add_synonym("ZXFOOsearch", "prefixated");
2204 db.commit();
2206 Xapian::QueryParser qp;
2207 qp.set_stemmer(Xapian::Stem("english"));
2208 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2209 qp.set_database(db);
2210 qp.add_prefix("foo", "XFOO");
2212 for (const test *p = test_synonym_op_queries; p->query; ++p) {
2213 string expect = "Query(";
2214 expect += p->expect;
2215 expect += ')';
2216 Xapian::Query q;
2217 q = qp.parse_query(p->query,
2218 Xapian::QueryParser::FLAG_SYNONYM |
2219 Xapian::QueryParser::FLAG_BOOLEAN |
2220 Xapian::QueryParser::FLAG_LOVEHATE |
2221 Xapian::QueryParser::FLAG_PHRASE );
2222 tout << "Query: " << p->query << endl;
2223 TEST_STRINGS_EQUAL(q.get_description(), expect);
2226 return true;
2229 static const test test_stem_all_queries[] = {
2230 { "\"chemical engineers\"", "(chemic@1 PHRASE 2 engin@2)" },
2231 { "chemical NEAR engineers", "(chemic@1 NEAR 11 engin@2)" },
2232 { "chemical engineers", "(chemic@1 OR engin@2)" },
2233 { "title:(chemical engineers)", "(XTchemic@1 OR XTengin@2)" },
2234 { NULL, NULL }
2237 static bool test_qp_stem_all1()
2239 Xapian::QueryParser qp;
2240 qp.set_stemmer(Xapian::Stem("english"));
2241 qp.set_stemming_strategy(qp.STEM_ALL);
2242 qp.add_prefix("title", "XT");
2243 for (const test *p = test_stem_all_queries; p->query; ++p) {
2244 string expect, parsed;
2245 if (p->expect)
2246 expect = p->expect;
2247 else
2248 expect = "parse error";
2249 try {
2250 Xapian::Query qobj = qp.parse_query(p->query);
2251 parsed = qobj.get_description();
2252 expect = string("Query(") + expect + ')';
2253 } catch (const Xapian::QueryParserError &e) {
2254 parsed = e.get_msg();
2255 } catch (const Xapian::Error &e) {
2256 parsed = e.get_description();
2257 } catch (...) {
2258 parsed = "Unknown exception!";
2260 tout << "Query: " << p->query << '\n';
2261 TEST_STRINGS_EQUAL(parsed, expect);
2263 return true;
2266 static const test test_stem_all_z_queries[] = {
2267 { "\"chemical engineers\"", "(Zchemic@1 PHRASE 2 Zengin@2)" },
2268 { "chemical NEAR engineers", "(Zchemic@1 NEAR 11 Zengin@2)" },
2269 { "chemical engineers", "(Zchemic@1 OR Zengin@2)" },
2270 { "title:(chemical engineers)", "(ZXTchemic@1 OR ZXTengin@2)" },
2271 { NULL, NULL }
2274 static bool test_qp_stem_all_z1()
2276 Xapian::QueryParser qp;
2277 qp.set_stemmer(Xapian::Stem("english"));
2278 qp.set_stemming_strategy(qp.STEM_ALL_Z);
2279 qp.add_prefix("title", "XT");
2280 for (const test *p = test_stem_all_z_queries; p->query; ++p) {
2281 string expect, parsed;
2282 if (p->expect)
2283 expect = p->expect;
2284 else
2285 expect = "parse error";
2286 try {
2287 Xapian::Query qobj = qp.parse_query(p->query);
2288 parsed = qobj.get_description();
2289 expect = string("Query(") + expect + ')';
2290 } catch (const Xapian::QueryParserError &e) {
2291 parsed = e.get_msg();
2292 } catch (const Xapian::Error &e) {
2293 parsed = e.get_description();
2294 } catch (...) {
2295 parsed = "Unknown exception!";
2297 tout << "Query: " << p->query << '\n';
2298 TEST_STRINGS_EQUAL(parsed, expect);
2300 return true;
2303 static double
2304 time_query_parse(const Xapian::Database & db, const string & q,
2305 int repetitions, unsigned flags)
2307 Xapian::QueryParser qp;
2308 qp.set_database(db);
2309 CPUTimer timer;
2310 std::vector<Xapian::Query> qs;
2311 qs.reserve(repetitions);
2312 for (int i = 0; i != repetitions; ++i) {
2313 qs.push_back(qp.parse_query(q, flags));
2315 if (repetitions > 1) {
2316 Xapian::Query qc(Xapian::Query::OP_OR, qs.begin(), qs.end());
2318 return timer.get_time();
2321 static void
2322 qp_scale1_helper(const Xapian::Database &db, const string & q, unsigned n,
2323 unsigned flags)
2325 double time1;
2326 while (true) {
2327 time1 = time_query_parse(db, q, n, flags);
2328 if (time1 != 0.0) break;
2330 // The first test completed before the timer ticked at all, so increase
2331 // the number of repetitions and retry.
2332 unsigned n_new = n * 10;
2333 if (n_new < n)
2334 SKIP_TEST("Can't count enough repetitions to be able to time test");
2335 n = n_new;
2338 n /= 5;
2340 string q_n;
2341 q_n.reserve(q.size() * n);
2342 for (unsigned i = n; i != 0; --i) {
2343 q_n += q;
2346 // Time 5 repetitions so we average random variations a bit.
2347 double time2 = time_query_parse(db, q_n, 5, flags);
2348 tout << "small=" << time1 << "s, large=" << time2 << "s\n";
2350 // Allow a factor of 2.15 difference, to cover random variation and a
2351 // native time interval which isn't an exact multiple of 1/CLK_TCK.
2352 TEST_REL(time2,<,time1 * 2.15);
2355 // Regression test: check that query parser doesn't scale very badly with the
2356 // size of the query.
2357 static bool test_qp_scale1()
2359 mkdir(".chert", 0755);
2360 string dbdir = ".chert/qp_scale1";
2361 Xapian::WritableDatabase db(dbdir, Xapian::DB_CREATE_OR_OVERWRITE);
2363 db.add_synonym("foo", "bar");
2364 db.commit();
2366 string q1("foo ");
2367 string q1b("baz ");
2368 const unsigned repetitions = 5000;
2370 // A long multiword synonym.
2371 string syn;
2372 for (int j = 60; j != 0; --j) {
2373 syn += q1;
2375 syn.resize(syn.size() - 1);
2377 unsigned synflags = Xapian::QueryParser::FLAG_DEFAULT |
2378 Xapian::QueryParser::FLAG_SYNONYM |
2379 Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS;
2381 // First, we test a simple query.
2382 qp_scale1_helper(db, q1, repetitions, Xapian::QueryParser::FLAG_DEFAULT);
2384 // If synonyms are enabled, a different code-path is followed.
2385 // Test a query which has no synonyms.
2386 qp_scale1_helper(db, q1b, repetitions, synflags);
2388 // Test a query which has short synonyms.
2389 qp_scale1_helper(db, q1, repetitions, synflags);
2391 // Add a synonym for the whole query, to test that code path.
2392 db.add_synonym(syn, "bar");
2393 db.commit();
2395 qp_scale1_helper(db, q1, repetitions, synflags);
2397 return true;
2400 static const test test_near_queries[] = {
2401 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
2402 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
2403 // FIXME: these give NEAR 2
2404 // { "foo -baz bar", "((foo@1 NEAR 11 bar@3) AND_NOT Zbaz@2)" },
2405 // { "one +two three", "(Ztwo@2 AND_MAYBE (one@1 NEAR 11 three@3))" },
2406 { "foo bar", "(foo@1 NEAR 11 bar@2)" },
2407 { "foo bar baz", "(foo@1 NEAR 12 bar@2 NEAR 12 baz@3)" },
2408 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
2409 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
2410 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
2411 { "author:orwell animal farm", "(Aorwell@1 NEAR 12 animal@2 NEAR 12 farm@3)" },
2412 { "author:Orwell Animal Farm", "(Aorwell@1 NEAR 12 animal@2 NEAR 12 farm@3)" },
2413 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
2414 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
2415 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2416 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2417 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
2418 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
2419 { "one AND two", "(Zone@1 AND Ztwo@2)" },
2420 { "NOT windows", "Syntax: <expression> NOT <expression>" },
2421 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
2422 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
2423 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
2424 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
2425 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
2426 { "OR foo", "Syntax: <expression> OR <expression>" },
2427 { "XOR", "Syntax: <expression> XOR <expression>" },
2428 { "hard\xa0space", "(hard@1 NEAR 11 space@2)" },
2429 { NULL, NULL }
2432 static bool test_qp_near1()
2434 Xapian::QueryParser queryparser;
2435 queryparser.set_stemmer(Xapian::Stem("english"));
2436 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2437 queryparser.add_prefix("author", "A");
2438 queryparser.add_prefix("writer", "A");
2439 queryparser.add_prefix("title", "XT");
2440 queryparser.add_prefix("subject", "XT");
2441 queryparser.add_prefix("authortitle", "A");
2442 queryparser.add_prefix("authortitle", "XT");
2443 queryparser.add_boolean_prefix("site", "H");
2444 queryparser.add_boolean_prefix("site2", "J");
2445 queryparser.add_boolean_prefix("multisite", "H");
2446 queryparser.add_boolean_prefix("multisite", "J");
2447 queryparser.add_boolean_prefix("category", "XCAT", false);
2448 queryparser.set_default_op(Xapian::Query::OP_NEAR);
2449 for (const test *p = test_near_queries; p->query; ++p) {
2450 string expect, parsed;
2451 if (p->expect)
2452 expect = p->expect;
2453 else
2454 expect = "parse error";
2455 try {
2456 Xapian::Query qobj = queryparser.parse_query(p->query);
2457 parsed = qobj.get_description();
2458 expect = string("Query(") + expect + ')';
2459 } catch (const Xapian::QueryParserError &e) {
2460 parsed = e.get_msg();
2461 } catch (const Xapian::Error &e) {
2462 parsed = e.get_description();
2463 } catch (...) {
2464 parsed = "Unknown exception!";
2466 tout << "Query: " << p->query << '\n';
2467 TEST_STRINGS_EQUAL(parsed, expect);
2469 return true;
2472 static const test test_phrase_queries[] = {
2473 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
2474 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
2475 // FIXME: these give PHRASE 2
2476 // { "foo -baz bar", "((foo@1 PHRASE 11 bar@3) AND_NOT Zbaz@2)" },
2477 // { "one +two three", "(Ztwo@2 AND_MAYBE (one@1 PHRASE 11 three@3))" },
2478 { "foo bar", "(foo@1 PHRASE 11 bar@2)" },
2479 { "foo bar baz", "(foo@1 PHRASE 12 bar@2 PHRASE 12 baz@3)" },
2480 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
2481 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
2482 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
2483 { "author:orwell animal farm", "(Aorwell@1 PHRASE 12 animal@2 PHRASE 12 farm@3)" },
2484 { "author:Orwell Animal Farm", "(Aorwell@1 PHRASE 12 animal@2 PHRASE 12 farm@3)" },
2485 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
2486 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
2487 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2488 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2489 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
2490 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
2491 { "one AND two", "(Zone@1 AND Ztwo@2)" },
2492 { "NOT windows", "Syntax: <expression> NOT <expression>" },
2493 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
2494 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
2495 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
2496 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
2497 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
2498 { "OR foo", "Syntax: <expression> OR <expression>" },
2499 { "XOR", "Syntax: <expression> XOR <expression>" },
2500 { "hard\xa0space", "(hard@1 PHRASE 11 space@2)" },
2501 // FIXME: this isn't what we want, but fixing phrase to work with
2502 // subqueries first might be the best approach.
2503 // FIXME: this isn't currently reimplemented:
2504 // { "(one AND two) three", "((Zone@1 PHRASE 11 Zthree@3) AND (Ztwo@2 PHRASE 11 Zthree@3))" },
2505 { NULL, NULL }
2508 static bool test_qp_phrase1()
2510 Xapian::QueryParser queryparser;
2511 queryparser.set_stemmer(Xapian::Stem("english"));
2512 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2513 queryparser.add_prefix("author", "A");
2514 queryparser.add_prefix("writer", "A");
2515 queryparser.add_prefix("title", "XT");
2516 queryparser.add_prefix("subject", "XT");
2517 queryparser.add_prefix("authortitle", "A");
2518 queryparser.add_prefix("authortitle", "XT");
2519 queryparser.add_boolean_prefix("site", "H");
2520 queryparser.add_boolean_prefix("site2", "J");
2521 queryparser.add_boolean_prefix("multisite", "H");
2522 queryparser.add_boolean_prefix("multisite", "J");
2523 queryparser.add_boolean_prefix("category", "XCAT", false);
2524 queryparser.set_default_op(Xapian::Query::OP_PHRASE);
2525 for (const test *p = test_phrase_queries; p->query; ++p) {
2526 string expect, parsed;
2527 if (p->expect)
2528 expect = p->expect;
2529 else
2530 expect = "parse error";
2531 try {
2532 Xapian::Query qobj = queryparser.parse_query(p->query);
2533 parsed = qobj.get_description();
2534 expect = string("Query(") + expect + ')';
2535 } catch (const Xapian::QueryParserError &e) {
2536 parsed = e.get_msg();
2537 } catch (const Xapian::Error &e) {
2538 parsed = e.get_description();
2539 } catch (...) {
2540 parsed = "Unknown exception!";
2542 tout << "Query: " << p->query << '\n';
2543 TEST_STRINGS_EQUAL(parsed, expect);
2545 return true;
2548 static const test test_stopword_group_or_queries[] = {
2549 { "this is a test", "test@4" },
2550 { "test*", "(SYNONYM WILDCARD OR test)" },
2551 { "a test*", "(SYNONYM WILDCARD OR test)" },
2552 { "is a test*", "(SYNONYM WILDCARD OR test)" },
2553 { "this is a test*", "(SYNONYM WILDCARD OR test)" },
2554 { "this is a us* test*", "((SYNONYM WILDCARD OR us) OR (SYNONYM WILDCARD OR test))" },
2555 { "this is a user test*", "(user@4 OR (SYNONYM WILDCARD OR test))" },
2556 { NULL, NULL }
2559 static const test test_stopword_group_and_queries[] = {
2560 { "this is a test", "test@4" },
2561 { "test*", "(SYNONYM WILDCARD OR test)" },
2562 { "a test*", "(SYNONYM WILDCARD OR test)" },
2563 // Two stopwords + one wildcard failed in 1.0.16
2564 { "is a test*", "(SYNONYM WILDCARD OR test)" },
2565 // Three stopwords + one wildcard failed in 1.0.16
2566 { "this is a test*", "(SYNONYM WILDCARD OR test)" },
2567 // Three stopwords + two wildcards failed in 1.0.16
2568 { "this is a us* test*", "((SYNONYM WILDCARD OR us) AND (SYNONYM WILDCARD OR test))" },
2569 { "this is a user test*", "(user@4 AND (SYNONYM WILDCARD OR test))" },
2570 { NULL, NULL }
2573 // Regression test for bug fixed in 1.0.17 and 1.1.3.
2574 static bool test_qp_stopword_group1()
2576 #ifndef XAPIAN_HAS_INMEMORY_BACKEND
2577 SKIP_TEST("Testcase requires the InMemory backend which is disabled");
2578 #else
2579 Xapian::WritableDatabase db(Xapian::InMemory::open());
2580 Xapian::Document doc;
2581 doc.add_term("test");
2582 doc.add_term("tester");
2583 doc.add_term("testable");
2584 doc.add_term("user");
2585 db.add_document(doc);
2587 Xapian::SimpleStopper stopper;
2588 stopper.add("this");
2589 stopper.add("is");
2590 stopper.add("a");
2592 Xapian::QueryParser qp;
2593 qp.set_stopper(&stopper);
2594 qp.set_database(db);
2596 // Process test cases with OP_OR first, then with OP_AND.
2597 qp.set_default_op(Xapian::Query::OP_OR);
2598 const test *p = test_stopword_group_or_queries;
2599 for (int i = 1; i <= 2; ++i) {
2600 for ( ; p->query; ++p) {
2601 string expect, parsed;
2602 if (p->expect)
2603 expect = p->expect;
2604 else
2605 expect = "parse error";
2606 try {
2607 Xapian::Query qobj = qp.parse_query(p->query, qp.FLAG_WILDCARD);
2608 parsed = qobj.get_description();
2609 expect = string("Query(") + expect + ')';
2610 } catch (const Xapian::QueryParserError &e) {
2611 parsed = e.get_msg();
2612 } catch (const Xapian::Error &e) {
2613 parsed = e.get_description();
2614 } catch (...) {
2615 parsed = "Unknown exception!";
2617 tout << "Query: " << p->query << '\n';
2618 TEST_STRINGS_EQUAL(parsed, expect);
2621 qp.set_default_op(Xapian::Query::OP_AND);
2622 p = test_stopword_group_and_queries;
2625 return true;
2626 #endif
2629 /// Check that QueryParser::set_default_op() rejects inappropriate ops.
2630 static bool test_qp_default_op2()
2632 Xapian::QueryParser qp;
2633 static const Xapian::Query::op ops[] = {
2634 Xapian::Query::OP_AND_NOT,
2635 Xapian::Query::OP_XOR,
2636 Xapian::Query::OP_AND_MAYBE,
2637 Xapian::Query::OP_FILTER,
2638 Xapian::Query::OP_VALUE_RANGE,
2639 Xapian::Query::OP_SCALE_WEIGHT,
2640 Xapian::Query::OP_VALUE_GE,
2641 Xapian::Query::OP_VALUE_LE
2643 const Xapian::Query::op * p;
2644 for (p = ops; p - ops != sizeof(ops) / sizeof(*ops); ++p) {
2645 tout << *p << endl;
2646 TEST_EXCEPTION(Xapian::InvalidArgumentError,
2647 qp.set_default_op(*p));
2648 TEST_EQUAL(qp.get_default_op(), Xapian::Query::OP_OR);
2650 return true;
2653 struct qp_default_op3_test {
2654 Xapian::Query::op op;
2655 const char *expect;
2658 /// Check that QueryParser::set_default_op() accepts appropriate ops.
2659 static bool test_qp_default_op3()
2661 Xapian::QueryParser qp;
2662 static const qp_default_op3_test tests[] = {
2663 { Xapian::Query::OP_AND,
2664 "Query((a@1 AND b@2 AND c@3))" },
2665 { Xapian::Query::OP_OR,
2666 "Query((a@1 OR b@2 OR c@3))" },
2667 { Xapian::Query::OP_PHRASE,
2668 "Query((a@1 PHRASE 12 b@2 PHRASE 12 c@3))" },
2669 { Xapian::Query::OP_NEAR,
2670 "Query((a@1 NEAR 12 b@2 NEAR 12 c@3))" },
2671 { Xapian::Query::OP_ELITE_SET,
2672 "Query((a@1 ELITE_SET 10 b@2 ELITE_SET 10 c@3))" },
2673 { Xapian::Query::OP_SYNONYM,
2674 "Query((a@1 SYNONYM b@2 SYNONYM c@3))" },
2676 const qp_default_op3_test * p;
2677 for (p = tests; p - tests != sizeof(tests) / sizeof(*tests); ++p) {
2678 tout << p ->op<< endl;
2679 qp.set_default_op(p->op);
2680 // Check that get_default_op() returns what we just set.
2681 TEST_EQUAL(qp.get_default_op(), p->op);
2682 TEST_EQUAL(qp.parse_query("A B C").get_description(), p->expect);
2684 return true;
2687 /// Test that the default strategy is now STEM_SOME (as of 1.3.1).
2688 static bool test_qp_defaultstrategysome1()
2690 Xapian::QueryParser qp;
2691 qp.set_stemmer(Xapian::Stem("en"));
2692 TEST_EQUAL(qp.parse_query("testing").get_description(), "Query(Ztest@1)");
2693 return true;
2696 /// Test cases for the QueryParser.
2697 static const test_desc tests[] = {
2698 TESTCASE(queryparser1),
2699 TESTCASE(qp_default_op1),
2700 TESTCASE(qp_odd_chars1),
2701 TESTCASE(qp_flag_wildcard1),
2702 TESTCASE(qp_flag_wildcard2),
2703 TESTCASE(qp_flag_wildcard3),
2704 TESTCASE(qp_flag_partial1),
2705 TESTCASE(qp_flag_bool_any_case1),
2706 TESTCASE(qp_stopper1),
2707 TESTCASE(qp_flag_pure_not1),
2708 TESTCASE(qp_unstem_boolean_prefix),
2709 TESTCASE(qp_default_prefix1),
2710 TESTCASE(qp_default_prefix2),
2711 TESTCASE(qp_value_range1),
2712 TESTCASE(qp_value_range2),
2713 TESTCASE(qp_value_range3),
2714 TESTCASE(qp_value_range4),
2715 TESTCASE(qp_value_daterange1),
2716 TESTCASE(qp_value_daterange2),
2717 TESTCASE(qp_value_stringrange1),
2718 TESTCASE(qp_value_customrange1),
2719 TESTCASE(qp_fieldproc1),
2720 TESTCASE(qp_fieldproc2),
2721 TESTCASE(qp_stoplist1),
2722 TESTCASE(qp_spell1),
2723 TESTCASE(qp_spell2),
2724 TESTCASE(qp_spellwild1),
2725 TESTCASE(qp_spellpartial1),
2726 TESTCASE(qp_synonym1),
2727 TESTCASE(qp_synonym2),
2728 TESTCASE(qp_synonym3),
2729 TESTCASE(qp_stem_all1),
2730 TESTCASE(qp_stem_all_z1),
2731 TESTCASE(qp_scale1),
2732 TESTCASE(qp_near1),
2733 TESTCASE(qp_phrase1),
2734 TESTCASE(qp_stopword_group1),
2735 TESTCASE(qp_default_op2),
2736 TESTCASE(qp_default_op3),
2737 TESTCASE(qp_defaultstrategysome1),
2738 END_OF_TESTCASES
2741 int main(int argc, char **argv)
2742 try {
2743 test_driver::parse_command_line(argc, argv);
2744 return test_driver::run(tests);
2745 } catch (const char * e) {
2746 cout << e << endl;
2747 return 1;