Fix whitespace irregularities in code
[xapian.git] / xapian-core / tests / api_queryparser.cc
blob3419570cd7a59adf8fc79b6d2af33019322202e3
1 /** @file api_queryparser.cc
2 * @brief Tests of Xapian::QueryParser
3 */
4 /* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2015,2016 Olly Betts
5 * Copyright (C) 2006,2007,2009 Lemur Consulting Ltd
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <config.h>
25 #include "api_queryparser.h"
27 #define XAPIAN_DEPRECATED(D) D
28 #include <xapian.h>
30 #include "apitest.h"
31 #include "cputimer.h"
32 #include "str.h"
33 #include "stringutils.h"
35 #include <cmath>
36 #include <string>
37 #include <vector>
39 using namespace std;
41 #include "testsuite.h"
42 #include "testutils.h"
44 struct test {
45 const char *query;
46 const char *expect;
49 static const test test_or_queries[] = {
50 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
51 { "time_t", "Ztime_t@1" },
52 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
53 { "foo -baz bar", "((Zfoo@1 OR Zbar@3) AND_NOT Zbaz@2)" },
54 { "d- school report", "(Zd@1 OR (Zschool@2 OR Zreport@3))" },
55 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
56 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
57 { "Mg2+ Cl-", "(mg2+@1 OR cl@2)" },
58 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
59 { "A&L A&RMCO AD&D", "(a&l@1 OR a&rmco@2 OR ad&d@3)" },
60 { "C# vs C++", "(c#@1 OR Zvs@2 OR c++@3)" },
61 { "j##", "Zj##@1" },
62 { "a#b", "(Za@1 OR Zb@2)" },
63 { "O.K. U.N.C.L.E XY.Z.", "((ok@1 OR uncle@2) OR (xy@3 PHRASE 2 z@4))" },
64 { "author:orwell animal farm", "(ZAorwel@1 OR Zanim@2 OR Zfarm@3)" },
65 { "author:Orwell Animal Farm", "(Aorwell@1 OR animal@2 OR farm@3)" },
66 // Regression test for bug reported in 0.9.6.
67 { "author:\"orwell\" title:\"animal\"", "(Aorwell@1 OR XTanimal@2)" },
68 // Regression test for bug related to one reported in 0.9.6.
69 { "author:(orwell) title:(animal)", "(ZAorwel@1 OR ZXTanim@2)" },
70 // Regression test for bug caused by fix for previous bug.
71 { "author:\"milne, a.a.\"", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
72 { "author:\"milne a.a.\"", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
73 // Regression test for bug reported in 0.9.7.
74 { "site:/path/name", "0 * H/path/name" },
75 // Regression test for bug introduced (and fixed) in SVN prior to 1.0.0.
76 { "author:/path/name", "(Apath@1 PHRASE 2 Aname@2)" },
77 // Feature tests for change to allow phrase generators after prefix in 1.2.4.
78 { "author:/path", "ZApath@1" },
79 { "author:-Foo", "Afoo@1" },
80 { "author:/", "Zauthor@1" },
81 { "author::", "Zauthor@1" },
82 { "author:/ foo", "(Zauthor@1 OR Zfoo@2)" },
83 { "author:: foo", "(Zauthor@1 OR Zfoo@2)" },
84 { "author::foo", "(author@1 PHRASE 2 foo@2)" },
85 { "author:/ AND foo", "(Zauthor@1 AND Zfoo@2)" },
86 { "author:: AND foo", "(Zauthor@1 AND Zfoo@2)" },
87 { "foo AND author:/", "(Zfoo@1 AND Zauthor@2)" },
88 { "foo AND author::", "(Zfoo@1 AND Zauthor@2)" },
89 // Regression test for bug introduced into (and fixed) in SVN prior to 1.0.0.
90 { "author:(title::case)", "(Atitle@1 PHRASE 2 Acase@2)" },
91 // Regression test for bug fixed in 1.0.4 - the '+' would be ignored there
92 // because the whitespace after the '"' wasn't noticed.
93 { "\"hello world\" +python", "(Zpython@3 AND_MAYBE (hello@1 PHRASE 2 world@2))" },
94 // In 1.1.0, NON_SPACING_MARK was added as a word character.
95 { "\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" },
96 // In 1.1.4, ENCLOSING_MARK and COMBINING_SPACING_MARK were added, and
97 // code to ignore several zero-width space characters was added.
98 { "\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" },
99 { "unmatched\"", "unmatched@1" },
100 { "unmatched \" \" ", "Zunmatch@1" },
101 { "hyphen-ated\" ", "(hyphen@1 PHRASE 2 ated@2)" },
102 { "hyphen-ated\" \"", "(hyphen@1 PHRASE 2 ated@2)" },
103 { "\"1.4\"", "1.4@1" },
104 { "\"1.\"", "1@1" },
105 { "\"A#.B.\"", "(a#@1 PHRASE 2 b@2)" },
106 { "\" Xapian QueryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
107 { "\" xapian queryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
108 { "h\xc3\xb6hle", "Zh\xc3\xb6hle@1" },
109 { "one +two three", "(Ztwo@2 AND_MAYBE (Zone@1 OR Zthree@3))" },
110 { "subject:test other", "(ZXTtest@1 OR Zother@2)" },
111 { "subject:\"space flight\"", "(XTspace@1 PHRASE 2 XTflight@2)" },
112 { "author:(twain OR poe) OR flight", "((ZAtwain@1 OR ZApoe@2) OR Zflight@3)" },
113 { "author:(twain OR title:pit OR poe)", "((ZAtwain@1 OR ZXTpit@2) OR ZApoe@3)" },
114 { "title:2001 title:space", "(XT2001@1 OR ZXTspace@2)" },
115 { "(title:help)", "ZXThelp@1" },
116 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
117 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
118 { "beer AND -lager", "(Zbeer@1 AND_NOT Zlager@2)" },
119 { "beer AND +lager", "(Zbeer@1 AND Zlager@2)" },
120 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
121 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
122 { "A OR B AND -C", "(a@1 OR (b@2 AND_NOT c@3))" },
123 { "A OR B AND +C", "(a@1 OR (b@2 AND c@3))" },
124 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
125 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
126 { "one AND two", "(Zone@1 AND Ztwo@2)" },
127 { "one A.N.D. two", "(Zone@1 OR and@2 OR Ztwo@3)" },
128 { "one \xc3\x81ND two", "(Zone@1 OR \xc3\xa1nd@2 OR Ztwo@3)" },
129 { "one author:AND two", "(Zone@1 OR Aand@2 OR Ztwo@3)" },
130 { "author:hyphen-ated", "(Ahyphen@1 PHRASE 2 Aated@2)" },
131 { "cvs site:xapian.org", "(Zcvs@1 FILTER Hxapian.org)" },
132 { "cvs -site:xapian.org", "(Zcvs@1 AND_NOT Hxapian.org)" },
133 { "foo -site:xapian.org bar", "((Zfoo@1 OR Zbar@2) AND_NOT Hxapian.org)" },
134 { "site:xapian.org mail", "(Zmail@1 FILTER Hxapian.org)" },
135 { "-site:xapian.org mail", "(Zmail@1 AND_NOT Hxapian.org)" },
136 { "mail AND -site:xapian.org", "(Zmail@1 AND_NOT 0 * Hxapian.org)" },
137 { "-Wredundant-decls", "(wredundant@1 PHRASE 2 decls@2)" },
138 { "site:xapian.org", "0 * Hxapian.org" },
139 { "mug +site:xapian.org -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT Hcvs.xapian.org)" },
140 { "mug -site:cvs.xapian.org +site:xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT Hcvs.xapian.org)" },
141 { "mug +site:xapian.org AND -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT 0 * Hcvs.xapian.org)" },
142 { "mug site:xapian.org AND -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT 0 * Hcvs.xapian.org)" },
143 { "mug site:xapian.org AND +site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND 0 * Hcvs.xapian.org)" },
144 { "NOT windows", "Syntax: <expression> NOT <expression>" },
145 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
146 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
147 { "AND -windows", "Syntax: <expression> AND <expression>" },
148 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
149 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
150 { "gordian AND -", "Syntax: <expression> AND <expression>" },
151 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
152 { "OR foo", "Syntax: <expression> OR <expression>" },
153 { "XOR", "Syntax: <expression> XOR <expression>" },
154 { "hard\xa0space", "(Zhard@1 OR Zspace@2)" },
155 { " white\r\nspace\ttest ", "(Zwhite@1 OR Zspace@2 OR Ztest@3)" },
156 { "one AND two three", "(Zone@1 AND (Ztwo@2 OR Zthree@3))" },
157 { "one two AND three", "((Zone@1 OR Ztwo@2) AND Zthree@3)" },
158 { "one AND two/three", "(Zone@1 AND (two@2 PHRASE 2 three@3))" },
159 { "one AND /two/three", "(Zone@1 AND (two@2 PHRASE 2 three@3))" },
160 { "one AND/two/three", "(Zone@1 AND (two@2 PHRASE 2 three@3))" },
161 { "one +/two/three", "((two@2 PHRASE 2 three@3) AND_MAYBE Zone@1)" },
162 { "one//two", "(one@1 PHRASE 2 two@2)" },
163 { "\"missing quote", "(missing@1 PHRASE 2 quote@2)" },
164 { "DVD+RW", "(dvd@1 OR rw@2)" }, // Would a phrase be better?
165 { "+\"must have\" optional", "((must@1 PHRASE 2 have@2) AND_MAYBE Zoption@3)" },
166 { "one NEAR two NEAR three", "(one@1 NEAR 12 two@2 NEAR 12 three@3)" },
167 { "something NEAR/3 else", "(something@1 NEAR 4 else@2)" },
168 { "a NEAR/6 b NEAR c", "(a@1 NEAR 8 b@2 NEAR 8 c@3)" },
169 { "something ADJ else", "(something@1 PHRASE 11 else@2)" },
170 { "something ADJ/3 else", "(something@1 PHRASE 4 else@2)" },
171 { "a ADJ/6 b ADJ c", "(a@1 PHRASE 8 b@2 PHRASE 8 c@3)" },
172 // Regression test - Unicode character values were truncated to 8 bits
173 // before testing C_isdigit(), so this rather artificial example parsed
174 // to: (a@1 NEAR 262 b@2)
175 { "a NEAR/\xc4\xb5 b", "((Za@1 OR (near@2 PHRASE 2 \xc4\xb5@3)) OR Zb@4)" },
176 { "a ADJ/\xc4\xb5 b", "((Za@1 OR (adj@2 PHRASE 2 \xc4\xb5@3)) OR Zb@4)" },
177 // Regression test - the first two cases were parsed as if the '/' were a
178 // space, which was inconsistent with the second two. Fixed in 1.2.5.
179 { "a NEAR/b", "(Za@1 OR (near@2 PHRASE 2 b@3))" },
180 { "a ADJ/b", "(Za@1 OR (adj@2 PHRASE 2 b@3))" },
181 { "a NEAR/b c", "((Za@1 OR (near@2 PHRASE 2 b@3)) OR Zc@4)" },
182 { "a ADJ/b c", "((Za@1 OR (adj@2 PHRASE 2 b@3)) OR Zc@4)" },
183 // Regression tests - + and - didn't work on bracketed subexpressions prior
184 // to 1.0.2.
185 { "+(one two) three", "((Zone@1 OR Ztwo@2) AND_MAYBE Zthree@3)" },
186 { "zero -(one two)", "(Zzero@1 AND_NOT (Zone@2 OR Ztwo@3))" },
187 // Feature tests that ':' is inserted between prefix and term correctly:
188 { "category:Foo", "0 * XCAT:Foo" },
189 { "category:foo", "0 * XCATfoo" },
190 { "category:\xc3\x96oo", "0 * XCAT\xc3\x96oo" },
191 // Feature tests for quoted boolean terms:
192 { "category:\"Hello world\"", "0 * XCAT:Hello world" },
193 { "category:\"literal \"\"\"", "0 * XCATliteral \"" },
194 { "category:\" \"", "0 * XCAT " },
195 { "category:\"\"", "0 * XCAT" },
196 { "category:\"(unterminated)", "0 * XCAT(unterminated)" },
197 // Feature tests for curly double quotes:
198 { "“curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
199 // Feature tests for implicitly closing brackets:
200 { "(foo", "Zfoo@1" },
201 { "(foo XOR bar", "(Zfoo@1 XOR Zbar@2)" },
202 { "(foo XOR (bar AND baz)", "(Zfoo@1 XOR (Zbar@2 AND Zbaz@3))" },
203 { "(foo XOR (bar AND baz", "(Zfoo@1 XOR (Zbar@2 AND Zbaz@3))" },
204 // Slightly arbitrarily we accept mismatched quotes.
205 { "\"curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
206 { "“curly quotes\"", "(curly@1 PHRASE 2 quotes@2)" },
207 { "“curly quotes“", "(curly@1 PHRASE 2 quotes@2)" },
208 { "”curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
209 { "author:“orwell” title:“animal\"", "(Aorwell@1 OR XTanimal@2)" },
210 { "author:\"orwell” title:“animal”", "(Aorwell@1 OR XTanimal@2)" },
211 { "author:“milne, a.a.”", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
212 { "author:“milne, a.a.\"", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
213 { "author:\"milne a.a.”", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
214 { "“hello world” +python", "(Zpython@3 AND_MAYBE (hello@1 PHRASE 2 world@2))" },
215 { "unmatched“", "Zunmatch@1" },
216 { "unmatched”", "Zunmatch@1" },
217 { "unmatched “ ” ", "Zunmatch@1" },
218 { "unmatched \" ” ", "Zunmatch@1" },
219 { "unmatched “ \" ", "Zunmatch@1" },
220 { "hyphen-ated“ ", "(hyphen@1 PHRASE 2 ated@2)" },
221 { "hyphen-ated” ", "(hyphen@1 PHRASE 2 ated@2)" },
222 { "hyphen-ated“ ”", "(hyphen@1 PHRASE 2 ated@2)" },
223 { "hyphen-ated“ \"", "(hyphen@1 PHRASE 2 ated@2)" },
224 { "hyphen-ated\" ”", "(hyphen@1 PHRASE 2 ated@2)" },
225 { "“1.4”", "1.4@1" },
226 { "“1.\"", "1@1" },
227 { "\"A#.B.”", "(a#@1 PHRASE 2 b@2)" },
228 { "“ Xapian QueryParser” parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
229 { "“ xapian queryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
230 { "beer NOT “orange juice”", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
231 { "“missing quote", "(missing@1 PHRASE 2 quote@2)" },
232 { "+“must have” optional", "((must@1 PHRASE 2 have@2) AND_MAYBE Zoption@3)" },
233 { "category:“Hello world”", "0 * XCAT:Hello world" },
234 { "category:“literal \"\"”", "0 * XCATliteral \"" },
235 { "category:“ ”", "0 * XCAT " },
236 { "category:\" \"", "0 * XCAT ”" },
237 { "category:\" ”", "0 * XCAT ”" },
238 { "category:“ \"", "0 * XCAT " },
239 { "category:“”", "0 * XCAT" },
240 { "category:\"\"", "0 * XCAT”" },
241 { "category:\"”", "0 * XCAT”" },
242 { "category:“\"", "0 * XCAT" },
243 { "category:“(unterminated)", "0 * XCAT(unterminated)" },
244 // Real world examples from tweakers.net:
245 { "Call to undefined function: imagecreate()", "(call@1 OR Zto@2 OR Zundefin@3 OR Zfunction@4 OR imagecreate@5)" },
246 { "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))" },
247 { "php date() nedelands", "((Zphp@1 OR date@2) OR Znedeland@3)" },
248 { "wget domein --http-user", "((Zwget@1 OR Zdomein@2) OR (http@3 PHRASE 2 user@4))" },
249 { "@home problemen", "(Zhome@1 OR Zproblemen@2)" },
250 { "'ipacsum'", "Zipacsum@1" },
251 { "canal + ", "Zcanal@1" },
252 { "/var/run/mysqld/mysqld.sock", "(var@1 PHRASE 5 run@2 PHRASE 5 mysqld@3 PHRASE 5 mysqld@4 PHRASE 5 sock@5)" },
253 { "\"QSI-161 drivers\"", "(qsi@1 PHRASE 3 161@2 PHRASE 3 drivers@3)" },
254 { "\"e-cube\" barebone", "((e@1 PHRASE 2 cube@2) OR Zbarebon@3)" },
255 { "\"./httpd: symbol not found: dlopen\"", "(httpd@1 PHRASE 5 symbol@2 PHRASE 5 not@3 PHRASE 5 found@4 PHRASE 5 dlopen@5)" },
256 { "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)" },
257 { "location.href = \"\"", "(location@1 PHRASE 2 href@2)" },
258 { "method=\"post\" action=\"\">", "((method@1 OR post@2) OR action@3)" },
259 { "behuizing 19\" inch", "((Zbehuiz@1 OR 19@2) OR inch@3)" },
260 { "19\" rack", "(19@1 OR rack@2)" },
261 { "3,5\" mainboard", "(3,5@1 OR mainboard@2)" },
262 { "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)" },
263 { "data error (clic redundancy check)", "((Zdata@1 OR Zerror@2) OR (Zclic@3 OR Zredund@4 OR Zcheck@5))" },
264 { "? mediaplayer 9\"", "(Zmediaplay@1 OR 9@2)" },
265 { "date(\"w\")", "(date@1 OR w@2)" },
266 { "Syntaxisfout (operator ontbreekt ASP", "(syntaxisfout@1 OR (Zoper@2 OR Zontbreekt@3 OR asp@4))" },
267 { "Request.ServerVariables(\"logon_user\")", "((request@1 PHRASE 2 servervariables@2) OR logon_user@3)" },
268 { "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))" },
269 { "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))" },
270 { "ip_masq_new(proto=TCP)", "((ip_masq_new@1 OR proto@2) OR tcp@3)" },
271 { "\"document.write(\"", "(document@1 PHRASE 2 write@2)" },
272 { "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))" },
273 { "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))" },
274 { "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))" },
275 { "\"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)" },
276 { "\") 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)" },
277 { "\"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)" },
278 { "$structure = imap_header($mbox, $tt);", "(((Zstructur@1 OR imap_header@2) OR Zmbox@3) OR Ztt@4)" },
279 { "\"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)" },
280 { "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))" },
281 { "ereg_replace(\"\\\\\",\"\\/\"", "ereg_replace@1" },
282 { "\\\\\"divx+geen+geluid\\\\\"", "(divx@1 PHRASE 3 geen@2 PHRASE 3 geluid@3)" },
283 { "lcase(\"string\")", "(lcase@1 OR string@2)" },
284 { "isEmpty( ) functie in visual basic", "(isempty@1 OR (Zfuncti@2 OR Zin@3 OR Zvisual@4 OR Zbasic@5))" },
285 { "*** stop: 0x0000001E (0xC0000005,0x00000000,0x00000000,0x00000000)", "((Zstop@1 OR 0x0000001e@2) OR 0xc0000005,0x00000000,0x00000000,0x00000000@3)" },
286 { "\"ctrl+v+c+a fout\"", "(ctrl@1 PHRASE 5 v@2 PHRASE 5 c@3 PHRASE 5 a@4 PHRASE 5 fout@5)" },
287 { "Server.CreateObject(\"ADODB.connection\")", "((server@1 PHRASE 2 createobject@2) OR (adodb@3 PHRASE 2 connection@4))" },
288 { "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))" },
289 { "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))" },
290 { "delphi CreateOleObject(\"MSXML2.DomDocument\")", "((Zdelphi@1 OR createoleobject@2) OR (msxml2@3 PHRASE 2 domdocument@4))" },
291 { "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))" },
292 { "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))" },
293 { "asp ' en \"", "(Zasp@1 OR Zen@2)" },
294 { "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)))" },
295 { "session_set_cookie_params(echo \"hoi\")", "((session_set_cookie_params@1 OR Zecho@2) OR hoi@3)" },
296 { "windows update werkt niet (windows se", "((Zwindow@1 OR Zupdat@2 OR Zwerkt@3 OR Zniet@4) OR (Zwindow@5 OR Zse@6))" },
297 { "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))" },
298 { "sony +(u20 u-20)", "((Zu20@2 OR (u@3 PHRASE 2 20@4)) AND_MAYBE Zsoni@1)" },
299 { "[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)))" },
300 { "directories lokaal php (uitlezen OR inladen)", "((Zdirectori@1 OR Zlokaal@2 OR Zphp@3) OR (Zuitlezen@4 OR Zinladen@5))" },
301 { "(multi pc modem)+ (line sync)", "((Zmulti@1 OR Zpc@2 OR Zmodem@3) OR (Zline@4 OR Zsync@5))" },
302 { "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))" },
303 { "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))" },
304 { "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))" },
305 { "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))" },
306 { "\"arp -s\" ip veranderen", "((arp@1 PHRASE 2 s@2) OR (Zip@3 OR Zveranderen@4))" },
307 { "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))" },
308 { "$datum = date(\"d-m-Y\");", "((Zdatum@1 OR date@2) OR (d@3 PHRASE 3 m@4 PHRASE 3 y@5))" },
309 { "\"'\" +asp", "Zasp@1" },
310 { "+session +[", "Zsession@1" },
311 { "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))" },
312 { "\"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)" },
313 { "\"+irq +veranderen +xp\"", "(irq@1 PHRASE 3 veranderen@2 PHRASE 3 xp@3)" },
314 { "\"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)" },
315 { "mkdir() failed (File exists) php", "(((mkdir@1 OR Zfail@2) OR (file@3 OR Zexist@4)) OR Zphp@5)" },
316 { "laatsteIndex(int n)", "(laatsteindex@1 OR (Zint@2 OR Zn@3))" },
317 { "\"line+in\" OR \"c8783\"", "((line@1 PHRASE 2 in@2) OR c8783@3)" },
318 { "if ($_POST['Submit'])", "(Zif@1 OR (_post@2 OR submit@3))" },
319 { "NEC DVD+-RW ND-1300A", "((nec@1 OR (dvd+@2 PHRASE 2 rw@3)) OR (nd@4 PHRASE 2 1300a@5))" },
320 { "*String not found* (*String not found*.)", "((string@1 OR Znot@2 OR found@3) OR (string@4 OR Znot@5 OR found@6))" },
321 { "MSI G4Ti4200-TD 128MB (GeForce4 Ti4200)", "(((msi@1 OR (g4ti4200@2 PHRASE 2 td@3)) OR 128mb@4) OR (geforce4@5 OR ti4200@6))" },
322 { "href=\"#\"", "href@1" },
323 { "Request.ServerVariables(\"REMOTE_USER\") javascript", "(((request@1 PHRASE 2 servervariables@2) OR remote_user@3) OR Zjavascript@4)" },
324 { "XF86Config(-4) waar", "((xf86config@1 OR 4@2) OR Zwaar@3)" },
325 { "Unknown (tag 2000)", "(unknown@1 OR (Ztag@2 OR 2000@3))" },
326 { "KT4V(MS-6712)", "(kt4v@1 OR (ms@2 PHRASE 2 6712@3))" },
327 { "scheduled+AND+nieuwsgroepen+AND+updaten", "((Zschedul@1 AND Znieuwsgroepen@2) AND Zupdaten@3)" },
328 { "137(netbios-ns)", "(137@1 OR (netbios@2 PHRASE 2 ns@3))" },
329 { "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))" },
330 { "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)" },
331 { "wat is code van \" teken", "((Zwat@1 OR Zis@2 OR Zcode@3 OR Zvan@4) OR teken@5)" },
332 { "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))" },
333 { "Permission denied (publickey,password,keyboard-interactive).", "((permission@1 OR Zdeni@2) OR ((Zpublickey@3 OR Zpassword@4) OR (keyboard@5 PHRASE 2 interactive@6)))" },
334 { "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))" },
335 { "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)" },
336 { "\"2020 NEAR zoom\"", "(2020@1 PHRASE 3 near@2 PHRASE 3 zoom@3)" },
337 { "setcookie(\"naam\",\"$user\");", "((setcookie@1 OR naam@2) OR user@3)" },
338 { "MSI 645 Ultra (MS-6547) Ver1", "(((msi@1 OR 645@2 OR ultra@3) OR (ms@4 PHRASE 2 6547@5)) OR ver1@6)" },
339 { "if ($HTTP", "(Zif@1 OR http@2)" },
340 { "data error(cyclic redundancy check)", "((Zdata@1 OR error@2) OR (Zcyclic@3 OR Zredund@4 OR Zcheck@5))" },
341 { "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)" },
342 { "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))" },
343 { "Call Shell(\"notepad.exe\",", "((call@1 OR shell@2) OR (notepad@3 PHRASE 2 exe@4))" },
344 { "2.5\" harddisk converter", "(2.5@1 OR (harddisk@2 PHRASE 2 converter@3))" },
345 { "creative labs \"dvd+rw\"", "((Zcreativ@1 OR Zlab@2) OR (dvd@3 PHRASE 2 rw@4))" },
346 { "\"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)" },
347 { "ati radeon \"driver cleaner", "((Zati@1 OR Zradeon@2) OR (driver@3 PHRASE 2 cleaner@4))" },
348 { "\"../\" path", "Zpath@1" },
349 { "(novell client) workstation only", "((Znovel@1 OR Zclient@2) OR (Zworkstat@3 OR Zonli@4))" },
350 { "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)" },
351 { "\"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)" },
352 { "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)" },
353 { "Forwarden van domeinnaam (naar HTTP adres)", "((forwarden@1 OR Zvan@2 OR Zdomeinnaam@3) OR (Znaar@4 OR http@5 OR Zadr@6))" },
354 { "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))" },
355 { "httpd (no pid file) not running", "((Zhttpd@1 OR (Zno@2 OR Zpid@3 OR Zfile@4)) OR (Znot@5 OR Zrun@6))" },
356 { "apache httpd (pid file) not running", "(((Zapach@1 OR Zhttpd@2) OR (Zpid@3 OR Zfile@4)) OR (Znot@5 OR Zrun@6))" },
357 { "Klasse is niet geregistreerd (Fout=80040154).", "((klasse@1 OR Zis@2 OR Zniet@3 OR Zgeregistreerd@4) OR (fout@5 OR 80040154@6))" },
358 { "\"dvd+r\" \"dvd-r\"", "((dvd@1 PHRASE 2 r@2) OR (dvd@3 PHRASE 2 r@4))" },
359 { "\"=\" tekens uit csvfile", "(Zteken@1 OR Zuit@2 OR Zcsvfile@3)" },
360 { "libc.so.6(GLIBC_2.3)", "((libc@1 PHRASE 3 so@2 PHRASE 3 6@3) OR glibc_2.3@4)" },
361 { "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))" },
362 { "(t-mobile) bereik", "((t@1 PHRASE 2 mobile@2) OR Zbereik@3)" },
363 { "error LNK2001: unresolved external symbol \"public", "((Zerror@1 OR lnk2001@2 OR Zunresolv@3 OR Zextern@4 OR Zsymbol@5) OR public@6)" },
364 { "patch linux exploit -p)", "((Zpatch@1 OR Zlinux@2 OR Zexploit@3) OR Zp@4)" },
365 { "MYD not found (Errcode: 2)", "((myd@1 OR Znot@2 OR Zfound@3) OR (errcode@4 OR 2@5))" },
366 { "ob_start(\"ob_gzhandler\"); file download", "((ob_start@1 OR ob_gzhandler@2) OR (Zfile@3 OR Zdownload@4))" },
367 { "ECS Elitegroup K7VZA (VIA VT8363/VT8363A)", "((ecs@1 OR elitegroup@2 OR k7vza@3) OR (via@4 OR (vt8363@5 PHRASE 2 vt8363a@6)))" },
368 { "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))" },
369 { "Javascript:history.go(-1)", "((javascript@1 PHRASE 3 history@2 PHRASE 3 go@3) OR 1@4)" },
370 { "java :) als icon", "(Zjava@1 OR (Zal@2 OR Zicon@3))" },
371 { "onmouseover=setPointer(this", "((onmouseover@1 OR setpointer@2) OR Zthis@3)" },
372 { "\" in vbscript", "(in@1 PHRASE 2 vbscript@2)" },
373 { "IRC (FAQ OR (hulp NEAR bij))", "(irc@1 OR (faq@2 OR (hulp@3 NEAR 11 bij@4)))" },
374 { "setProperty(\"McSquare\"+i, _xscale, _xscale++);", "((((setproperty@1 OR mcsquare@2) OR Zi@3) OR _xscale@4) OR _xscale++@5)" },
375 { "[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)" },
376 { "(php.ini) (memory_limit)", "((php@1 PHRASE 2 ini@2) OR Zmemory_limit@3)" },
377 { "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)" },
378 { "VXD NAVEX()@)", "(vxd@1 OR navex@2)" },
379 { "\"Iiyama AS4314UT 17\" \"", "(iiyama@1 PHRASE 3 as4314ut@2 PHRASE 3 17@3)" },
380 { "include (\"$id.html\");", "(Zinclud@1 OR (id@2 PHRASE 2 html@3))" },
381 { "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)" },
382 { "(program files\\common) opstarten", "((Zprogram@1 OR (files@2 PHRASE 2 common@3)) OR Zopstarten@4)" },
383 { "java \" string", "(Zjava@1 OR string@2)" },
384 { "+=", "" },
385 { "php +=", "Zphp@1" },
386 { "[php] ereg_replace(\".\"", "(Zphp@1 OR ereg_replace@2)" },
387 { "\"echo -e\" kleur", "((echo@1 PHRASE 2 e@2) OR Zkleur@3)" },
388 { "adobe premiere \"-1\"", "((Zadob@1 OR Zpremier@2) OR 1@3)" },
389 { "DVD brander \"+\" en \"-\"", "((dvd@1 OR Zbrander@2) OR Zen@3)" },
390 { "inspirion \"dvd+R\"", "(Zinspirion@1 OR (dvd@2 PHRASE 2 r@3))" },
391 { "asp 0x80040E14)", "(Zasp@1 OR 0x80040e14@2)" },
392 { "\"e-tech motorola router", "(e@1 PHRASE 4 tech@2 PHRASE 4 motorola@3 PHRASE 4 router@4)" },
393 { "bluetooth '1.3.2.19\"", "(Zbluetooth@1 OR 1.3.2.19@2)" },
394 { "ms +-connect", "(Zms@1 OR Zconnect@2)" },
395 { "php+print+\"", "(Zphp@1 OR print+@2)" },
396 { "athlon 1400 :welke videokaart\"", "((Zathlon@1 OR 1400@2) OR (Zwelk@3 OR videokaart@4))" },
397 { "+-dvd", "Zdvd@1" },
398 { "glftpd \"-new-\"", "(Zglftpd@1 OR new@2)" },
399 { "\"scandisk + dos5.0", "(scandisk@1 PHRASE 2 dos5.0@2)" },
400 { "socket\\(\\)", "socket@1" },
401 { "msn (e-tech) router", "((Zmsn@1 OR (e@2 PHRASE 2 tech@3)) OR Zrouter@4)" },
402 { "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)" },
403 { "\"CF+bluetooth\"", "(cf@1 PHRASE 2 bluetooth@2)" },
404 { "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))" },
405 { "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)" },
406 { "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))" },
407 { "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)))" },
408 { "\"muziek 2x zo snel\"\"", "(muziek@1 PHRASE 4 2x@2 PHRASE 4 zo@3 PHRASE 4 snel@4)" },
409 { "execCommand('inserthorizontalrule'", "(execcommand@1 OR Zinserthorizontalrul@2)" },
410 { "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))" },
411 { "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)" },
412 { "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))" },
413 { "'\"><br>bla</br>", "(br@1 PHRASE 3 bla@2 PHRASE 3 br@3)" },
414 { "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))" },
415 { "\"(P5A-b)\"", "(p5a@1 PHRASE 2 b@2)" },
416 { "(13,5 > 13) == no-go!", "((13,5@1 OR 13@2) OR (no@3 PHRASE 2 go@4))" },
417 { "eth not found \"ifconfig -a\"", "((Zeth@1 OR Znot@2 OR Zfound@3) OR (ifconfig@4 PHRASE 2 a@5))" },
418 { "<META NAME=\"ROBOTS", "((meta@1 OR name@2) OR robots@3)" },
419 { "lp0: using parport0 (interrupt-driven)", "((Zlp0@1 OR (Zuse@2 OR Zparport0@3)) OR (interrupt@4 PHRASE 2 driven@5))" },
420 { "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)" },
421 { "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))" },
422 { "header(\"Content Type: text/html\");", "((header@1 OR (content@2 OR type@3)) OR (text@4 PHRASE 2 html@5))" },
423 { "\"-RW\" \"+RW\"", "(rw@1 OR rw@2)" },
424 { "\"cresta digital answering machine", "(cresta@1 PHRASE 4 digital@2 PHRASE 4 answering@3 PHRASE 4 machine@4)" },
425 { "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)))" },
426 { "c++ fopen \"r+t\"", "((Zc++@1 OR Zfopen@2) OR (r@3 PHRASE 2 t@4))" },
427 { "c++ fopen (r+t)", "((Zc++@1 OR Zfopen@2) OR (Zr@3 OR Zt@4))" },
428 { "\"DVD+R\"", "(dvd@1 PHRASE 2 r@2)" },
429 { "Class.forName(\"jdbc.odbc.JdbcOdbcDriver\");", "((class@1 PHRASE 2 forname@2) OR (jdbc@3 PHRASE 3 odbc@4 PHRASE 3 jdbcodbcdriver@5))" },
430 { "perl(find.pl)", "(perl@1 OR (find@2 PHRASE 2 pl@3))" },
431 { "\"-5v\" voeding", "(5v@1 OR Zvoed@2)" },
432 { "\"-5v\" power supply", "(5v@1 OR (Zpower@2 OR Zsuppli@3))" },
433 { "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))" },
434 { "(error $2108) Borland", "((Zerror@1 OR 2108@2) OR borland@3)" },
435 { "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))" },
436 { "Elektrotechniek + \"hoe bevalt het?\"\"", "(elektrotechniek@1 OR (hoe@2 PHRASE 3 bevalt@3 PHRASE 3 het@4))" },
437 { "Shortcuts in menu (java", "((shortcuts@1 OR Zin@2 OR Zmenu@3) OR Zjava@4)" },
438 { "detonator+settings\"", "(Zdeton@1 OR settings@2)" },
439 { "(ez-bios) convert", "((ez@1 PHRASE 2 bios@2) OR Zconvert@3)" },
440 { "Sparkle 7100M4 64MB (GeForce4 MX440)", "((sparkle@1 OR 7100m4@2 OR 64mb@3) OR (geforce4@4 OR mx440@5))" },
441 { "freebsd \"boek OR newbie\"", "(Zfreebsd@1 OR (boek@2 PHRASE 3 or@3 PHRASE 3 newbie@4))" },
442 { "for (;;) c++", "(Zfor@1 OR Zc++@2)" },
443 { "1700+-2100+", "(1700+@1 PHRASE 2 2100+@2)" },
444 { "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))" },
445 { "NEC DV-5800B (Bul", "((nec@1 OR (dv@2 PHRASE 2 5800b@3)) OR bul@4)" },
446 { "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))" },
447 { "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))" },
448 { "'q ben\"", "(Zq@1 OR ben@2)" },
449 { "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))" },
450 { "\xc3\xb6ude onderdelen\"", "(Z\xc3\xb6ude@1 OR onderdelen@2)" },
451 { "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))" },
452 { "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))" },
453 { "cybercom \"dvd+r\"", "(Zcybercom@1 OR (dvd@2 PHRASE 2 r@3))" },
454 { "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))" },
455 { "relais +/-", "Zrelai@1" },
456 { "formules (slepen OR doortrekken) excel", "((Zformul@1 OR (Zslepen@2 OR Zdoortrekken@3)) OR Zexcel@4)" },
457 { "\"%English", "english@1" },
458 { "select max( mysql", "((Zselect@1 OR max@2) OR Zmysql@3)" },
459 { "leejow(saait", "(leejow@1 OR Zsaait@2)" },
460 { "'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))" },
461 { "K7T Turbo 2 (MS-6330)", "((k7t@1 OR turbo@2 OR 2@3) OR (ms@4 PHRASE 2 6330@5))" },
462 { "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))" },
463 { "\"cannot find -lz\"", "(cannot@1 PHRASE 3 find@2 PHRASE 3 lz@3)" },
464 { "undefined reference to `mysql_drop_db'\"", "((Zundefin@1 OR Zrefer@2 OR Zto@3) OR Zmysql_drop_db@4)" },
465 { "search form asp \"%'", "(Zsearch@1 OR Zform@2 OR Zasp@3)" },
466 { "(dvd+r) kwaliteit", "((Zdvd@1 OR Zr@2) OR Zkwaliteit@3)" },
467 { "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))" },
468 { "geluid (schokt OR hapert)", "(Zgeluid@1 OR (Zschokt@2 OR Zhapert@3))" },
469 { "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)" },
470 { "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))" },
471 { "(library qt-mt) not found", "((Zlibrari@1 OR (qt@2 PHRASE 2 mt@3)) OR (Znot@4 OR Zfound@5))" },
472 { "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))" },
473 { "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))" },
474 { "Titan TTC-D5TB(4/CU35)", "((titan@1 OR (ttc@2 PHRASE 2 d5tb@3)) OR (4@4 PHRASE 2 cu35@5))" },
475 { "[php] date( min", "((Zphp@1 OR date@2) OR Zmin@3)" },
476 { "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)" },
477 { "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))" },
478 { "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))" },
479 { "ati linux drivers (4.3.0)", "((Zati@1 OR Zlinux@2 OR Zdriver@3) OR 4.3.0@4)" },
480 { "ENCAPSED_AND_WHITESPACE", "encapsed_and_whitespace@1" },
481 { "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))" },
482 { "welke dvd \"+r\" media", "(((Zwelk@1 OR Zdvd@2) OR r@3) OR Zmedia@4)" },
483 { "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))" },
484 { "dvd +/-", "Zdvd@1" },
485 { "7vaxp +voltage mod\"", "(Zvoltag@2 AND_MAYBE (7vaxp@1 OR mod@3))" },
486 { "lpt port (SPP/EPP) is enabled", "(((Zlpt@1 OR Zport@2) OR (spp@3 PHRASE 2 epp@4)) OR (Zis@5 OR Zenabl@6))" },
487 { "getenv(\"HTTP_REFERER\")", "(getenv@1 OR http_referer@2)" },
488 { "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)" },
489 { "Exception number: c0000005 (access violation)", "((exception@1 OR Znumber@2 OR Zc0000005@3) OR (Zaccess@4 OR Zviolat@5))" },
490 { "header(\"Content-type:application/octetstream\");", "(header@1 OR (content@2 PHRASE 4 type@3 PHRASE 4 application@4 PHRASE 4 octetstream@5))" },
491 { "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)))" },
492 { "(001.part.met", "(001@1 PHRASE 3 part@2 PHRASE 3 met@3)" },
493 { "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))" },
494 { "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)" },
495 { "dvd \"+\" \"-\"", "Zdvd@1" },
496 { "bericht ( %)", "Zbericht@1" },
497 { "2500+ of 2600+ (niett OC)", "((2500+@1 OR Zof@2 OR 2600+@3) OR (Zniett@4 OR oc@5))" },
498 { "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))" },
499 { "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))" },
500 { "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))" },
501 { "GA-8IHXP(3.0)", "((ga@1 PHRASE 2 8ihxp@2) OR 3.0@3)" },
502 { "8IHXP(3.0)", "(8ihxp@1 OR 3.0@2)" },
503 { "na\xc2\xb7si (de ~ (m.))", "(Zna\xc2\xb7si@1 OR (Zde@2 OR Zm@3))" },
504 { "header(\"Content-Disposition: attachment;", "(header@1 OR (content@2 PHRASE 3 disposition@3 PHRASE 3 attachment@4))" },
505 { "\"header(\"Content-Disposition: attachment;\"", "((header@1 OR (content@2 PHRASE 2 disposition@3)) OR Zattach@4)" },
506 { "\"Beep -f\"", "(beep@1 PHRASE 2 f@2)" },
507 { "kraan NEAR (Elektrisch OR Electrisch)", "((Zkraan@1 OR near@2) OR (elektrisch@3 OR or@4 OR electrisch@5))" },
508 { "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))" },
509 { "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))" },
510 { "ac3 (0x2000) \"Dolby Laboratories,", "((Zac3@1 OR 0x2000@2) OR (dolby@3 PHRASE 2 laboratories@4))" },
511 { "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))" },
512 { "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))" },
513 { "Motion JPEG (MJPEG codec)", "((motion@1 OR jpeg@2) OR (mjpeg@3 OR Zcodec@4))" },
514 { ": zoomtext\"", "zoomtext@1" },
515 { "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))" },
516 { "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))" },
517 { "\"\"wa is fase\"", "(Zwa@1 OR Zis@2 OR fase@3)" },
518 { "<v:imagedata src=\"", "((v@1 PHRASE 2 imagedata@2) OR src@3)" },
519 { "system(play ringin.wav); ?>", "((system@1 OR Zplay@2) OR (ringin@3 PHRASE 2 wav@4))" },
520 { "\"perfect NEAR systems\"", "(perfect@1 PHRASE 3 near@2 PHRASE 3 systems@3)" },
521 { "LoadLibrary(\"mainta/gamex86.dll\") failed", "((loadlibrary@1 OR (mainta@2 PHRASE 3 gamex86@3 PHRASE 3 dll@4)) OR Zfail@5)" },
522 { "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)" },
523 { "secundaire IDE-controller (dubbele fifo)", "((Zsecundair@1 OR (ide@2 PHRASE 2 controller@3)) OR (Zdubbel@4 OR Zfifo@5))" },
524 { "\"Postal2+Explorer.exe\"", "(postal2@1 PHRASE 3 explorer@2 PHRASE 3 exe@3)" },
525 { "COUNT(*)", "count@1" },
526 { "Nuttige Windows progs (1/11)", "((nuttige@1 OR windows@2 OR Zprog@3) OR (1@4 PHRASE 2 11@5))" },
527 { "if(usercode==passcode==)", "((if@1 OR usercode@2) OR passcode@3)" },
528 { "lg 8160b (dvd+r)", "((Zlg@1 OR 8160b@2) OR (Zdvd@3 OR Zr@4))" },
529 { "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)))" },
530 { "'ipod pakt tags niet\"", "(Zipod@1 OR Zpakt@2 OR Ztag@3 OR niet@4)" },
531 { "\"DVD+/-R\"", "(dvd+@1 PHRASE 2 r@2)" },
532 { "\"DVD+R DVD-R\"", "(dvd@1 PHRASE 4 r@2 PHRASE 4 dvd@3 PHRASE 4 r@4)" },
533 { "php ;) in een array zetten", "(Zphp@1 OR (Zin@2 OR Zeen@3 OR Zarray@4 OR Zzetten@5))" },
534 { "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)" },
535 { "creative (soundblaster OR sb) 128", "((Zcreativ@1 OR (Zsoundblast@2 OR Zsb@3)) OR 128@4)" },
536 { "Can't open file: (errno: 145)", "((can't@1 OR Zopen@2 OR Zfile@3) OR (Zerrno@4 OR 145@5))" },
537 { "Formateren lukt niet(98,XP)", "(((formateren@1 OR Zlukt@2 OR niet@3) OR 98@4) OR xp@5)" },
538 { "access denied (java.io.", "((Zaccess@1 OR Zdeni@2) OR (java@3 PHRASE 2 io@4))" },
539 { "(access denied (java.io.)", "((Zaccess@1 OR Zdeni@2) OR (java@3 PHRASE 2 io@4))" },
540 { "wil niet installeren ( crc fouten)", "((Zwil@1 OR Zniet@2 OR Zinstalleren@3) OR (Zcrc@4 OR Zfouten@5))" },
541 { "(DVD+RW) brandsoftware meerdere", "((dvd@1 OR rw@2) OR (Zbrandsoftwar@3 OR Zmeerder@4))" },
542 { "(database OF databases) EN geheugen", "((Zdatabas@1 OR of@2 OR Zdatabas@3) OR (en@4 OR Zgeheugen@5))" },
543 { "(server 2003) winroute", "((Zserver@1 OR 2003@2) OR Zwinrout@3)" },
544 { "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))" },
545 { "(draadloos OR wireless) netwerk", "((Zdraadloo@1 OR Zwireless@2) OR Znetwerk@3)" },
546 { "localtime(time(NULL));", "((localtime@1 OR time@2) OR null@3)" },
547 { "ob_start(\"ob_gzhandler\");", "(ob_start@1 OR ob_gzhandler@2)" },
548 { "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))" },
549 { "COM+-gebeurtenissysteem", "(com+@1 PHRASE 2 gebeurtenissysteem@2)" },
550 { "rcpthosts (#5.7.1)", "(Zrcpthost@1 OR 5.7.1@2)" },
551 { "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))" },
552 { "window.open( scrollbar", "((window@1 PHRASE 2 open@2) OR Zscrollbar@3)" },
553 { "T68i truc ->", "(t68i@1 OR Ztruc@2)" },
554 { "T68i ->", "t68i@1" },
555 { "\"de lijn is bezet\"\"", "(de@1 PHRASE 4 lijn@2 PHRASE 4 is@3 PHRASE 4 bezet@4)" },
556 { "if (eregi(\"", "(Zif@1 OR eregi@2)" },
557 { "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))" },
558 { "execCommand(\"Paste\");", "(execcommand@1 OR paste@2)" },
559 { "\"-1 unread\"", "(1@1 PHRASE 2 unread@2)" },
560 { "\"www.historical-fire-engines", "(www@1 PHRASE 4 historical@2 PHRASE 4 fire@3 PHRASE 4 engines@4)" },
561 { "\"DVD+RW\" erase", "((dvd@1 PHRASE 2 rw@2) OR Zeras@3)" },
562 { "[showjekamer)", "Zshowjekam@1" },
563 { "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)" },
564 { "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))" },
565 { "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))" },
566 { "vervangen # \"/", "Zvervangen@1" },
567 { "vervangen # /\"", "Zvervangen@1" },
568 { "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)" },
569 { "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))" },
570 { "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))" },
571 { "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))" },
572 { "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))" },
573 { "mag mijn waarschuwing nu weg ? ;)", "(Zmag@1 OR Zmijn@2 OR Zwaarschuw@3 OR Znu@4 OR Zweg@5)" },
574 { "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))" },
575 { "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))" },
576 { "(browser 19) citrix", "((Zbrowser@1 OR 19@2) OR Zcitrix@3)" },
577 { "preg_replace (.*?)", "Zpreg_replac@1" },
578 { "formule excel #naam\"?\"", "((Zformul@1 OR Zexcel@2) OR naam@3)" },
579 { "->", "" },
580 { "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))" },
581 { "<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))" },
582 { "\"rpm -e httpd\"", "(rpm@1 PHRASE 3 e@2 PHRASE 3 httpd@3)" },
583 { "automatisch op All Flis (*.*)", "(Zautomatisch@1 OR Zop@2 OR all@3 OR flis@4)" },
584 { "(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))" },
585 { "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))" },
586 { "\"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)" },
587 { "(./) chmod.sh", "(chmod@1 PHRASE 2 sh@2)" },
588 { "document.write(ssg(\" html", "(((document@1 PHRASE 2 write@2) OR ssg@3) OR html@4)" },
589 { "superstack \"mac+adressen\"", "(Zsuperstack@1 OR (mac@2 PHRASE 2 adressen@3))" },
590 { "IIS getenv(REMOTE_HOST)_", "(((iis@1 OR getenv@2) OR remote_host@3) OR _@4)" },
591 { "IIS en getenv(REMOTE_HOST)", "((iis@1 OR Zen@2 OR getenv@3) OR remote_host@4)" },
592 { "php getenv(\"HTTP_REFERER\")", "((Zphp@1 OR getenv@2) OR http_referer@3)" },
593 { "nec+-1300", "(nec+@1 PHRASE 2 1300@2)" },
594 { "smbpasswd script \"-s\"", "((Zsmbpasswd@1 OR Zscript@2) OR s@3)" },
595 { "leestekens \" \xc3\xb6 \xc3\xab", "(Zleesteken@1 OR (\xc3\xb6@2 PHRASE 2 \xc3\xab@3))" },
596 { "freesco and (all seeing eye)", "((Zfreesco@1 OR Zand@2) OR (Zall@3 OR Zsee@4 OR Zeye@5))" },
597 { "('all seeing eye') and freesco", "((Zall@1 OR Zsee@2 OR Zeye@3) OR (Zand@4 OR Zfreesco@5))" },
598 { "\"[......\"", "" },
599 { "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)))" },
600 { "gegevensfout (cyclishe redundantiecontrole)", "(Zgegevensfout@1 OR (Zcyclish@2 OR Zredundantiecontrol@3))" },
601 { "firmware versie waar NEC\"", "(Zfirmwar@1 OR Zversi@2 OR Zwaar@3 OR nec@4)" },
602 { "nu.nl \"-1\"", "((nu@1 PHRASE 2 nl@2) OR 1@3)" },
603 { "provider+-webspace", "(provider+@1 PHRASE 2 webspace@2)" },
604 { "verschil \"dvd+rw\" \"dvd-rw\"", "((Zverschil@1 OR (dvd@2 PHRASE 2 rw@3)) OR (dvd@4 PHRASE 2 rw@5))" },
605 { "(dhcp client) + hangt", "((Zdhcp@1 OR Zclient@2) OR Zhangt@3)" },
606 { "MSI 875P Neo-FIS2R (Intel 875P)", "(((msi@1 OR 875p@2) OR (neo@3 PHRASE 2 fis2r@4)) OR (intel@5 OR 875p@6))" },
607 { "voeding passief gekoeld\"", "(Zvoed@1 OR Zpassief@2 OR gekoeld@3)" },
608 { "if (mysql_num_rows($resultaat)==1)", "(((Zif@1 OR mysql_num_rows@2) OR Zresultaat@3) OR 1@4)" },
609 { "Server.CreateObject(\"Persits.Upload.1\")", "((server@1 PHRASE 2 createobject@2) OR (persits@3 PHRASE 3 upload@4 PHRASE 3 1@5))" },
610 { "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))" },
611 { "if (cod>9999999", "(Zif@1 OR (cod@2 OR 9999999@3))" },
612 { "\"rm -rf /bin/laden\"", "(rm@1 PHRASE 4 rf@2 PHRASE 4 bin@3 PHRASE 4 laden@4)" },
613 { "\">>> 0) & 0xFF\"", "(0@1 PHRASE 2 0xff@2)" },
614 { "<!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))" },
615 { "<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)" },
616 { "linux humor :)", "(Zlinux@1 OR Zhumor@2)" },
617 { "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))" },
618 { "remote_smtp defer (-44)", "((Zremote_smtp@1 OR Zdefer@2) OR 44@3)" },
619 { "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)" },
620 { "Koper + amoniak (NH2", "((koper@1 OR Zamoniak@2) OR nh2@3)" },
621 { "nec dvd -/+r", "((Znec@1 OR Zdvd@2) AND_NOT Zr@3)" },
622 { "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)" },
623 { "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))" },
624 { "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))" },
625 { "\"~\" + \"c:\\\"", "Zc@1" },
626 { "mysql count(*)", "(Zmysql@1 OR count@2)" },
627 { "for %f in (*.*) do", "((Zfor@1 OR (Zf@2 OR Zin@3)) OR Zdo@4)" },
628 { "raar \"~\" bestand", "(Zraar@1 OR Zbestand@2)" },
629 { "NEC DVD +-R/RW 1300", "(((nec@1 OR dvd@2) OR (r@3 PHRASE 2 rw@4)) OR 1300@5)" },
630 { "approved (ref: 38446-263)", "(Zapprov@1 OR (Zref@2 OR (38446@3 PHRASE 2 263@4)))" },
631 { "GA-7VRXP(2.0)", "((ga@1 PHRASE 2 7vrxp@2) OR 2.0@3)" },
632 { "~ Could not retrieve directory listing for \"/\"", "(could@1 OR Znot@2 OR Zretriev@3 OR Zdirectori@4 OR Zlist@5 OR Zfor@6)" },
633 { "asp CreateObject(\"Word.Document\")", "((Zasp@1 OR createobject@2) OR (word@3 PHRASE 2 document@4))" },
634 { "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))" },
635 { "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)))" },
636 { "parent.document.getElementById(\\\"leftmenu\\\").cols", "(((parent@1 PHRASE 3 document@2 PHRASE 3 getelementbyid@3) OR leftmenu@4) OR Zcol@5)" },
637 { "<% if not isEmpty(Request.QueryString) then", "(((Zif@1 OR Znot@2 OR isempty@3) OR (request@4 PHRASE 2 querystring@5)) OR Zthen@6)" },
638 { "Active Desktop (Hier issie)", "((active@1 OR desktop@2) OR (hier@3 OR Zissi@4))" },
639 { "Asus A7V8X (LAN + Sound)", "((asus@1 OR a7v8x@2) OR (lan@3 OR sound@4))" },
640 { "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)" },
641 { "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)" },
642 { "session_register(\"login\");", "(session_register@1 OR login@2)" },
643 { "\"kylix+ndmb\"", "(kylix@1 PHRASE 2 ndmb@2)" },
644 { "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))" },
645 { "If ($_SESSION[\"Login\"] == 1)", "(if@1 OR ((_session@2 OR login@3) OR 1@4))" },
646 { "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))" },
647 { "ASRock K7VT2 (incl. LAN)", "((asrock@1 OR k7vt2@2) OR (Zincl@3 OR lan@4))" },
648 { "+windows98 +(geen communicatie) +ie5", "((Zwindows98@1 AND (Zgeen@2 OR Zcommunicati@3)) AND Zie5@4)" },
649 { "\"xterm -fn\"", "(xterm@1 PHRASE 2 fn@2)" },
650 { "IRQL_NOT_LESS_OR_EQUAL", "irql_not_less_or_equal@1" },
651 { "access query \"NOT IN\"", "((Zaccess@1 OR Zqueri@2) OR (not@3 PHRASE 2 in@4))" },
652 { "\"phrase one \"phrase two\"", "((phrase@1 PHRASE 2 one@2) OR (Zphrase@3 OR two@4))" },
653 { "NEAR 207 46 249 27", "(near@1 OR 207@2 OR 46@3 OR 249@4 OR 27@5)" },
654 { "- NEAR 12V voeding", "(near@1 OR 12v@2 OR Zvoed@3)" },
655 { "waarom \"~\" in directorynaam", "(Zwaarom@1 OR (Zin@2 OR Zdirectorynaam@3))" },
656 { "cd'r NEAR toebehoren", "(cd'r@1 NEAR 11 toebehoren@2)" },
657 { "site:1 site:2", "0 * (H1 OR H2)" },
658 { "site:1 site2:2", "0 * (H1 AND J2)" },
659 { "site:1 site:2 site2:2", "0 * ((H1 OR H2) AND J2)" },
660 { "site:1 OR site:2", "(0 * H1 OR 0 * H2)" },
661 { "site:1 AND site:2", "(0 * H1 AND 0 * H2)" },
662 { "foo AND site:2", "(Zfoo@1 AND 0 * H2)" },
663 // Non-exclusive boolean prefixes feature tests (ticket#402):
664 { "category:1 category:2", "0 * (XCAT1 AND XCAT2)" },
665 { "category:1 site2:2", "0 * (XCAT1 AND J2)" },
666 { "category:1 category:2 site2:2", "0 * ((XCAT1 AND XCAT2) AND J2)" },
667 { "category:1 OR category:2", "(0 * XCAT1 OR 0 * XCAT2)" },
668 { "category:1 AND category:2", "(0 * XCAT1 AND 0 * XCAT2)" },
669 { "foo AND category:2", "(Zfoo@1 AND 0 * XCAT2)" },
670 // Regression test for combining multiple non-exclusive prefixes, fixed in
671 // 1.2.22 and 1.3.4.
672 { "category:1 dogegory:2", "0 * (XCAT1 AND XDOG2)" },
673 { "A site:1 site:2", "(a@1 FILTER (H1 OR H2))" },
674 #if 0
675 { "A (site:1 OR site:2)", "(a@1 FILTER (H1 OR H2))" },
676 #endif
677 { "A site:1 site2:2", "(a@1 FILTER (H1 AND J2))" },
678 { "A site:1 site:2 site2:2", "(a@1 FILTER ((H1 OR H2) AND J2))" },
679 #if 0
680 { "A site:1 OR site:2", "(a@1 FILTER (H1 OR H2))" },
681 #endif
682 { "A site:1 AND site:2", "((a@1 FILTER H1) AND 0 * H2)" },
683 { "site:xapian.org OR site:www.xapian.org", "(0 * Hxapian.org OR 0 * Hwww.xapian.org)" },
684 { "site:xapian.org site:www.xapian.org", "0 * (Hxapian.org OR Hwww.xapian.org)" },
685 { "site:xapian.org AND site:www.xapian.org", "(0 * Hxapian.org AND 0 * Hwww.xapian.org)" },
686 { "Xapian site:xapian.org site:www.xapian.org", "(xapian@1 FILTER (Hxapian.org OR Hwww.xapian.org))" },
687 { "author:richard author:olly writer:charlie", "(ZArichard@1 OR ZAolli@2 OR ZAcharli@3)"},
688 { "author:richard NEAR title:book", "(Arichard@1 NEAR 11 XTbook@2)"},
689 // FIXME: This throws an exception as of 1.3.6, but once implemented we
690 // should re-enable it.
691 // { "authortitle:richard NEAR title:book", "((Arichard@1 OR XTrichard@1) NEAR 11 XTbook@2)" },
692 { "multisite:xapian.org", "0 * (Hxapian.org OR Jxapian.org)"},
693 { "authortitle:richard", "(ZArichard@1 OR ZXTrichard@1)"},
694 { "multisite:xapian.org site:www.xapian.org author:richard authortitle:richard", "((ZArichard@1 OR (ZArichard@2 OR ZXTrichard@2)) FILTER ((Hxapian.org OR Jxapian.org) AND Hwww.xapian.org))" },
695 { "authortitle:richard-boulton", "((Arichard@1 PHRASE 2 Aboulton@2) OR (XTrichard@1 PHRASE 2 XTboulton@2))"},
696 { "authortitle:\"richard boulton\"", "((Arichard@1 PHRASE 2 Aboulton@2) OR (XTrichard@1 PHRASE 2 XTboulton@2))"},
697 // Test FLAG_CJK_NGRAM isn't on by default:
698 { "久有归天愿", "Z久有归天愿@1" },
699 { NULL, "CJK" }, // Enable FLAG_CJK_NGRAM
700 // Test non-CJK queries still parse the same:
701 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
702 { "“curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
703 // Test n-gram generation:
704 { "久有归天愿", "(久@1 AND 久有@1 AND 有@1 AND 有归@1 AND 归@1 AND 归天@1 AND 天@1 AND 天愿@1 AND 愿@1)" },
705 { "久有 归天愿", "((久@1 AND 久有@1 AND 有@1) OR (归@2 AND 归天@2 AND 天@2 AND 天愿@2 AND 愿@2))" },
706 { "久有!归天愿", "((久@1 AND 久有@1 AND 有@1) OR (归@2 AND 归天@2 AND 天@2 AND 天愿@2 AND 愿@2))" },
707 { "title:久有 归 天愿", "(((XT久@1 AND XT久有@1 AND XT有@1) OR 归@2) OR (天@3 AND 天愿@3 AND 愿@3))" },
708 { "h众ello万众", "(((Zh@1 OR 众@2) OR Zello@3) OR (万@4 AND 万众@4 AND 众@4))" },
709 { "世(の中)TEST_tm", "((世@1 OR (の@2 AND の中@2 AND 中@2)) OR test_tm@3)" },
710 { "다녀 AND 와야", "((다@1 AND 다녀@1 AND 녀@1) AND (와@2 AND 와야@2 AND 야@2))" },
711 { "authortitle:학술 OR 연구를", "(((A학@1 AND A학술@1 AND A술@1) OR (XT학@1 AND XT학술@1 AND XT술@1)) OR (연@2 AND 연구@2 AND 구@2 AND 구를@2 AND 를@2))" },
712 // FIXME: These should really filter by bigrams to accelerate:
713 { "\"久有归\"", "(久@1 PHRASE 3 有@1 PHRASE 3 归@1)" },
714 { "\"久有test归\"", "(久@1 PHRASE 4 有@1 PHRASE 4 test@2 PHRASE 4 归@3)" },
715 // FIXME: this should work: { "久 NEAR 有", "(久@1 NEAR 11 有@2)" },
716 { NULL, NULL }
719 DEFINE_TESTCASE(queryparser1, !backend) {
720 Xapian::QueryParser queryparser;
721 queryparser.set_stemmer(Xapian::Stem("english"));
722 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
723 queryparser.add_prefix("author", "A");
724 queryparser.add_prefix("writer", "A");
725 queryparser.add_prefix("title", "XT");
726 queryparser.add_prefix("subject", "XT");
727 queryparser.add_prefix("authortitle", "A");
728 queryparser.add_prefix("authortitle", "XT");
729 queryparser.add_boolean_prefix("site", "H");
730 queryparser.add_boolean_prefix("site2", "J");
731 queryparser.add_boolean_prefix("multisite", "H");
732 queryparser.add_boolean_prefix("multisite", "J");
733 queryparser.add_boolean_prefix("category", "XCAT", false);
734 queryparser.add_boolean_prefix("dogegory", "XDOG", false);
735 TEST_EXCEPTION(Xapian::InvalidOperationError,
736 queryparser.add_boolean_prefix("authortitle", "B");
738 TEST_EXCEPTION(Xapian::InvalidOperationError,
739 queryparser.add_prefix("multisite", "B");
741 unsigned flags = queryparser.FLAG_DEFAULT;
742 for (const test *p = test_or_queries; ; ++p) {
743 if (!p->query) {
744 if (!p->expect) break;
745 if (strcmp(p->expect, "CJK") == 0) {
746 flags = queryparser.FLAG_DEFAULT|queryparser.FLAG_CJK_NGRAM;
747 continue;
749 FAIL_TEST("Unknown flag code: " << p->expect);
751 string expect, parsed;
752 if (p->expect)
753 expect = p->expect;
754 else
755 expect = "parse error";
756 try {
757 Xapian::Query qobj = queryparser.parse_query(p->query, flags);
758 parsed = qobj.get_description();
759 expect = string("Query(") + expect + ')';
760 } catch (const Xapian::QueryParserError &e) {
761 parsed = e.get_msg();
762 } catch (const Xapian::Error &e) {
763 parsed = e.get_description();
764 } catch (...) {
765 parsed = "Unknown exception!";
767 tout << "Query: " << p->query << '\n';
768 TEST_STRINGS_EQUAL(parsed, expect);
770 return true;
773 static const test test_and_queries[] = {
774 { "internet explorer title:(http www)", "((Zinternet@1 AND Zexplor@2) AND (ZXThttp@3 AND ZXTwww@4))" },
775 // Regression test for bug in 0.9.2 and earlier - this would give
776 // (two@2 AND_MAYBE (one@1 AND three@3))
777 { "one +two three", "((Zone@1 AND Ztwo@2) AND Zthree@3)" },
778 { "hello -title:\"hello world\"", "(Zhello@1 AND_NOT (XThello@2 PHRASE 2 XTworld@3))" },
779 // Regression test for bug fixed in 1.0.4 - the '-' would be ignored there
780 // because the whitespace after the '"' wasn't noticed.
781 { "\"hello world\" -C++", "((hello@1 PHRASE 2 world@2) AND_NOT c++@3)" },
782 // Regression tests for bug fixed in 1.0.4 - queries with only boolean
783 // filter and HATE terms weren't accepted.
784 { "-cup site:world", "(0 * Hworld AND_NOT Zcup@1)" },
785 { "site:world -cup", "(0 * Hworld AND_NOT Zcup@1)" },
786 // Regression test for bug fixed in 1.0.4 - the KET token for ')' was lost.
787 { "(site:world) -cup", "(0 * Hworld AND_NOT Zcup@1)" },
788 // Regression test for bug fixed in 1.0.4 - a boolean filter term between
789 // probabilistic terms caused a parse error (probably broken during the
790 // addition of synonym support in 1.0.2).
791 { "foo site:xapian.org bar", "((Zfoo@1 AND Zbar@2) FILTER Hxapian.org)" },
792 // Add coverage for other cases similar to the above.
793 { "a b site:xapian.org", "((Za@1 AND Zb@2) FILTER Hxapian.org)" },
794 { "site:xapian.org a b", "((Za@1 AND Zb@2) FILTER Hxapian.org)" },
795 { NULL, "CJK" }, // Enable FLAG_CJK_NGRAM
796 // Test n-gram generation:
797 { "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)))" },
798 { "洛伊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)" },
799 { NULL, NULL }
802 // With default_op = OP_AND.
803 DEFINE_TESTCASE(qp_default_op1, !backend) {
804 Xapian::QueryParser queryparser;
805 queryparser.set_stemmer(Xapian::Stem("english"));
806 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
807 queryparser.add_prefix("author", "A");
808 queryparser.add_prefix("title", "XT");
809 queryparser.add_prefix("subject", "XT");
810 queryparser.add_boolean_prefix("site", "H");
811 queryparser.set_default_op(Xapian::Query::OP_AND);
812 unsigned flags = queryparser.FLAG_DEFAULT;
813 for (const test *p = test_and_queries; ; ++p) {
814 if (!p->query) {
815 if (!p->expect) break;
816 if (strcmp(p->expect, "CJK") == 0) {
817 flags = queryparser.FLAG_DEFAULT|queryparser.FLAG_CJK_NGRAM;
818 continue;
820 FAIL_TEST("Unknown flag code: " << p->expect);
822 string expect, parsed;
823 if (p->expect)
824 expect = p->expect;
825 else
826 expect = "parse error";
827 try {
828 Xapian::Query qobj = queryparser.parse_query(p->query, flags);
829 parsed = qobj.get_description();
830 expect = string("Query(") + expect + ')';
831 } catch (const Xapian::QueryParserError &e) {
832 parsed = e.get_msg();
833 } catch (const Xapian::Error &e) {
834 parsed = e.get_description();
835 } catch (...) {
836 parsed = "Unknown exception!";
838 tout << "Query: " << p->query << '\n';
839 TEST_STRINGS_EQUAL(parsed, expect);
841 return true;
844 // Feature test for specify the default prefix (new in Xapian 1.0.0).
845 DEFINE_TESTCASE(qp_default_prefix1, !backend) {
846 Xapian::QueryParser qp;
847 qp.set_stemmer(Xapian::Stem("english"));
848 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
849 qp.add_prefix("title", "XT");
851 Xapian::Query qobj;
852 qobj = qp.parse_query("hello world", 0, "A");
853 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAhello@1 OR ZAworld@2))");
854 qobj = qp.parse_query("me title:stuff", 0, "A");
855 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAme@1 OR ZXTstuff@2))");
856 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "A");
857 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZAme@2))");
858 qobj = qp.parse_query("英国 title:文森hello", qp.FLAG_CJK_NGRAM, "A");
859 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))");
860 return true;
863 // Feature test for setting the default prefix with add_prefix()
864 // (new in Xapian 1.0.3).
865 DEFINE_TESTCASE(qp_default_prefix2, !backend) {
866 Xapian::QueryParser qp;
867 qp.set_stemmer(Xapian::Stem("english"));
868 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
870 // test that default prefixes can only be set with add_prefix().
871 TEST_EXCEPTION(Xapian::UnimplementedError,
872 qp.add_boolean_prefix("", "B");
875 qp.add_prefix("title", "XT");
876 qp.add_prefix("", "A");
878 Xapian::Query qobj;
879 qobj = qp.parse_query("hello world", 0);
880 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAhello@1 OR ZAworld@2))");
881 qobj = qp.parse_query("me title:stuff", 0);
882 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAme@1 OR ZXTstuff@2))");
883 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN);
884 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZAme@2))");
886 qobj = qp.parse_query("hello world", 0, "B");
887 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZBhello@1 OR ZBworld@2))");
888 qobj = qp.parse_query("me title:stuff", 0, "B");
889 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZBme@1 OR ZXTstuff@2))");
890 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "B");
891 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZBme@2))");
893 qp.add_prefix("", "B");
894 qobj = qp.parse_query("me-us title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN);
895 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)))");
896 qobj = qp.parse_query("me-us title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "C");
897 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((((Cme@1 PHRASE 2 Cus@2) OR ZXTstuff@3) OR ZCme@4))");
899 qobj = qp.parse_query("me-us title:\"not-me\"", Xapian::QueryParser::FLAG_PHRASE);
900 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)))");
901 return true;
904 // Test query with odd characters in.
905 DEFINE_TESTCASE(qp_odd_chars1, !backend) {
906 Xapian::QueryParser qp;
907 string query("\x01weird\x00stuff\x7f", 13);
908 Xapian::Query qobj = qp.parse_query(query);
909 tout << "Query: " << query << '\n';
910 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((weird@1 OR stuff@2))"); // FIXME: should these be stemmed?
911 return true;
914 // Test right truncation.
915 DEFINE_TESTCASE(qp_flag_wildcard1, writable) {
916 Xapian::WritableDatabase db = get_writable_database();
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;
1013 // Test right truncation with prefixes.
1014 DEFINE_TESTCASE(qp_flag_wildcard2, writable) {
1015 Xapian::WritableDatabase db = get_writable_database();
1016 Xapian::Document doc;
1017 doc.add_term("Aheinlein");
1018 doc.add_term("Ahuxley");
1019 doc.add_term("hello");
1020 db.add_document(doc);
1021 Xapian::QueryParser qp;
1022 qp.set_database(db);
1023 qp.add_prefix("author", "A");
1024 Xapian::Query qobj;
1025 qobj = qp.parse_query("author:h*", Xapian::QueryParser::FLAG_WILDCARD);
1026 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR Ah))");
1027 qobj = qp.parse_query("author:h* test", Xapian::QueryParser::FLAG_WILDCARD);
1028 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR Ah) OR test@2))");
1029 return true;
1032 static void
1033 test_qp_flag_wildcard1_helper(const Xapian::Database &db,
1034 Xapian::termcount max_expansion,
1035 const string & query_string)
1037 Xapian::QueryParser qp;
1038 qp.set_database(db);
1039 qp.set_max_expansion(max_expansion);
1040 Xapian::Enquire e(db);
1041 e.set_query(qp.parse_query(query_string, Xapian::QueryParser::FLAG_WILDCARD));
1042 // The exception for expanding too much may happen at parse time or later
1043 // so we need to calculate the MSet too.
1044 e.get_mset(0, 10);
1047 // Test right truncation with a limit on expansion.
1048 DEFINE_TESTCASE(qp_flag_wildcard3, writable) {
1049 Xapian::WritableDatabase db = get_writable_database();
1050 Xapian::Document doc;
1051 doc.add_term("abc");
1052 doc.add_term("main");
1053 doc.add_term("muscat");
1054 doc.add_term("muscle");
1055 doc.add_term("musclebound");
1056 doc.add_term("muscular");
1057 doc.add_term("mutton");
1058 db.add_document(doc);
1060 // Test that a max of 0 doesn't set a limit.
1061 test_qp_flag_wildcard1_helper(db, 0, "z*");
1062 test_qp_flag_wildcard1_helper(db, 0, "m*");
1064 // These cases should expand to the limit given.
1065 test_qp_flag_wildcard1_helper(db, 1, "z*");
1066 test_qp_flag_wildcard1_helper(db, 1, "ab*");
1067 test_qp_flag_wildcard1_helper(db, 2, "muscle*");
1068 test_qp_flag_wildcard1_helper(db, 4, "musc*");
1069 test_qp_flag_wildcard1_helper(db, 4, "mus*");
1070 test_qp_flag_wildcard1_helper(db, 5, "mu*");
1071 test_qp_flag_wildcard1_helper(db, 6, "m*");
1073 // These cases should expand to one more than the limit.
1074 TEST_EXCEPTION(Xapian::WildcardError,
1075 test_qp_flag_wildcard1_helper(db, 1, "muscle*"));
1076 TEST_EXCEPTION(Xapian::WildcardError,
1077 test_qp_flag_wildcard1_helper(db, 3, "musc*"));
1078 TEST_EXCEPTION(Xapian::WildcardError,
1079 test_qp_flag_wildcard1_helper(db, 3, "mus*"));
1080 TEST_EXCEPTION(Xapian::WildcardError,
1081 test_qp_flag_wildcard1_helper(db, 4, "mu*"));
1082 TEST_EXCEPTION(Xapian::WildcardError,
1083 test_qp_flag_wildcard1_helper(db, 5, "m*"));
1085 return true;
1088 // Test partial queries.
1089 DEFINE_TESTCASE(qp_flag_partial1, writable) {
1090 Xapian::WritableDatabase db = get_writable_database();
1091 Xapian::Document doc;
1092 Xapian::Stem stemmer("english");
1093 doc.add_term("abc");
1094 doc.add_term("main");
1095 doc.add_term("muscat");
1096 doc.add_term("muscle");
1097 doc.add_term("musclebound");
1098 doc.add_term("muscular");
1099 doc.add_term("mutton");
1100 doc.add_term("Z" + stemmer("outside"));
1101 doc.add_term("Z" + stemmer("out"));
1102 doc.add_term("outside");
1103 doc.add_term("out");
1104 doc.add_term("XTcove");
1105 doc.add_term("XTcows");
1106 doc.add_term("XTcowl");
1107 doc.add_term("XTcox");
1108 doc.add_term("ZXTcow");
1109 doc.add_term("XONEpartial");
1110 doc.add_term("XONEpartial2");
1111 doc.add_term("XTWOpartial3");
1112 doc.add_term("XTWOpartial4");
1113 db.add_document(doc);
1114 Xapian::QueryParser qp;
1115 qp.set_database(db);
1116 qp.set_stemmer(stemmer);
1117 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1118 qp.add_prefix("title", "XT");
1119 qp.add_prefix("double", "XONE");
1120 qp.add_prefix("double", "XTWO");
1122 // Check behaviour with unstemmed terms
1123 Xapian::Query qobj = qp.parse_query("a", Xapian::QueryParser::FLAG_PARTIAL);
1124 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR a) OR Za@1))");
1125 qobj = qp.parse_query("ab", Xapian::QueryParser::FLAG_PARTIAL);
1126 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR ab) OR Zab@1))");
1127 qobj = qp.parse_query("muscle", Xapian::QueryParser::FLAG_PARTIAL);
1128 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR muscle) OR Zmuscl@1))");
1129 qobj = qp.parse_query("meat", Xapian::QueryParser::FLAG_PARTIAL);
1130 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR meat) OR Zmeat@1))");
1131 qobj = qp.parse_query("musc", Xapian::QueryParser::FLAG_PARTIAL);
1132 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR musc) OR Zmusc@1))");
1133 qobj = qp.parse_query("mutt", Xapian::QueryParser::FLAG_PARTIAL);
1134 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR mutt) OR Zmutt@1))");
1135 qobj = qp.parse_query("abc musc", Xapian::QueryParser::FLAG_PARTIAL);
1136 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((Zabc@1 OR ((SYNONYM WILDCARD OR musc) OR Zmusc@2)))");
1137 qobj = qp.parse_query("a* mutt", Xapian::QueryParser::FLAG_PARTIAL | Xapian::QueryParser::FLAG_WILDCARD);
1138 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR a) OR ((SYNONYM WILDCARD OR mutt) OR Zmutt@2)))");
1140 // Check behaviour with stemmed terms, and stem strategy STEM_SOME.
1141 qobj = qp.parse_query("o", Xapian::QueryParser::FLAG_PARTIAL);
1142 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR o) OR Zo@1))");
1143 qobj = qp.parse_query("ou", Xapian::QueryParser::FLAG_PARTIAL);
1144 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR ou) OR Zou@1))");
1145 qobj = qp.parse_query("out", Xapian::QueryParser::FLAG_PARTIAL);
1146 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR Zout@1))");
1147 qobj = qp.parse_query("outs", Xapian::QueryParser::FLAG_PARTIAL);
1148 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR Zout@1))");
1149 qobj = qp.parse_query("outsi", Xapian::QueryParser::FLAG_PARTIAL);
1150 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outsi) OR Zoutsi@1))");
1151 qobj = qp.parse_query("outsid", Xapian::QueryParser::FLAG_PARTIAL);
1152 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outsid) OR Zoutsid@1))");
1153 qobj = qp.parse_query("outside", Xapian::QueryParser::FLAG_PARTIAL);
1154 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR Zoutsid@1))");
1156 // Check behaviour with capitalised terms, and stem strategy STEM_SOME.
1157 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1158 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1159 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1160 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR outs@1))");
1161 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1162 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outside@1))");
1163 // FIXME: Used to be this, but we aren't currently doing this change:
1164 // TEST_STRINGS_EQUAL(qobj.get_description(), "Query(outside@1#2)");
1166 // And now with stemming strategy STEM_ALL.
1167 qp.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
1168 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1169 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1170 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1171 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR out@1))");
1172 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1173 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outsid@1))");
1175 // And now with stemming strategy STEM_ALL_Z.
1176 qp.set_stemming_strategy(Xapian::QueryParser::STEM_ALL_Z);
1177 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1178 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR Zout@1))");
1179 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1180 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR Zout@1))");
1181 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1182 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR Zoutsid@1))");
1184 // Check handling of a case with a prefix.
1185 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1186 qobj = qp.parse_query("title:cow", Xapian::QueryParser::FLAG_PARTIAL);
1187 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcow) OR ZXTcow@1))");
1188 qobj = qp.parse_query("title:cows", Xapian::QueryParser::FLAG_PARTIAL);
1189 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcows) OR ZXTcow@1))");
1190 qobj = qp.parse_query("title:Cow", Xapian::QueryParser::FLAG_PARTIAL);
1191 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcow) OR XTcow@1))");
1192 qobj = qp.parse_query("title:Cows", Xapian::QueryParser::FLAG_PARTIAL);
1193 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcows) OR XTcows@1))");
1194 // FIXME: Used to be this, but we aren't currently doing this change:
1195 // TEST_STRINGS_EQUAL(qobj.get_description(), "Query(XTcows@1#2)");
1197 // Regression test - the initial version of the multi-prefix code would
1198 // inflate the wqf of the "parsed as normal" version of a partial term
1199 // by multiplying it by the number of prefixes mapped to.
1200 qobj = qp.parse_query("double:vision", Xapian::QueryParser::FLAG_PARTIAL);
1201 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEvision SYNONYM WILDCARD OR XTWOvision) OR (ZXONEvision@1 SYNONYM ZXTWOvision@1)))");
1203 // Test handling of FLAG_PARTIAL when there's more than one prefix.
1204 qobj = qp.parse_query("double:part", Xapian::QueryParser::FLAG_PARTIAL);
1205 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpart SYNONYM WILDCARD OR XTWOpart) OR (ZXONEpart@1 SYNONYM ZXTWOpart@1)))");
1207 // Test handling of FLAG_PARTIAL when there's more than one prefix, without
1208 // stemming.
1209 qp.set_stemming_strategy(Xapian::QueryParser::STEM_NONE);
1210 qobj = qp.parse_query("double:part", Xapian::QueryParser::FLAG_PARTIAL);
1211 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpart SYNONYM WILDCARD OR XTWOpart) OR (XONEpart@1 SYNONYM XTWOpart@1)))");
1212 qobj = qp.parse_query("double:partial", Xapian::QueryParser::FLAG_PARTIAL);
1213 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpartial SYNONYM WILDCARD OR XTWOpartial) OR (XONEpartial@1 SYNONYM XTWOpartial@1)))");
1215 return true;
1218 // Tests for document counts for wildcard queries.
1219 // Regression test for bug fixed in 1.0.0.
1220 DEFINE_TESTCASE(wildquery1, backend) {
1221 Xapian::QueryParser queryparser;
1222 unsigned flags = Xapian::QueryParser::FLAG_WILDCARD |
1223 Xapian::QueryParser::FLAG_LOVEHATE;
1224 queryparser.set_stemmer(Xapian::Stem("english"));
1225 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
1226 Xapian::Database db = get_database("apitest_simpledata");
1227 queryparser.set_database(db);
1228 Xapian::Enquire enquire(db);
1230 Xapian::Query qobj = queryparser.parse_query("th*", flags);
1231 tout << qobj.get_description() << endl;
1232 enquire.set_query(qobj);
1233 Xapian::MSet mymset = enquire.get_mset(0, 10);
1234 // Check that 6 documents were returned.
1235 TEST_MSET_SIZE(mymset, 6);
1237 qobj = queryparser.parse_query("notindb* \"this\"", flags);
1238 tout << qobj.get_description() << endl;
1239 enquire.set_query(qobj);
1240 mymset = enquire.get_mset(0, 10);
1241 // Check that 6 documents were returned.
1242 TEST_MSET_SIZE(mymset, 6);
1244 qobj = queryparser.parse_query("+notindb* \"this\"", flags);
1245 tout << qobj.get_description() << endl;
1246 enquire.set_query(qobj);
1247 mymset = enquire.get_mset(0, 10);
1248 // Check that 0 documents were returned.
1249 TEST_MSET_SIZE(mymset, 0);
1251 return true;
1254 DEFINE_TESTCASE(qp_flag_bool_any_case1, !backend) {
1255 using Xapian::QueryParser;
1256 Xapian::QueryParser qp;
1257 Xapian::Query qobj;
1258 qobj = qp.parse_query("to and fro", QueryParser::FLAG_BOOLEAN | QueryParser::FLAG_BOOLEAN_ANY_CASE);
1259 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 AND fro@2))");
1260 qobj = qp.parse_query("to and fro", QueryParser::FLAG_BOOLEAN);
1261 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 OR and@2 OR fro@3))");
1262 // Regression test for bug in 0.9.4 and earlier.
1263 qobj = qp.parse_query("to And fro", QueryParser::FLAG_BOOLEAN | QueryParser::FLAG_BOOLEAN_ANY_CASE);
1264 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 AND fro@2))");
1265 qobj = qp.parse_query("to And fro", QueryParser::FLAG_BOOLEAN);
1266 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 OR and@2 OR fro@3))");
1267 return true;
1270 static const test test_stop_queries[] = {
1271 { "test the queryparser", "(test@1 AND queryparser@3)" },
1272 // Regression test for bug in 0.9.6 and earlier. This would fail to
1273 // parse.
1274 { "test AND the AND queryparser", "((test@1 AND the@2) AND queryparser@3)" },
1275 // 0.9.6 and earlier ignored a stopword even if it was the only term.
1276 // More recent versions don't ever treat a single term as a stopword.
1277 { "the", "the@1" },
1278 // 1.2.2 and earlier ignored an all-stopword query with multiple terms,
1279 // which prevents 'to be or not to be' for being searchable unless the
1280 // user made it into a phrase query or prefixed all terms with '+'
1281 // (ticket#245).
1282 { "an the a", "(an@1 AND the@2 AND a@3)" },
1283 // Regression test for bug in initial version of the patch for the
1284 // "all-stopword" case.
1285 { "the AND a an", "(the@1 AND (a@2 AND an@3))" },
1286 { NULL, NULL }
1289 DEFINE_TESTCASE(qp_stopper1, !backend) {
1290 Xapian::QueryParser qp;
1291 const char * stopwords[] = { "a", "an", "the" };
1292 Xapian::SimpleStopper stop(stopwords, stopwords + 3);
1293 qp.set_stopper(&stop);
1294 qp.set_default_op(Xapian::Query::OP_AND);
1295 for (const test *p = test_stop_queries; p->query; ++p) {
1296 string expect, parsed;
1297 if (p->expect)
1298 expect = p->expect;
1299 else
1300 expect = "parse error";
1301 try {
1302 Xapian::Query qobj = qp.parse_query(p->query);
1303 parsed = qobj.get_description();
1304 expect = string("Query(") + expect + ')';
1305 } catch (const Xapian::QueryParserError &e) {
1306 parsed = e.get_msg();
1307 } catch (const Xapian::Error &e) {
1308 parsed = e.get_description();
1309 } catch (...) {
1310 parsed = "Unknown exception!";
1312 tout << "Query: " << p->query << '\n';
1313 TEST_STRINGS_EQUAL(parsed, expect);
1315 return true;
1318 static const test test_pure_not_queries[] = {
1319 { "NOT windows", "(<alldocuments> AND_NOT Zwindow@1)" },
1320 { "a AND (NOT b)", "(Za@1 AND (<alldocuments> AND_NOT Zb@2))" },
1321 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
1322 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
1323 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
1324 { NULL, NULL }
1327 DEFINE_TESTCASE(qp_flag_pure_not1, !backend) {
1328 using Xapian::QueryParser;
1329 Xapian::QueryParser qp;
1330 qp.set_stemmer(Xapian::Stem("english"));
1331 qp.set_stemming_strategy(QueryParser::STEM_SOME);
1332 for (const test *p = test_pure_not_queries; p->query; ++p) {
1333 string expect, parsed;
1334 if (p->expect)
1335 expect = p->expect;
1336 else
1337 expect = "parse error";
1338 try {
1339 Xapian::Query qobj = qp.parse_query(p->query,
1340 QueryParser::FLAG_BOOLEAN |
1341 QueryParser::FLAG_PURE_NOT);
1342 parsed = qobj.get_description();
1343 expect = string("Query(") + expect + ')';
1344 } catch (const Xapian::QueryParserError &e) {
1345 parsed = e.get_msg();
1346 } catch (const Xapian::Error &e) {
1347 parsed = e.get_description();
1348 } catch (...) {
1349 parsed = "Unknown exception!";
1351 tout << "Query: " << p->query << '\n';
1352 TEST_STRINGS_EQUAL(parsed, expect);
1354 return true;
1357 // Debatable if this is a regression test or a feature test, as it's not
1358 // obvious is this was a bug fix or a new feature. Either way, it first
1359 // appeared in Xapian 1.0.0.
1360 DEFINE_TESTCASE(qp_unstem_boolean_prefix, !backend) {
1361 Xapian::QueryParser qp;
1362 qp.add_boolean_prefix("test", "XTEST");
1363 Xapian::Query q = qp.parse_query("hello test:foo");
1364 TEST_STRINGS_EQUAL(q.get_description(), "Query((hello@1 FILTER XTESTfoo))");
1365 Xapian::TermIterator u = qp.unstem_begin("XTESTfoo");
1366 TEST(u != qp.unstem_end("XTESTfoo"));
1367 TEST_EQUAL(*u, "test:foo");
1368 ++u;
1369 TEST(u == qp.unstem_end("XTESTfoo"));
1370 return true;
1373 static const test test_value_range1_queries[] = {
1374 { "a..b", "0 * VALUE_RANGE 1 a b" },
1375 { "$50..100", "0 * VALUE_RANGE 1 $50 100" },
1376 { "$50..$99", "0 * VALUE_RANGE 1 $50 $99" },
1377 { "$50..$100", "" }, // start > range
1378 { "02/03/1979..10/12/1980", "0 * VALUE_RANGE 1 02/03/1979 10/12/1980" },
1379 { "a..b hello", "(hello@1 FILTER VALUE_RANGE 1 a b)" },
1380 { "hello a..b", "(hello@1 FILTER VALUE_RANGE 1 a b)" },
1381 { "hello a..b world", "((hello@1 OR world@2) FILTER VALUE_RANGE 1 a b)" },
1382 { "hello a..b test:foo", "(hello@1 FILTER (VALUE_RANGE 1 a b AND XTESTfoo))" },
1383 { "hello a..b test:foo test:bar", "(hello@1 FILTER (VALUE_RANGE 1 a b AND (XTESTfoo OR XTESTbar)))" },
1384 { "hello a..b c..d test:foo", "(hello@1 FILTER ((VALUE_RANGE 1 a b OR VALUE_RANGE 1 c d) AND XTESTfoo))" },
1385 { "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)))" },
1386 { "-5..7", "0 * VALUE_RANGE 1 -5 7" },
1387 { "hello -5..7", "(hello@1 FILTER VALUE_RANGE 1 -5 7)" },
1388 { "-5..7 hello", "(hello@1 FILTER VALUE_RANGE 1 -5 7)" },
1389 { "\"time flies\" 09:00..12:30", "((time@1 PHRASE 2 flies@2) FILTER VALUE_RANGE 1 09:00 12:30)" },
1390 // Feature test for single-ended ranges (ticket#480):
1391 { "..b", "0 * VALUE_LE 1 b" },
1392 { "a..", "0 * VALUE_GE 1 a" },
1393 // Test for expanded set of characters allowed in range start:
1394 { "10:30+1300..11:00+1300", "0 * VALUE_RANGE 1 10:30+1300 11:00+1300" },
1395 { NULL, NULL }
1398 // Simple test of ValueRangeProcessor class.
1399 DEFINE_TESTCASE(qp_value_range1, !backend) {
1400 Xapian::QueryParser qp;
1401 qp.add_boolean_prefix("test", "XTEST");
1402 Xapian::StringValueRangeProcessor vrp(1);
1403 qp.add_valuerangeprocessor(&vrp);
1404 for (const test *p = test_value_range1_queries; p->query; ++p) {
1405 string expect, parsed;
1406 if (p->expect)
1407 expect = p->expect;
1408 else
1409 expect = "parse error";
1410 try {
1411 Xapian::Query qobj = qp.parse_query(p->query);
1412 parsed = qobj.get_description();
1413 expect = string("Query(") + expect + ')';
1414 } catch (const Xapian::QueryParserError &e) {
1415 parsed = e.get_msg();
1416 } catch (const Xapian::Error &e) {
1417 parsed = e.get_description();
1418 } catch (...) {
1419 parsed = "Unknown exception!";
1421 tout << "Query: " << p->query << '\n';
1422 TEST_STRINGS_EQUAL(parsed, expect);
1424 return true;
1427 // Simple test of RangeProcessor class.
1428 DEFINE_TESTCASE(qp_range1, !backend) {
1429 Xapian::QueryParser qp;
1430 qp.add_boolean_prefix("test", "XTEST");
1431 Xapian::RangeProcessor rp(1);
1432 qp.add_rangeprocessor(&rp);
1433 for (const test *p = test_value_range1_queries; p->query; ++p) {
1434 string expect, parsed;
1435 if (p->expect)
1436 expect = p->expect;
1437 else
1438 expect = "parse error";
1439 try {
1440 Xapian::Query qobj = qp.parse_query(p->query);
1441 parsed = qobj.get_description();
1442 expect = string("Query(") + expect + ')';
1443 } catch (const Xapian::QueryParserError &e) {
1444 parsed = e.get_msg();
1445 } catch (const Xapian::Error &e) {
1446 parsed = e.get_description();
1447 } catch (...) {
1448 parsed = "Unknown exception!";
1450 tout << "Query: " << p->query << '\n';
1451 TEST_STRINGS_EQUAL(parsed, expect);
1453 return true;
1456 static const test test_value_range2_queries[] = {
1457 { "a..b", "0 * VALUE_RANGE 3 a b" },
1458 { "1..12", "0 * VALUE_RANGE 2 \\xa0 \\xae" },
1459 { "20070201..20070228", "0 * VALUE_RANGE 1 20070201 20070228" },
1460 { "$10..20", "0 * VALUE_RANGE 4 \\xad \\xb1" },
1461 { "$10..$20", "0 * VALUE_RANGE 4 \\xad \\xb1" },
1462 // Feature test for single-ended ranges (ticket#480):
1463 { "$..20", "0 * VALUE_LE 4 \\xb1" },
1464 { "..$20", "0 * VALUE_LE 3 $20" }, // FIXME: probably should parse as $..20
1465 { "$10..", "0 * VALUE_GE 4 \\xad" },
1466 { "12..42kg", "0 * VALUE_RANGE 5 \\xae \\xb5@" },
1467 { "12kg..42kg", "0 * VALUE_RANGE 5 \\xae \\xb5@" },
1468 { "12kg..42", "0 * VALUE_RANGE 3 12kg 42" },
1469 { "10..$20", "" }, // start > end
1470 { "1999-03-12..2020-12-30", "0 * VALUE_RANGE 1 19990312 20201230" },
1471 { "1999/03/12..2020/12/30", "0 * VALUE_RANGE 1 19990312 20201230" },
1472 { "1999.03.12..2020.12.30", "0 * VALUE_RANGE 1 19990312 20201230" },
1473 // Feature test for single-ended ranges (ticket#480):
1474 { "..2020.12.30", "0 * VALUE_LE 1 20201230" },
1475 { "1999.03.12..", "0 * VALUE_GE 1 19990312" },
1476 { "12/03/99..12/04/01", "0 * VALUE_RANGE 1 19990312 20010412" },
1477 { "03-12-99..04-14-01", "0 * VALUE_RANGE 1 19990312 20010414" },
1478 { "(test:a..test:b hello)", "(hello@1 FILTER VALUE_RANGE 3 test:a test:b)" },
1479 { "12..42kg 5..6kg 1..12", "0 * (VALUE_RANGE 2 \\xa0 \\xae AND (VALUE_RANGE 5 \\xae \\xb5@ OR VALUE_RANGE 5 \\xa9 \\xaa))" },
1480 // Check that a VRP which fails to match doesn't remove a prefix or suffix.
1481 // 1.0.13/1.1.1 and earlier got this wrong in some cases.
1482 { "$12a..13", "0 * VALUE_RANGE 3 $12a 13" },
1483 { "$12..13b", "0 * VALUE_RANGE 3 $12 13b" },
1484 { "$12..12kg", "0 * VALUE_RANGE 3 $12 12kg" },
1485 { "12..b12kg", "0 * VALUE_RANGE 3 12 b12kg" },
1486 { NULL, NULL }
1489 // Test chaining of ValueRangeProcessor classes.
1490 DEFINE_TESTCASE(qp_value_range2, !backend) {
1491 Xapian::QueryParser qp;
1492 qp.add_boolean_prefix("test", "XTEST");
1493 Xapian::DateValueRangeProcessor vrp_date(1);
1494 Xapian::NumberValueRangeProcessor vrp_num(2);
1495 Xapian::StringValueRangeProcessor vrp_str(3);
1496 Xapian::NumberValueRangeProcessor vrp_cash(4, "$");
1497 Xapian::NumberValueRangeProcessor vrp_weight(5, "kg", false);
1498 qp.add_valuerangeprocessor(&vrp_date);
1499 qp.add_valuerangeprocessor(&vrp_num);
1500 qp.add_valuerangeprocessor(&vrp_cash);
1501 qp.add_valuerangeprocessor(&vrp_weight);
1502 qp.add_valuerangeprocessor(&vrp_str);
1503 for (const test *p = test_value_range2_queries; p->query; ++p) {
1504 string expect, parsed;
1505 if (p->expect)
1506 expect = p->expect;
1507 else
1508 expect = "parse error";
1509 try {
1510 Xapian::Query qobj = qp.parse_query(p->query);
1511 parsed = qobj.get_description();
1512 expect = string("Query(") + expect + ')';
1513 } catch (const Xapian::QueryParserError &e) {
1514 parsed = e.get_msg();
1515 } catch (const Xapian::Error &e) {
1516 parsed = e.get_description();
1517 } catch (...) {
1518 parsed = "Unknown exception!";
1520 tout << "Query: " << p->query << '\n';
1521 TEST_STRINGS_EQUAL(parsed, expect);
1523 return true;
1526 // Test chaining of RangeProcessor classes.
1527 DEFINE_TESTCASE(qp_range2, !backend) {
1528 using Xapian::RP_REPEATED;
1529 using Xapian::RP_SUFFIX;
1530 Xapian::QueryParser qp;
1531 qp.add_boolean_prefix("test", "XTEST");
1532 Xapian::DateRangeProcessor rp_date(1);
1533 Xapian::NumberRangeProcessor rp_num(2);
1534 Xapian::RangeProcessor rp_str(3);
1535 Xapian::NumberRangeProcessor rp_cash(4, "$", RP_REPEATED);
1536 Xapian::NumberRangeProcessor rp_weight(5, "kg", RP_SUFFIX|RP_REPEATED);
1537 qp.add_rangeprocessor(&rp_date);
1538 qp.add_rangeprocessor(&rp_num);
1539 qp.add_rangeprocessor(&rp_cash);
1540 qp.add_rangeprocessor(&rp_weight);
1541 qp.add_rangeprocessor(&rp_str);
1542 for (const test *p = test_value_range2_queries; p->query; ++p) {
1543 string expect, parsed;
1544 if (p->expect)
1545 expect = p->expect;
1546 else
1547 expect = "parse error";
1548 try {
1549 Xapian::Query qobj = qp.parse_query(p->query);
1550 parsed = qobj.get_description();
1551 expect = string("Query(") + expect + ')';
1552 } catch (const Xapian::QueryParserError &e) {
1553 parsed = e.get_msg();
1554 } catch (const Xapian::Error &e) {
1555 parsed = e.get_description();
1556 } catch (...) {
1557 parsed = "Unknown exception!";
1559 tout << "Query: " << p->query << '\n';
1560 TEST_STRINGS_EQUAL(parsed, expect);
1562 return true;
1565 // Test NumberValueRangeProcessors with actual data.
1566 DEFINE_TESTCASE(qp_value_range3, writable) {
1567 Xapian::WritableDatabase db = get_writable_database();
1568 double low = -10;
1569 int steps = 60;
1570 double step = 0.5;
1572 for (int i = 0; i <= steps; ++i) {
1573 double v = low + i * step;
1574 Xapian::Document doc;
1575 doc.add_value(1, Xapian::sortable_serialise(v));
1576 db.add_document(doc);
1579 Xapian::NumberValueRangeProcessor vrp_num(1);
1580 Xapian::QueryParser qp;
1581 qp.add_valuerangeprocessor(&vrp_num);
1583 for (int j = 0; j <= steps; ++j) {
1584 double start = low + j * step;
1585 for (int k = 0; k <= steps; ++k) {
1586 double end = low + k * step;
1587 string query = str(start) + ".." + str(end);
1588 tout << "Query: " << query << '\n';
1589 Xapian::Query qobj = qp.parse_query(query);
1590 Xapian::Enquire enq(db);
1591 enq.set_query(qobj);
1592 Xapian::MSet mset = enq.get_mset(0, steps + 1);
1593 if (end < start) {
1594 TEST_EQUAL(mset.size(), 0);
1595 } else {
1596 TEST_EQUAL(mset.size(), 1u + (k - j));
1597 for (unsigned int m = 0; m != mset.size(); ++m) {
1598 double v = start + m * step;
1599 TEST_EQUAL(mset[m].get_document().get_value(1),
1600 Xapian::sortable_serialise(v));
1605 return true;
1608 // Test NumberRangeProcessors with actual data.
1609 DEFINE_TESTCASE(qp_range3, writable) {
1610 Xapian::WritableDatabase db = get_writable_database();
1611 double low = -10;
1612 int steps = 60;
1613 double step = 0.5;
1615 for (int i = 0; i <= steps; ++i) {
1616 double v = low + i * step;
1617 Xapian::Document doc;
1618 doc.add_value(1, Xapian::sortable_serialise(v));
1619 db.add_document(doc);
1622 Xapian::NumberRangeProcessor rp_num(1);
1623 Xapian::QueryParser qp;
1624 qp.add_rangeprocessor(&rp_num);
1626 for (int j = 0; j <= steps; ++j) {
1627 double start = low + j * step;
1628 for (int k = 0; k <= steps; ++k) {
1629 double end = low + k * step;
1630 string query = str(start) + ".." + str(end);
1631 tout << "Query: " << query << '\n';
1632 Xapian::Query qobj = qp.parse_query(query);
1633 Xapian::Enquire enq(db);
1634 enq.set_query(qobj);
1635 Xapian::MSet mset = enq.get_mset(0, steps + 1);
1636 if (end < start) {
1637 TEST_EQUAL(mset.size(), 0);
1638 } else {
1639 TEST_EQUAL(mset.size(), 1u + (k - j));
1640 for (unsigned int m = 0; m != mset.size(); ++m) {
1641 double v = start + m * step;
1642 TEST_EQUAL(mset[m].get_document().get_value(1),
1643 Xapian::sortable_serialise(v));
1648 return true;
1651 static const test test_value_range4_queries[] = {
1652 { "id:19254@foo..example.com", "0 * Q19254@foo..example.com" },
1653 { "hello:world", "0 * XHELLOworld" },
1654 { "hello:mum..world", "0 * VALUE_RANGE 1 mum world" },
1655 { NULL, NULL }
1658 /** Test a boolean filter which happens to contain "..".
1660 * Regression test for bug fixed in 1.2.3.
1662 * Also test that the same prefix can be set for a valuerange and filter.
1664 DEFINE_TESTCASE(qp_value_range4, !backend) {
1665 Xapian::QueryParser qp;
1666 qp.add_boolean_prefix("id", "Q");
1667 qp.add_boolean_prefix("hello", "XHELLO");
1668 Xapian::StringValueRangeProcessor vrp_str(1, "hello:");
1669 qp.add_valuerangeprocessor(&vrp_str);
1670 for (const test *p = test_value_range4_queries; p->query; ++p) {
1671 string expect, parsed;
1672 if (p->expect)
1673 expect = p->expect;
1674 else
1675 expect = "parse error";
1676 try {
1677 Xapian::Query qobj = qp.parse_query(p->query);
1678 parsed = qobj.get_description();
1679 expect = string("Query(") + expect + ')';
1680 } catch (const Xapian::QueryParserError &e) {
1681 parsed = e.get_msg();
1682 } catch (const Xapian::Error &e) {
1683 parsed = e.get_description();
1684 } catch (...) {
1685 parsed = "Unknown exception!";
1687 tout << "Query: " << p->query << '\n';
1688 TEST_STRINGS_EQUAL(parsed, expect);
1690 return true;
1693 /** Test a boolean filter which happens to contain "..".
1695 * Regression test for bug fixed in 1.2.3.
1697 * Also test that the same prefix can be set for a range and filter.
1699 DEFINE_TESTCASE(qp_range4, !backend) {
1700 Xapian::QueryParser qp;
1701 qp.add_boolean_prefix("id", "Q");
1702 qp.add_boolean_prefix("hello", "XHELLO");
1703 Xapian::RangeProcessor rp_str(1, "hello:");
1704 qp.add_rangeprocessor(&rp_str);
1705 for (const test *p = test_value_range4_queries; p->query; ++p) {
1706 string expect, parsed;
1707 if (p->expect)
1708 expect = p->expect;
1709 else
1710 expect = "parse error";
1711 try {
1712 Xapian::Query qobj = qp.parse_query(p->query);
1713 parsed = qobj.get_description();
1714 expect = string("Query(") + expect + ')';
1715 } catch (const Xapian::QueryParserError &e) {
1716 parsed = e.get_msg();
1717 } catch (const Xapian::Error &e) {
1718 parsed = e.get_description();
1719 } catch (...) {
1720 parsed = "Unknown exception!";
1722 tout << "Query: " << p->query << '\n';
1723 TEST_STRINGS_EQUAL(parsed, expect);
1725 return true;
1728 static const test test_value_daterange1_queries[] = {
1729 { "12/03/99..12/04/01", "0 * VALUE_RANGE 1 19991203 20011204" },
1730 { "03-12-99..04-14-01", "0 * VALUE_RANGE 1 19990312 20010414" },
1731 { "01/30/60..02/02/59", "0 * VALUE_RANGE 1 19600130 20590202" },
1732 { "1999-03-12..2001-04-14", "0 * VALUE_RANGE 1 19990312 20010414" },
1733 { "12/03/99..02", "Unknown range operation" },
1734 { "1999-03-12..2001", "Unknown range operation" },
1735 { NULL, NULL }
1738 // Test DateValueRangeProcessor
1739 DEFINE_TESTCASE(qp_value_daterange1, !backend) {
1740 Xapian::QueryParser qp;
1741 Xapian::DateValueRangeProcessor vrp_date(1, true, 1960);
1742 qp.add_valuerangeprocessor(&vrp_date);
1743 for (const test *p = test_value_daterange1_queries; p->query; ++p) {
1744 string expect, parsed;
1745 if (p->expect)
1746 expect = p->expect;
1747 else
1748 expect = "parse error";
1749 try {
1750 Xapian::Query qobj = qp.parse_query(p->query);
1751 parsed = qobj.get_description();
1752 expect = string("Query(") + expect + ')';
1753 } catch (const Xapian::QueryParserError &e) {
1754 parsed = e.get_msg();
1755 } catch (const Xapian::Error &e) {
1756 parsed = e.get_description();
1757 } catch (...) {
1758 parsed = "Unknown exception!";
1760 tout << "Query: " << p->query << '\n';
1761 TEST_STRINGS_EQUAL(parsed, expect);
1763 return true;
1766 // Test DateRangeProcessor
1767 DEFINE_TESTCASE(qp_daterange1, !backend) {
1768 Xapian::QueryParser qp;
1769 Xapian::DateRangeProcessor rp_date(1, Xapian::RP_DATE_PREFER_MDY, 1960);
1770 qp.add_rangeprocessor(&rp_date);
1771 for (const test *p = test_value_daterange1_queries; p->query; ++p) {
1772 string expect, parsed;
1773 if (p->expect)
1774 expect = p->expect;
1775 else
1776 expect = "parse error";
1777 try {
1778 Xapian::Query qobj = qp.parse_query(p->query);
1779 parsed = qobj.get_description();
1780 expect = string("Query(") + expect + ')';
1781 } catch (const Xapian::QueryParserError &e) {
1782 parsed = e.get_msg();
1783 } catch (const Xapian::Error &e) {
1784 parsed = e.get_description();
1785 } catch (...) {
1786 parsed = "Unknown exception!";
1788 tout << "Query: " << p->query << '\n';
1789 TEST_STRINGS_EQUAL(parsed, expect);
1791 return true;
1794 static const test test_value_daterange2_queries[] = {
1795 { "created:12/03/99..12/04/01", "0 * VALUE_RANGE 1 19991203 20011204" },
1796 { "modified:03-12-99..04-14-01", "0 * VALUE_RANGE 2 19990312 20010414" },
1797 { "accessed:01/30/70..02/02/69", "0 * VALUE_RANGE 3 19700130 20690202" },
1798 // In <=1.2.12, and in 1.3.0, this gave "Unknown range operation":
1799 { "deleted:12/03/99..12/04/01", "0 * VALUE_RANGE 4 19990312 20010412" },
1800 { "1999-03-12..2001-04-14", "Unknown range operation" },
1801 { "12/03/99..created:12/04/01", "Unknown range operation" },
1802 { "12/03/99created:..12/04/01", "Unknown range operation" },
1803 { "12/03/99..12/04/01created:", "Unknown range operation" },
1804 { "12/03/99..02", "Unknown range operation" },
1805 { "1999-03-12..2001", "Unknown range operation" },
1806 { NULL, NULL }
1809 // Feature test DateValueRangeProcessor with prefixes (added in 1.1.2).
1810 DEFINE_TESTCASE(qp_value_daterange2, !backend) {
1811 Xapian::QueryParser qp;
1812 Xapian::DateValueRangeProcessor vrp_cdate(1, "created:", true, true, 1970);
1813 Xapian::DateValueRangeProcessor vrp_mdate(2, "modified:", true, true, 1970);
1814 Xapian::DateValueRangeProcessor vrp_adate(3, "accessed:", true, true, 1970);
1815 // Regression test - here a const char * was taken as a bool rather than a
1816 // std::string when resolving the overloaded forms. Fixed in 1.2.13 and
1817 // 1.3.1.
1818 Xapian::DateValueRangeProcessor vrp_ddate(4, "deleted:");
1819 qp.add_valuerangeprocessor(&vrp_cdate);
1820 qp.add_valuerangeprocessor(&vrp_mdate);
1821 qp.add_valuerangeprocessor(&vrp_adate);
1822 qp.add_valuerangeprocessor(&vrp_ddate);
1823 for (const test *p = test_value_daterange2_queries; p->query; ++p) {
1824 string expect, parsed;
1825 if (p->expect)
1826 expect = p->expect;
1827 else
1828 expect = "parse error";
1829 try {
1830 Xapian::Query qobj = qp.parse_query(p->query);
1831 parsed = qobj.get_description();
1832 expect = string("Query(") + expect + ')';
1833 } catch (const Xapian::QueryParserError &e) {
1834 parsed = e.get_msg();
1835 } catch (const Xapian::Error &e) {
1836 parsed = e.get_description();
1837 } catch (...) {
1838 parsed = "Unknown exception!";
1840 tout << "Query: " << p->query << '\n';
1841 TEST_STRINGS_EQUAL(parsed, expect);
1843 return true;
1846 // Feature test DateRangeProcessor with prefixes (added in 1.1.2).
1847 DEFINE_TESTCASE(qp_daterange2, !backend) {
1848 using Xapian::RP_DATE_PREFER_MDY;
1849 Xapian::QueryParser qp;
1850 Xapian::DateRangeProcessor rp_cdate(1, "created:", RP_DATE_PREFER_MDY, 1970);
1851 Xapian::DateRangeProcessor rp_mdate(2, "modified:", RP_DATE_PREFER_MDY, 1970);
1852 Xapian::DateRangeProcessor rp_adate(3, "accessed:", RP_DATE_PREFER_MDY, 1970);
1853 // Regression test - here a const char * was taken as a bool rather than a
1854 // std::string when resolving the overloaded forms. Fixed in 1.2.13 and
1855 // 1.3.1.
1856 Xapian::DateRangeProcessor rp_ddate(4, "deleted:");
1857 qp.add_rangeprocessor(&rp_cdate);
1858 qp.add_rangeprocessor(&rp_mdate);
1859 qp.add_rangeprocessor(&rp_adate);
1860 qp.add_rangeprocessor(&rp_ddate);
1861 for (const test *p = test_value_daterange2_queries; p->query; ++p) {
1862 string expect, parsed;
1863 if (p->expect)
1864 expect = p->expect;
1865 else
1866 expect = "parse error";
1867 try {
1868 Xapian::Query qobj = qp.parse_query(p->query);
1869 parsed = qobj.get_description();
1870 expect = string("Query(") + expect + ')';
1871 } catch (const Xapian::QueryParserError &e) {
1872 parsed = e.get_msg();
1873 } catch (const Xapian::Error &e) {
1874 parsed = e.get_description();
1875 } catch (...) {
1876 parsed = "Unknown exception!";
1878 tout << "Query: " << p->query << '\n';
1879 TEST_STRINGS_EQUAL(parsed, expect);
1881 return true;
1884 static const test test_value_stringrange1_queries[] = {
1885 { "tag:bar..foo", "0 * VALUE_RANGE 1 bar foo" },
1886 { "bar..foo", "0 * VALUE_RANGE 0 bar foo" },
1887 { NULL, NULL }
1890 // Feature test StringValueRangeProcessor with prefixes (added in 1.1.2).
1891 DEFINE_TESTCASE(qp_value_stringrange1, !backend) {
1892 Xapian::QueryParser qp;
1893 Xapian::StringValueRangeProcessor vrp_default(0);
1894 Xapian::StringValueRangeProcessor vrp_tag(1, "tag:", true);
1895 qp.add_valuerangeprocessor(&vrp_tag);
1896 qp.add_valuerangeprocessor(&vrp_default);
1897 for (const test *p = test_value_stringrange1_queries; p->query; ++p) {
1898 string expect, parsed;
1899 if (p->expect)
1900 expect = p->expect;
1901 else
1902 expect = "parse error";
1903 try {
1904 Xapian::Query qobj = qp.parse_query(p->query);
1905 parsed = qobj.get_description();
1906 expect = string("Query(") + expect + ')';
1907 } catch (const Xapian::QueryParserError &e) {
1908 parsed = e.get_msg();
1909 } catch (const Xapian::Error &e) {
1910 parsed = e.get_description();
1911 } catch (...) {
1912 parsed = "Unknown exception!";
1914 tout << "Query: " << p->query << '\n';
1915 TEST_STRINGS_EQUAL(parsed, expect);
1917 return true;
1920 // Feature test RangeProcessor with prefixes.
1921 DEFINE_TESTCASE(qp_stringrange1, !backend) {
1922 Xapian::QueryParser qp;
1923 Xapian::RangeProcessor rp_default(0);
1924 Xapian::RangeProcessor rp_tag(1, "tag:");
1925 qp.add_rangeprocessor(&rp_tag);
1926 qp.add_rangeprocessor(&rp_default);
1927 for (const test *p = test_value_stringrange1_queries; p->query; ++p) {
1928 string expect, parsed;
1929 if (p->expect)
1930 expect = p->expect;
1931 else
1932 expect = "parse error";
1933 try {
1934 Xapian::Query qobj = qp.parse_query(p->query);
1935 parsed = qobj.get_description();
1936 expect = string("Query(") + expect + ')';
1937 } catch (const Xapian::QueryParserError &e) {
1938 parsed = e.get_msg();
1939 } catch (const Xapian::Error &e) {
1940 parsed = e.get_description();
1941 } catch (...) {
1942 parsed = "Unknown exception!";
1944 tout << "Query: " << p->query << '\n';
1945 TEST_STRINGS_EQUAL(parsed, expect);
1947 return true;
1950 static const test test_value_customrange1_queries[] = {
1951 { "mars author:Asimov..Bradbury", "(mars@1 FILTER VALUE_RANGE 4 asimov bradbury)" },
1952 { NULL, NULL }
1955 struct AuthorValueRangeProcessor : public Xapian::ValueRangeProcessor {
1956 AuthorValueRangeProcessor() {}
1958 Xapian::valueno operator()(std::string &begin, std::string &end) {
1959 if (!startswith(begin, "author:"))
1960 return Xapian::BAD_VALUENO;
1961 begin.erase(0, 7);
1962 begin = Xapian::Unicode::tolower(begin);
1963 end = Xapian::Unicode::tolower(end);
1964 return 4;
1968 // Test custom ValueRangeProcessor subclass.
1969 DEFINE_TESTCASE(qp_value_customrange1, !backend) {
1970 Xapian::QueryParser qp;
1971 AuthorValueRangeProcessor vrp_author;
1972 qp.add_valuerangeprocessor(&vrp_author);
1973 for (const test *p = test_value_customrange1_queries; p->query; ++p) {
1974 string expect, parsed;
1975 if (p->expect)
1976 expect = p->expect;
1977 else
1978 expect = "parse error";
1979 try {
1980 Xapian::Query qobj = qp.parse_query(p->query);
1981 parsed = qobj.get_description();
1982 expect = string("Query(") + expect + ')';
1983 } catch (const Xapian::QueryParserError &e) {
1984 parsed = e.get_msg();
1985 } catch (const Xapian::Error &e) {
1986 parsed = e.get_description();
1987 } catch (...) {
1988 parsed = "Unknown exception!";
1990 tout << "Query: " << p->query << '\n';
1991 TEST_STRINGS_EQUAL(parsed, expect);
1993 return true;
1996 struct AuthorRangeProcessor : public Xapian::RangeProcessor {
1997 AuthorRangeProcessor() : Xapian::RangeProcessor(4, "author:") { }
1999 Xapian::Query operator()(const std::string& b, const std::string& e)
2001 string begin = Xapian::Unicode::tolower(b);
2002 string end = Xapian::Unicode::tolower(e);
2003 return Xapian::RangeProcessor::operator()(begin, end);
2007 // Test custom RangeProcessor subclass.
2008 DEFINE_TESTCASE(qp_customrange1, !backend) {
2009 Xapian::QueryParser qp;
2010 AuthorRangeProcessor rp_author;
2011 qp.add_rangeprocessor(&rp_author);
2012 for (const test *p = test_value_customrange1_queries; p->query; ++p) {
2013 string expect, parsed;
2014 if (p->expect)
2015 expect = p->expect;
2016 else
2017 expect = "parse error";
2018 try {
2019 Xapian::Query qobj = qp.parse_query(p->query);
2020 parsed = qobj.get_description();
2021 expect = string("Query(") + expect + ')';
2022 } catch (const Xapian::QueryParserError &e) {
2023 parsed = e.get_msg();
2024 } catch (const Xapian::Error &e) {
2025 parsed = e.get_description();
2026 } catch (...) {
2027 parsed = "Unknown exception!";
2029 tout << "Query: " << p->query << '\n';
2030 TEST_STRINGS_EQUAL(parsed, expect);
2032 return true;
2035 class TitleFieldProcessor : public Xapian::FieldProcessor {
2036 Xapian::Query operator()(const std::string & str) {
2037 if (str == "all")
2038 return Xapian::Query::MatchAll;
2039 return Xapian::Query("S" + str);
2043 class HostFieldProcessor : public Xapian::FieldProcessor {
2044 Xapian::Query operator()(const std::string & str) {
2045 if (str == "*")
2046 return Xapian::Query::MatchAll;
2047 string res = "H";
2048 for (string::const_iterator i = str.begin(); i != str.end(); ++i)
2049 res += C_tolower(*i);
2050 return Xapian::Query(res);
2054 static const test test_fieldproc1_queries[] = {
2055 { "title:test", "Stest" },
2056 { "title:all", "<alldocuments>" },
2057 { "host:Xapian.org", "0 * Hxapian.org" },
2058 { "host:*", "0 * <alldocuments>" },
2059 { "host:\"Space Station.Example.Org\"", "0 * Hspace station.example.org" },
2060 { NULL, NULL }
2063 // FieldProcessor test.
2064 DEFINE_TESTCASE(qp_fieldproc1, !backend) {
2065 Xapian::QueryParser qp;
2066 TitleFieldProcessor title_fproc;
2067 HostFieldProcessor host_fproc;
2068 qp.add_prefix("title", &title_fproc);
2069 qp.add_boolean_prefix("host", &host_fproc);
2070 for (const test *p = test_fieldproc1_queries; p->query; ++p) {
2071 string expect, parsed;
2072 if (p->expect)
2073 expect = p->expect;
2074 else
2075 expect = "parse error";
2076 try {
2077 Xapian::Query qobj = qp.parse_query(p->query);
2078 parsed = qobj.get_description();
2079 expect = string("Query(") + expect + ')';
2080 } catch (const Xapian::QueryParserError &e) {
2081 parsed = e.get_msg();
2082 } catch (const Xapian::Error &e) {
2083 parsed = e.get_description();
2084 } catch (...) {
2085 parsed = "Unknown exception!";
2087 tout << "Query: " << p->query << '\n';
2088 TEST_STRINGS_EQUAL(parsed, expect);
2090 return true;
2093 class DateRangeFieldProcessor : public Xapian::FieldProcessor {
2094 Xapian::Query operator()(const std::string & str) {
2095 // In reality, these would be built from the current date, but for
2096 // testing it is much simpler to fix the date.
2097 if (str == "today")
2098 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120725");
2099 if (str == "this week")
2100 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120723");
2101 if (str == "this month")
2102 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120701");
2103 if (str == "this year")
2104 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120101");
2105 if (str == "this decade")
2106 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20100101");
2107 if (str == "this century")
2108 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20000101");
2109 throw Xapian::QueryParserError("Didn't understand date specification '" + str + "'");
2113 static const test test_fieldproc2_queries[] = {
2114 { "date:\"this week\"", "0 * VALUE_GE 1 20120723" },
2115 { "date:23/7/2012..25/7/2012", "0 * VALUE_RANGE 1 20120723 20120725" },
2116 { NULL, NULL }
2119 // Test using FieldProcessor and ValueRangeProcessor together.
2120 DEFINE_TESTCASE(qp_fieldproc2, !backend) {
2121 Xapian::QueryParser qp;
2122 DateRangeFieldProcessor date_fproc;
2123 qp.add_boolean_prefix("date", &date_fproc);
2124 Xapian::DateValueRangeProcessor vrp_date(1, "date:");
2125 qp.add_valuerangeprocessor(&vrp_date);
2126 for (const test *p = test_fieldproc2_queries; p->query; ++p) {
2127 string expect, parsed;
2128 if (p->expect)
2129 expect = p->expect;
2130 else
2131 expect = "parse error";
2132 try {
2133 Xapian::Query qobj = qp.parse_query(p->query);
2134 parsed = qobj.get_description();
2135 expect = string("Query(") + expect + ')';
2136 } catch (const Xapian::QueryParserError &e) {
2137 parsed = e.get_msg();
2138 } catch (const Xapian::Error &e) {
2139 parsed = e.get_description();
2140 } catch (...) {
2141 parsed = "Unknown exception!";
2143 tout << "Query: " << p->query << '\n';
2144 TEST_STRINGS_EQUAL(parsed, expect);
2146 return true;
2149 // Test using FieldProcessor and RangeProcessor together.
2150 DEFINE_TESTCASE(qp_fieldproc3, !backend) {
2151 Xapian::QueryParser qp;
2152 DateRangeFieldProcessor date_fproc;
2153 qp.add_boolean_prefix("date", &date_fproc);
2154 Xapian::DateRangeProcessor rp_date(1, "date:");
2155 qp.add_rangeprocessor(&rp_date);
2156 for (const test *p = test_fieldproc2_queries; p->query; ++p) {
2157 string expect, parsed;
2158 if (p->expect)
2159 expect = p->expect;
2160 else
2161 expect = "parse error";
2162 try {
2163 Xapian::Query qobj = qp.parse_query(p->query);
2164 parsed = qobj.get_description();
2165 expect = string("Query(") + expect + ')';
2166 } catch (const Xapian::QueryParserError &e) {
2167 parsed = e.get_msg();
2168 } catch (const Xapian::Error &e) {
2169 parsed = e.get_description();
2170 } catch (...) {
2171 parsed = "Unknown exception!";
2173 tout << "Query: " << p->query << '\n';
2174 TEST_STRINGS_EQUAL(parsed, expect);
2176 return true;
2179 DEFINE_TESTCASE(qp_stoplist1, !backend) {
2180 Xapian::QueryParser qp;
2181 const char * stopwords[] = { "a", "an", "the" };
2182 Xapian::SimpleStopper stop(stopwords, stopwords + 3);
2183 qp.set_stopper(&stop);
2185 Xapian::TermIterator i;
2187 Xapian::Query query1 = qp.parse_query("some mice");
2188 i = qp.stoplist_begin();
2189 TEST(i == qp.stoplist_end());
2191 Xapian::Query query2 = qp.parse_query("the cat");
2192 i = qp.stoplist_begin();
2193 TEST(i != qp.stoplist_end());
2194 TEST_EQUAL(*i, "the");
2195 ++i;
2196 TEST(i == qp.stoplist_end());
2198 // Regression test - prior to Xapian 1.0.0 the stoplist wasn't being cleared
2199 // when a new query was parsed.
2200 Xapian::Query query3 = qp.parse_query("an aardvark");
2201 i = qp.stoplist_begin();
2202 TEST(i != qp.stoplist_end());
2203 TEST_EQUAL(*i, "an");
2204 ++i;
2205 TEST(i == qp.stoplist_end());
2207 return true;
2210 static const test test_mispelled_queries[] = {
2211 { "doucment search", "document search" },
2212 { "doucment seeacrh", "document search" },
2213 { "docment seeacrh test", "document search test" },
2214 { "\"paragahp pineapple\"", "\"paragraph pineapple\"" },
2215 { "\"paragahp pineapple\"", "\"paragraph pineapple\"" },
2216 { "test S.E.A.R.C.", "" },
2217 { "this AND that", "" },
2218 { "documento", "document" },
2219 { "documento-documento", "document-document" },
2220 { "documento-searcho", "document-search" },
2221 { "test saerch", "test search" },
2222 { "paragraf search", "paragraph search" },
2223 { NULL, NULL }
2226 // Test spelling correction in the QueryParser.
2227 DEFINE_TESTCASE(qp_spell1, spelling) {
2228 Xapian::WritableDatabase db = get_writable_database();
2230 Xapian::Document doc;
2231 doc.add_term("document", 6);
2232 doc.add_term("search", 7);
2233 doc.add_term("saerch", 1);
2234 doc.add_term("paragraph", 8);
2235 doc.add_term("paragraf", 2);
2236 db.add_document(doc);
2238 db.add_spelling("document");
2239 db.add_spelling("search");
2240 db.add_spelling("paragraph");
2241 db.add_spelling("band");
2243 Xapian::QueryParser qp;
2244 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2245 qp.set_database(db);
2247 for (const test *p = test_mispelled_queries; p->query; ++p) {
2248 Xapian::Query q;
2249 q = qp.parse_query(p->query,
2250 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2251 Xapian::QueryParser::FLAG_BOOLEAN);
2252 tout << "Query: " << p->query << endl;
2253 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2256 return true;
2259 // Test spelling correction in the QueryParser with multiple databases.
2260 DEFINE_TESTCASE(qp_spell2, spelling)
2262 Xapian::WritableDatabase db1 = get_writable_database();
2264 db1.add_spelling("document");
2265 db1.add_spelling("search");
2266 db1.commit();
2268 Xapian::WritableDatabase db2 = get_named_writable_database("qp_spell2a");
2270 db2.add_spelling("document");
2271 db2.add_spelling("paragraph");
2272 db2.add_spelling("band");
2274 Xapian::Database db;
2275 db.add_database(db1);
2276 db.add_database(db2);
2278 Xapian::QueryParser qp;
2279 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2280 qp.set_database(db);
2282 for (const test *p = test_mispelled_queries; p->query; ++p) {
2283 Xapian::Query q;
2284 q = qp.parse_query(p->query,
2285 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2286 Xapian::QueryParser::FLAG_BOOLEAN);
2287 tout << "Query: " << p->query << endl;
2288 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2291 return true;
2294 static const test test_mispelled_wildcard_queries[] = {
2295 { "doucment", "document" },
2296 { "doucment*", "" },
2297 { "doucment* seearch", "doucment* search" },
2298 { "doucment* search", "" },
2299 { NULL, NULL }
2302 // Test spelling correction in the QueryParser with wildcards.
2303 // Regression test for bug fixed in 1.1.3 and 1.0.17.
2304 DEFINE_TESTCASE(qp_spellwild1, spelling) {
2305 Xapian::WritableDatabase db = get_writable_database();
2307 db.add_spelling("document");
2308 db.add_spelling("search");
2309 db.add_spelling("paragraph");
2310 db.add_spelling("band");
2312 Xapian::QueryParser qp;
2313 qp.set_database(db);
2315 const test *p;
2316 for (p = test_mispelled_queries; p->query; ++p) {
2317 Xapian::Query q;
2318 q = qp.parse_query(p->query,
2319 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2320 Xapian::QueryParser::FLAG_BOOLEAN |
2321 Xapian::QueryParser::FLAG_WILDCARD);
2322 tout << "Query: " << p->query << endl;
2323 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2325 for (p = test_mispelled_wildcard_queries; p->query; ++p) {
2326 Xapian::Query q;
2327 q = qp.parse_query(p->query,
2328 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2329 Xapian::QueryParser::FLAG_BOOLEAN |
2330 Xapian::QueryParser::FLAG_WILDCARD);
2331 tout << "Query: " << p->query << endl;
2332 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2335 return true;
2338 static const test test_mispelled_partial_queries[] = {
2339 { "doucment", "" },
2340 { "doucment ", "document " },
2341 { "documen", "" },
2342 { "documen ", "document " },
2343 { "seearch documen", "search documen" },
2344 { "search documen", "" },
2345 { NULL, NULL }
2348 // Test spelling correction in the QueryParser with FLAG_PARTIAL.
2349 // Regression test for bug fixed in 1.1.3 and 1.0.17.
2350 DEFINE_TESTCASE(qp_spellpartial1, spelling) {
2351 Xapian::WritableDatabase db = get_writable_database();
2353 db.add_spelling("document");
2354 db.add_spelling("search");
2355 db.add_spelling("paragraph");
2356 db.add_spelling("band");
2358 Xapian::QueryParser qp;
2359 qp.set_database(db);
2361 for (const test *p = test_mispelled_partial_queries; p->query; ++p) {
2362 Xapian::Query q;
2363 q = qp.parse_query(p->query,
2364 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2365 Xapian::QueryParser::FLAG_PARTIAL);
2366 tout << "Query: " << p->query << endl;
2367 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2370 return true;
2373 static const test test_synonym_queries[] = {
2374 { "searching", "(Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1)" },
2375 { "search", "(Zsearch@1 SYNONYM find@1)" },
2376 { "Search", "(search@1 SYNONYM find@1)" },
2377 { "Searching", "searching@1" },
2378 { "searching OR terms", "((Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1) OR Zterm@2)" },
2379 { "search OR terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2380 { "search +terms", "(Zterm@2 AND_MAYBE (Zsearch@1 SYNONYM find@1))" },
2381 { "search -terms", "((Zsearch@1 SYNONYM find@1) AND_NOT Zterm@2)" },
2382 { "+search terms", "((Zsearch@1 SYNONYM find@1) AND_MAYBE Zterm@2)" },
2383 { "-search terms", "(Zterm@2 AND_NOT (Zsearch@1 SYNONYM find@1))" },
2384 { "search terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2385 // Shouldn't trigger synonyms:
2386 { "\"search terms\"", "(search@1 PHRASE 2 terms@2)" },
2387 // Check that setting FLAG_AUTO_SYNONYMS doesn't enable multi-word
2388 // synonyms. Regression test for bug fixed in 1.3.0 and 1.2.9.
2389 { "regression test", "(Zregress@1 OR Ztest@2)" },
2390 { NULL, NULL }
2393 // Test single term synonyms in the QueryParser.
2394 DEFINE_TESTCASE(qp_synonym1, spelling) {
2395 Xapian::WritableDatabase db = get_writable_database();
2397 db.add_synonym("Zsearch", "Zfind");
2398 db.add_synonym("Zsearch", "Zlocate");
2399 db.add_synonym("search", "find");
2400 db.add_synonym("Zseek", "Zsearch");
2401 db.add_synonym("regression test", "magic");
2403 db.commit();
2405 Xapian::QueryParser qp;
2406 qp.set_stemmer(Xapian::Stem("english"));
2407 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2408 qp.set_database(db);
2410 for (const test *p = test_synonym_queries; p->query; ++p) {
2411 string expect = "Query(";
2412 expect += p->expect;
2413 expect += ')';
2414 Xapian::Query q;
2415 q = qp.parse_query(p->query, qp.FLAG_AUTO_SYNONYMS|qp.FLAG_DEFAULT);
2416 tout << "Query: " << p->query << endl;
2417 TEST_STRINGS_EQUAL(q.get_description(), expect);
2420 return true;
2423 static const test test_multi_synonym_queries[] = {
2424 { "sun OR tan OR cream", "((Zsun@1 OR Ztan@2) OR Zcream@3)" },
2425 { "sun tan", "((Zsun@1 OR Ztan@2) SYNONYM bathe@1)" },
2426 { "sun tan cream", "((Zsun@1 OR Ztan@2 OR Zcream@3) SYNONYM lotion@1)" },
2427 { "beach sun tan holiday", "(Zbeach@1 OR ((Zsun@2 OR Ztan@3) SYNONYM bathe@2) OR Zholiday@4)" },
2428 { "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))" },
2429 { "single", "(Zsingl@1 SYNONYM record@1)" },
2430 { NULL, NULL }
2433 // Test multi term synonyms in the QueryParser.
2434 DEFINE_TESTCASE(qp_synonym2, synonyms) {
2435 Xapian::WritableDatabase db = get_writable_database();
2437 db.add_synonym("sun tan cream", "lotion");
2438 db.add_synonym("sun tan", "bathe");
2439 db.add_synonym("single", "record");
2441 db.commit();
2443 Xapian::QueryParser qp;
2444 qp.set_stemmer(Xapian::Stem("english"));
2445 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2446 qp.set_database(db);
2448 for (const test *p = test_multi_synonym_queries; p->query; ++p) {
2449 string expect = "Query(";
2450 expect += p->expect;
2451 expect += ')';
2452 Xapian::Query q;
2453 q = qp.parse_query(p->query,
2454 Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS |
2455 Xapian::QueryParser::FLAG_DEFAULT);
2456 tout << "Query: " << p->query << endl;
2457 TEST_STRINGS_EQUAL(q.get_description(), expect);
2460 return true;
2463 static const test test_synonym_op_queries[] = {
2464 { "searching", "Zsearch@1" },
2465 { "~searching", "(Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1)" },
2466 { "~search", "(Zsearch@1 SYNONYM find@1)" },
2467 { "~Search", "(search@1 SYNONYM find@1)" },
2468 { "~Searching", "searching@1" },
2469 { "~searching OR terms", "((Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1) OR Zterm@2)" },
2470 { "~search OR terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2471 { "~search +terms", "(Zterm@2 AND_MAYBE (Zsearch@1 SYNONYM find@1))" },
2472 { "~search -terms", "((Zsearch@1 SYNONYM find@1) AND_NOT Zterm@2)" },
2473 { "+~search terms", "((Zsearch@1 SYNONYM find@1) AND_MAYBE Zterm@2)" },
2474 { "-~search terms", "(Zterm@2 AND_NOT (Zsearch@1 SYNONYM find@1))" },
2475 { "~search terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2476 { "~foo:search", "(ZXFOOsearch@1 SYNONYM prefixated@1)" },
2477 // FIXME: should look for multi-term synonym...
2478 { "~\"search terms\"", "(search@1 PHRASE 2 terms@2)" },
2479 { NULL, NULL }
2482 // Test the synonym operator in the QueryParser.
2483 DEFINE_TESTCASE(qp_synonym3, synonyms) {
2484 Xapian::WritableDatabase db = get_writable_database();
2486 db.add_synonym("Zsearch", "Zfind");
2487 db.add_synonym("Zsearch", "Zlocate");
2488 db.add_synonym("search", "find");
2489 db.add_synonym("Zseek", "Zsearch");
2490 db.add_synonym("ZXFOOsearch", "prefixated");
2492 db.commit();
2494 Xapian::QueryParser qp;
2495 qp.set_stemmer(Xapian::Stem("english"));
2496 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2497 qp.set_database(db);
2498 qp.add_prefix("foo", "XFOO");
2500 for (const test *p = test_synonym_op_queries; p->query; ++p) {
2501 string expect = "Query(";
2502 expect += p->expect;
2503 expect += ')';
2504 Xapian::Query q;
2505 q = qp.parse_query(p->query,
2506 Xapian::QueryParser::FLAG_SYNONYM |
2507 Xapian::QueryParser::FLAG_BOOLEAN |
2508 Xapian::QueryParser::FLAG_LOVEHATE |
2509 Xapian::QueryParser::FLAG_PHRASE);
2510 tout << "Query: " << p->query << endl;
2511 TEST_STRINGS_EQUAL(q.get_description(), expect);
2514 return true;
2517 static const test test_stem_all_queries[] = {
2518 { "\"chemical engineers\"", "(chemic@1 PHRASE 2 engin@2)" },
2519 { "chemical NEAR engineers", "(chemic@1 NEAR 11 engin@2)" },
2520 { "chemical engineers", "(chemic@1 OR engin@2)" },
2521 { "title:(chemical engineers)", "(XTchemic@1 OR XTengin@2)" },
2522 { NULL, NULL }
2525 DEFINE_TESTCASE(qp_stem_all1, !backend) {
2526 Xapian::QueryParser qp;
2527 qp.set_stemmer(Xapian::Stem("english"));
2528 qp.set_stemming_strategy(qp.STEM_ALL);
2529 qp.add_prefix("title", "XT");
2530 for (const test *p = test_stem_all_queries; p->query; ++p) {
2531 string expect, parsed;
2532 if (p->expect)
2533 expect = p->expect;
2534 else
2535 expect = "parse error";
2536 try {
2537 Xapian::Query qobj = qp.parse_query(p->query);
2538 parsed = qobj.get_description();
2539 expect = string("Query(") + expect + ')';
2540 } catch (const Xapian::QueryParserError &e) {
2541 parsed = e.get_msg();
2542 } catch (const Xapian::Error &e) {
2543 parsed = e.get_description();
2544 } catch (...) {
2545 parsed = "Unknown exception!";
2547 tout << "Query: " << p->query << '\n';
2548 TEST_STRINGS_EQUAL(parsed, expect);
2550 return true;
2553 static const test test_stem_all_z_queries[] = {
2554 { "\"chemical engineers\"", "(Zchemic@1 PHRASE 2 Zengin@2)" },
2555 { "chemical NEAR engineers", "(Zchemic@1 NEAR 11 Zengin@2)" },
2556 { "chemical engineers", "(Zchemic@1 OR Zengin@2)" },
2557 { "title:(chemical engineers)", "(ZXTchemic@1 OR ZXTengin@2)" },
2558 { NULL, NULL }
2561 DEFINE_TESTCASE(qp_stem_all_z1, !backend) {
2562 Xapian::QueryParser qp;
2563 qp.set_stemmer(Xapian::Stem("english"));
2564 qp.set_stemming_strategy(qp.STEM_ALL_Z);
2565 qp.add_prefix("title", "XT");
2566 for (const test *p = test_stem_all_z_queries; p->query; ++p) {
2567 string expect, parsed;
2568 if (p->expect)
2569 expect = p->expect;
2570 else
2571 expect = "parse error";
2572 try {
2573 Xapian::Query qobj = qp.parse_query(p->query);
2574 parsed = qobj.get_description();
2575 expect = string("Query(") + expect + ')';
2576 } catch (const Xapian::QueryParserError &e) {
2577 parsed = e.get_msg();
2578 } catch (const Xapian::Error &e) {
2579 parsed = e.get_description();
2580 } catch (...) {
2581 parsed = "Unknown exception!";
2583 tout << "Query: " << p->query << '\n';
2584 TEST_STRINGS_EQUAL(parsed, expect);
2586 return true;
2589 static double
2590 time_query_parse(const Xapian::Database & db, const string & q,
2591 int repetitions, unsigned flags)
2593 Xapian::QueryParser qp;
2594 qp.set_database(db);
2595 CPUTimer timer;
2596 std::vector<Xapian::Query> qs;
2597 qs.reserve(repetitions);
2598 for (int i = 0; i != repetitions; ++i) {
2599 qs.push_back(qp.parse_query(q, flags));
2601 if (repetitions > 1) {
2602 Xapian::Query qc(Xapian::Query::OP_OR, qs.begin(), qs.end());
2604 return timer.get_time();
2607 static void
2608 qp_scale1_helper(const Xapian::Database &db, const string & q, unsigned n,
2609 unsigned flags)
2611 double time1;
2612 while (true) {
2613 time1 = time_query_parse(db, q, n, flags);
2614 if (time1 != 0.0) break;
2616 // The first test completed before the timer ticked at all, so increase
2617 // the number of repetitions and retry.
2618 unsigned n_new = n * 10;
2619 if (n_new < n)
2620 SKIP_TEST("Can't count enough repetitions to be able to time test");
2621 n = n_new;
2624 n /= 5;
2626 string q_n;
2627 q_n.reserve(q.size() * n);
2628 for (unsigned i = n; i != 0; --i) {
2629 q_n += q;
2632 // Time 5 repetitions so we average random variations a bit.
2633 double time2 = time_query_parse(db, q_n, 5, flags);
2634 tout << "small=" << time1 << "s, large=" << time2 << "s\n";
2636 // Allow a factor of 2.15 difference, to cover random variation and a
2637 // native time interval which isn't an exact multiple of 1/CLK_TCK.
2638 TEST_REL(time2,<,time1 * 2.15);
2641 // Regression test: check that query parser doesn't scale very badly with the
2642 // size of the query.
2643 DEFINE_TESTCASE(qp_scale1, synonyms) {
2644 Xapian::WritableDatabase db = get_writable_database();
2646 db.add_synonym("foo", "bar");
2647 db.commit();
2649 string q1("foo ");
2650 string q1b("baz ");
2651 const unsigned repetitions = 5000;
2653 // A long multiword synonym.
2654 string syn;
2655 for (int j = 60; j != 0; --j) {
2656 syn += q1;
2658 syn.resize(syn.size() - 1);
2660 unsigned synflags = Xapian::QueryParser::FLAG_DEFAULT |
2661 Xapian::QueryParser::FLAG_SYNONYM |
2662 Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS;
2664 // First, we test a simple query.
2665 qp_scale1_helper(db, q1, repetitions, Xapian::QueryParser::FLAG_DEFAULT);
2667 // If synonyms are enabled, a different code-path is followed.
2668 // Test a query which has no synonyms.
2669 qp_scale1_helper(db, q1b, repetitions, synflags);
2671 // Test a query which has short synonyms.
2672 qp_scale1_helper(db, q1, repetitions, synflags);
2674 // Add a synonym for the whole query, to test that code path.
2675 db.add_synonym(syn, "bar");
2676 db.commit();
2678 qp_scale1_helper(db, q1, repetitions, synflags);
2680 return true;
2683 static const test test_near_queries[] = {
2684 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
2685 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
2686 // FIXME: these give NEAR 2
2687 // { "foo -baz bar", "((foo@1 NEAR 11 bar@3) AND_NOT Zbaz@2)" },
2688 // { "one +two three", "(Ztwo@2 AND_MAYBE (one@1 NEAR 11 three@3))" },
2689 { "foo bar", "(foo@1 NEAR 11 bar@2)" },
2690 { "foo bar baz", "(foo@1 NEAR 12 bar@2 NEAR 12 baz@3)" },
2691 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
2692 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
2693 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
2694 { "author:orwell animal farm", "(Aorwell@1 NEAR 12 animal@2 NEAR 12 farm@3)" },
2695 { "author:Orwell Animal Farm", "(Aorwell@1 NEAR 12 animal@2 NEAR 12 farm@3)" },
2696 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
2697 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
2698 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2699 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2700 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
2701 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
2702 { "one AND two", "(Zone@1 AND Ztwo@2)" },
2703 { "NOT windows", "Syntax: <expression> NOT <expression>" },
2704 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
2705 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
2706 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
2707 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
2708 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
2709 { "OR foo", "Syntax: <expression> OR <expression>" },
2710 { "XOR", "Syntax: <expression> XOR <expression>" },
2711 { "hard\xa0space", "(hard@1 NEAR 11 space@2)" },
2712 { NULL, NULL }
2715 DEFINE_TESTCASE(qp_near1, !backend) {
2716 Xapian::QueryParser queryparser;
2717 queryparser.set_stemmer(Xapian::Stem("english"));
2718 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2719 queryparser.add_prefix("author", "A");
2720 queryparser.add_prefix("writer", "A");
2721 queryparser.add_prefix("title", "XT");
2722 queryparser.add_prefix("subject", "XT");
2723 queryparser.add_prefix("authortitle", "A");
2724 queryparser.add_prefix("authortitle", "XT");
2725 queryparser.add_boolean_prefix("site", "H");
2726 queryparser.add_boolean_prefix("site2", "J");
2727 queryparser.add_boolean_prefix("multisite", "H");
2728 queryparser.add_boolean_prefix("multisite", "J");
2729 queryparser.add_boolean_prefix("category", "XCAT", false);
2730 queryparser.add_boolean_prefix("dogegory", "XDOG", false);
2731 queryparser.set_default_op(Xapian::Query::OP_NEAR);
2732 for (const test *p = test_near_queries; p->query; ++p) {
2733 string expect, parsed;
2734 if (p->expect)
2735 expect = p->expect;
2736 else
2737 expect = "parse error";
2738 try {
2739 Xapian::Query qobj = queryparser.parse_query(p->query);
2740 parsed = qobj.get_description();
2741 expect = string("Query(") + expect + ')';
2742 } catch (const Xapian::QueryParserError &e) {
2743 parsed = e.get_msg();
2744 } catch (const Xapian::Error &e) {
2745 parsed = e.get_description();
2746 } catch (...) {
2747 parsed = "Unknown exception!";
2749 tout << "Query: " << p->query << '\n';
2750 TEST_STRINGS_EQUAL(parsed, expect);
2752 return true;
2755 static const test test_phrase_queries[] = {
2756 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
2757 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
2758 // FIXME: these give PHRASE 2
2759 // { "foo -baz bar", "((foo@1 PHRASE 11 bar@3) AND_NOT Zbaz@2)" },
2760 // { "one +two three", "(Ztwo@2 AND_MAYBE (one@1 PHRASE 11 three@3))" },
2761 { "foo bar", "(foo@1 PHRASE 11 bar@2)" },
2762 { "foo bar baz", "(foo@1 PHRASE 12 bar@2 PHRASE 12 baz@3)" },
2763 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
2764 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
2765 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
2766 { "author:orwell animal farm", "(Aorwell@1 PHRASE 12 animal@2 PHRASE 12 farm@3)" },
2767 { "author:Orwell Animal Farm", "(Aorwell@1 PHRASE 12 animal@2 PHRASE 12 farm@3)" },
2768 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
2769 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
2770 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2771 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2772 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
2773 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
2774 { "one AND two", "(Zone@1 AND Ztwo@2)" },
2775 { "NOT windows", "Syntax: <expression> NOT <expression>" },
2776 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
2777 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
2778 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
2779 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
2780 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
2781 { "OR foo", "Syntax: <expression> OR <expression>" },
2782 { "XOR", "Syntax: <expression> XOR <expression>" },
2783 { "hard\xa0space", "(hard@1 PHRASE 11 space@2)" },
2784 // FIXME: this isn't what we want, but fixing phrase to work with
2785 // subqueries first might be the best approach.
2786 // FIXME: this isn't currently reimplemented:
2787 // { "(one AND two) three", "((Zone@1 PHRASE 11 Zthree@3) AND (Ztwo@2 PHRASE 11 Zthree@3))" },
2788 { NULL, NULL }
2791 DEFINE_TESTCASE(qp_phrase1, !backend) {
2792 Xapian::QueryParser queryparser;
2793 queryparser.set_stemmer(Xapian::Stem("english"));
2794 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2795 queryparser.add_prefix("author", "A");
2796 queryparser.add_prefix("writer", "A");
2797 queryparser.add_prefix("title", "XT");
2798 queryparser.add_prefix("subject", "XT");
2799 queryparser.add_prefix("authortitle", "A");
2800 queryparser.add_prefix("authortitle", "XT");
2801 queryparser.add_boolean_prefix("site", "H");
2802 queryparser.add_boolean_prefix("site2", "J");
2803 queryparser.add_boolean_prefix("multisite", "H");
2804 queryparser.add_boolean_prefix("multisite", "J");
2805 queryparser.add_boolean_prefix("category", "XCAT", false);
2806 queryparser.add_boolean_prefix("dogegory", "XDOG", false);
2807 queryparser.set_default_op(Xapian::Query::OP_PHRASE);
2808 for (const test *p = test_phrase_queries; p->query; ++p) {
2809 string expect, parsed;
2810 if (p->expect)
2811 expect = p->expect;
2812 else
2813 expect = "parse error";
2814 try {
2815 Xapian::Query qobj = queryparser.parse_query(p->query);
2816 parsed = qobj.get_description();
2817 expect = string("Query(") + expect + ')';
2818 } catch (const Xapian::QueryParserError &e) {
2819 parsed = e.get_msg();
2820 } catch (const Xapian::Error &e) {
2821 parsed = e.get_description();
2822 } catch (...) {
2823 parsed = "Unknown exception!";
2825 tout << "Query: " << p->query << '\n';
2826 TEST_STRINGS_EQUAL(parsed, expect);
2828 return true;
2831 static const test test_stopword_group_or_queries[] = {
2832 { "this is a test", "test@4" },
2833 { "test*", "(SYNONYM WILDCARD OR test)" },
2834 { "a test*", "(SYNONYM WILDCARD OR test)" },
2835 { "is a test*", "(SYNONYM WILDCARD OR test)" },
2836 { "this is a test*", "(SYNONYM WILDCARD OR test)" },
2837 { "this is a us* test*", "((SYNONYM WILDCARD OR us) OR (SYNONYM WILDCARD OR test))" },
2838 { "this is a user test*", "(user@4 OR (SYNONYM WILDCARD OR test))" },
2839 { NULL, NULL }
2842 static const test test_stopword_group_and_queries[] = {
2843 { "this is a test", "test@4" },
2844 { "test*", "(SYNONYM WILDCARD OR test)" },
2845 { "a test*", "(SYNONYM WILDCARD OR test)" },
2846 // Two stopwords + one wildcard failed in 1.0.16
2847 { "is a test*", "(SYNONYM WILDCARD OR test)" },
2848 // Three stopwords + one wildcard failed in 1.0.16
2849 { "this is a test*", "(SYNONYM WILDCARD OR test)" },
2850 // Three stopwords + two wildcards failed in 1.0.16
2851 { "this is a us* test*", "((SYNONYM WILDCARD OR us) AND (SYNONYM WILDCARD OR test))" },
2852 { "this is a user test*", "(user@4 AND (SYNONYM WILDCARD OR test))" },
2853 { NULL, NULL }
2856 // Regression test for bug fixed in 1.0.17 and 1.1.3.
2857 DEFINE_TESTCASE(qp_stopword_group1, writable) {
2858 Xapian::WritableDatabase db = get_writable_database();
2859 Xapian::Document doc;
2860 doc.add_term("test");
2861 doc.add_term("tester");
2862 doc.add_term("testable");
2863 doc.add_term("user");
2864 db.add_document(doc);
2866 Xapian::SimpleStopper stopper;
2867 stopper.add("this");
2868 stopper.add("is");
2869 stopper.add("a");
2871 Xapian::QueryParser qp;
2872 qp.set_stopper(&stopper);
2873 qp.set_database(db);
2875 // Process test cases with OP_OR first, then with OP_AND.
2876 qp.set_default_op(Xapian::Query::OP_OR);
2877 const test *p = test_stopword_group_or_queries;
2878 for (int i = 1; i <= 2; ++i) {
2879 for ( ; p->query; ++p) {
2880 string expect, parsed;
2881 if (p->expect)
2882 expect = p->expect;
2883 else
2884 expect = "parse error";
2885 try {
2886 Xapian::Query qobj = qp.parse_query(p->query, qp.FLAG_WILDCARD);
2887 parsed = qobj.get_description();
2888 expect = string("Query(") + expect + ')';
2889 } catch (const Xapian::QueryParserError &e) {
2890 parsed = e.get_msg();
2891 } catch (const Xapian::Error &e) {
2892 parsed = e.get_description();
2893 } catch (...) {
2894 parsed = "Unknown exception!";
2896 tout << "Query: " << p->query << '\n';
2897 TEST_STRINGS_EQUAL(parsed, expect);
2900 qp.set_default_op(Xapian::Query::OP_AND);
2901 p = test_stopword_group_and_queries;
2904 return true;
2907 /// Check that QueryParser::set_default_op() rejects inappropriate ops.
2908 DEFINE_TESTCASE(qp_default_op2, !backend) {
2909 Xapian::QueryParser qp;
2910 static const Xapian::Query::op ops[] = {
2911 Xapian::Query::OP_AND_NOT,
2912 Xapian::Query::OP_XOR,
2913 Xapian::Query::OP_AND_MAYBE,
2914 Xapian::Query::OP_FILTER,
2915 Xapian::Query::OP_VALUE_RANGE,
2916 Xapian::Query::OP_SCALE_WEIGHT,
2917 Xapian::Query::OP_VALUE_GE,
2918 Xapian::Query::OP_VALUE_LE
2920 const Xapian::Query::op * p;
2921 for (p = ops; p - ops != sizeof(ops) / sizeof(*ops); ++p) {
2922 tout << *p << endl;
2923 TEST_EXCEPTION(Xapian::InvalidArgumentError,
2924 qp.set_default_op(*p));
2925 TEST_EQUAL(qp.get_default_op(), Xapian::Query::OP_OR);
2927 return true;
2930 struct qp_default_op3_test {
2931 Xapian::Query::op op;
2932 const char *expect;
2935 /// Check that QueryParser::set_default_op() accepts appropriate ops.
2936 DEFINE_TESTCASE(qp_default_op3, !backend) {
2937 Xapian::QueryParser qp;
2938 static const qp_default_op3_test tests[] = {
2939 { Xapian::Query::OP_AND,
2940 "Query((a@1 AND b@2 AND c@3))" },
2941 { Xapian::Query::OP_OR,
2942 "Query((a@1 OR b@2 OR c@3))" },
2943 { Xapian::Query::OP_PHRASE,
2944 "Query((a@1 PHRASE 12 b@2 PHRASE 12 c@3))" },
2945 { Xapian::Query::OP_NEAR,
2946 "Query((a@1 NEAR 12 b@2 NEAR 12 c@3))" },
2947 { Xapian::Query::OP_ELITE_SET,
2948 "Query((a@1 ELITE_SET 10 b@2 ELITE_SET 10 c@3))" },
2949 { Xapian::Query::OP_SYNONYM,
2950 "Query((a@1 SYNONYM b@2 SYNONYM c@3))" },
2952 const qp_default_op3_test * p;
2953 for (p = tests; p - tests != sizeof(tests) / sizeof(*tests); ++p) {
2954 tout << p->op << endl;
2955 qp.set_default_op(p->op);
2956 // Check that get_default_op() returns what we just set.
2957 TEST_EQUAL(qp.get_default_op(), p->op);
2958 TEST_EQUAL(qp.parse_query("A B C").get_description(), p->expect);
2960 return true;
2963 /// Test that the default strategy is now STEM_SOME (as of 1.3.1).
2964 DEFINE_TESTCASE(qp_defaultstrategysome1, !backend) {
2965 Xapian::QueryParser qp;
2966 qp.set_stemmer(Xapian::Stem("en"));
2967 TEST_EQUAL(qp.parse_query("testing").get_description(), "Query(Ztest@1)");
2968 return true;