Add new stemming mode STEM_SOME_FULL_POS
[xapian.git] / xapian-core / tests / api_queryparser.cc
blob0f6fc986317224a0820eaced40a09f99abfde0e4
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 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 Hcvs.xapian.org)" },
142 { "mug site:xapian.org AND -site:cvs.xapian.org", "((Zmug@1 FILTER Hxapian.org) AND_NOT 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 { "category::colon", "0 * XCAT::colon" },
192 // Feature tests for quoted boolean terms:
193 { "category:\"Hello world\"", "0 * XCAT:Hello world" },
194 { "category:\"literal \"\"\"", "0 * XCATliteral \"" },
195 { "category:\" \"", "0 * XCAT " },
196 { "category:\"\"", "0 * XCAT" },
197 { "category:\"(unterminated)", "0 * XCAT(unterminated)" },
198 // Feature tests for curly double quotes:
199 { "“curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
200 // Feature tests for implicitly closing brackets:
201 { "(foo", "Zfoo@1" },
202 { "(foo XOR bar", "(Zfoo@1 XOR Zbar@2)" },
203 { "(foo XOR (bar AND baz)", "(Zfoo@1 XOR (Zbar@2 AND Zbaz@3))" },
204 { "(foo XOR (bar AND baz", "(Zfoo@1 XOR (Zbar@2 AND Zbaz@3))" },
205 // Slightly arbitrarily we accept mismatched quotes.
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 { "”curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
210 { "author:“orwell” title:“animal\"", "(Aorwell@1 OR XTanimal@2)" },
211 { "author:\"orwell” title:“animal”", "(Aorwell@1 OR XTanimal@2)" },
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 { "author:\"milne a.a.”", "(Amilne@1 PHRASE 3 Aa@2 PHRASE 3 Aa@3)" },
215 { "“hello world” +python", "(Zpython@3 AND_MAYBE (hello@1 PHRASE 2 world@2))" },
216 { "unmatched“", "Zunmatch@1" },
217 { "unmatched”", "Zunmatch@1" },
218 { "unmatched “ ” ", "Zunmatch@1" },
219 { "unmatched \" ” ", "Zunmatch@1" },
220 { "unmatched “ \" ", "Zunmatch@1" },
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 { "hyphen-ated\" ”", "(hyphen@1 PHRASE 2 ated@2)" },
226 { "“1.4”", "1.4@1" },
227 { "“1.\"", "1@1" },
228 { "\"A#.B.”", "(a#@1 PHRASE 2 b@2)" },
229 { "“ Xapian QueryParser” parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
230 { "“ xapian queryParser\" parses queries", "((xapian@1 PHRASE 2 queryparser@2) OR (Zpars@3 OR Zqueri@4))" },
231 { "beer NOT “orange juice”", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
232 { "“missing quote", "(missing@1 PHRASE 2 quote@2)" },
233 { "+“must have” optional", "((must@1 PHRASE 2 have@2) AND_MAYBE Zoption@3)" },
234 { "category:“Hello world”", "0 * XCAT:Hello world" },
235 { "category:“literal \"\"”", "0 * XCATliteral \"" },
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:“\"", "0 * XCAT" },
244 { "category:“(unterminated)", "0 * XCAT(unterminated)" },
245 // Real world examples from tweakers.net:
246 { "Call to undefined function: imagecreate()", "(call@1 OR Zto@2 OR Zundefin@3 OR Zfunction@4 OR imagecreate@5)" },
247 { "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))" },
248 { "php date() nedelands", "((Zphp@1 OR date@2) OR Znedeland@3)" },
249 { "wget domein --http-user", "((Zwget@1 OR Zdomein@2) OR (http@3 PHRASE 2 user@4))" },
250 { "@home problemen", "(Zhome@1 OR Zproblemen@2)" },
251 { "'ipacsum'", "Zipacsum@1" },
252 { "canal + ", "Zcanal@1" },
253 { "/var/run/mysqld/mysqld.sock", "(var@1 PHRASE 5 run@2 PHRASE 5 mysqld@3 PHRASE 5 mysqld@4 PHRASE 5 sock@5)" },
254 { "\"QSI-161 drivers\"", "(qsi@1 PHRASE 3 161@2 PHRASE 3 drivers@3)" },
255 { "\"e-cube\" barebone", "((e@1 PHRASE 2 cube@2) OR Zbarebon@3)" },
256 { "\"./httpd: symbol not found: dlopen\"", "(httpd@1 PHRASE 5 symbol@2 PHRASE 5 not@3 PHRASE 5 found@4 PHRASE 5 dlopen@5)" },
257 { "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)" },
258 { "location.href = \"\"", "(location@1 PHRASE 2 href@2)" },
259 { "method=\"post\" action=\"\">", "((method@1 OR post@2) OR action@3)" },
260 { "behuizing 19\" inch", "((Zbehuiz@1 OR 19@2) OR inch@3)" },
261 { "19\" rack", "(19@1 OR rack@2)" },
262 { "3,5\" mainboard", "(3,5@1 OR mainboard@2)" },
263 { "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)" },
264 { "data error (clic redundancy check)", "((Zdata@1 OR Zerror@2) OR (Zclic@3 OR Zredund@4 OR Zcheck@5))" },
265 { "? mediaplayer 9\"", "(Zmediaplay@1 OR 9@2)" },
266 { "date(\"w\")", "(date@1 OR w@2)" },
267 { "Syntaxisfout (operator ontbreekt ASP", "(syntaxisfout@1 OR (Zoper@2 OR Zontbreekt@3 OR asp@4))" },
268 { "Request.ServerVariables(\"logon_user\")", "((request@1 PHRASE 2 servervariables@2) OR logon_user@3)" },
269 { "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))" },
270 { "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))" },
271 { "ip_masq_new(proto=TCP)", "((ip_masq_new@1 OR proto@2) OR tcp@3)" },
272 { "\"document.write(\"", "(document@1 PHRASE 2 write@2)" },
273 { "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))" },
274 { "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))" },
275 { "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))" },
276 { "\"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)" },
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 { "\"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)" },
279 { "$structure = imap_header($mbox, $tt);", "(((Zstructur@1 OR imap_header@2) OR Zmbox@3) OR Ztt@4)" },
280 { "\"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)" },
281 { "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))" },
282 { "ereg_replace(\"\\\\\",\"\\/\"", "ereg_replace@1" },
283 { "\\\\\"divx+geen+geluid\\\\\"", "(divx@1 PHRASE 3 geen@2 PHRASE 3 geluid@3)" },
284 { "lcase(\"string\")", "(lcase@1 OR string@2)" },
285 { "isEmpty( ) functie in visual basic", "(isempty@1 OR (Zfuncti@2 OR Zin@3 OR Zvisual@4 OR Zbasic@5))" },
286 { "*** stop: 0x0000001E (0xC0000005,0x00000000,0x00000000,0x00000000)", "((Zstop@1 OR 0x0000001e@2) OR 0xc0000005,0x00000000,0x00000000,0x00000000@3)" },
287 { "\"ctrl+v+c+a fout\"", "(ctrl@1 PHRASE 5 v@2 PHRASE 5 c@3 PHRASE 5 a@4 PHRASE 5 fout@5)" },
288 { "Server.CreateObject(\"ADODB.connection\")", "((server@1 PHRASE 2 createobject@2) OR (adodb@3 PHRASE 2 connection@4))" },
289 { "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))" },
290 { "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))" },
291 { "delphi CreateOleObject(\"MSXML2.DomDocument\")", "((Zdelphi@1 OR createoleobject@2) OR (msxml2@3 PHRASE 2 domdocument@4))" },
292 { "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))" },
293 { "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))" },
294 { "asp ' en \"", "(Zasp@1 OR Zen@2)" },
295 { "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)))" },
296 { "session_set_cookie_params(echo \"hoi\")", "((session_set_cookie_params@1 OR Zecho@2) OR hoi@3)" },
297 { "windows update werkt niet (windows se", "((Zwindow@1 OR Zupdat@2 OR Zwerkt@3 OR Zniet@4) OR (Zwindow@5 OR Zse@6))" },
298 { "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))" },
299 { "sony +(u20 u-20)", "((Zu20@2 OR (u@3 PHRASE 2 20@4)) AND_MAYBE Zsoni@1)" },
300 { "[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)))" },
301 { "directories lokaal php (uitlezen OR inladen)", "((Zdirectori@1 OR Zlokaal@2 OR Zphp@3) OR (Zuitlezen@4 OR Zinladen@5))" },
302 { "(multi pc modem)+ (line sync)", "((Zmulti@1 OR Zpc@2 OR Zmodem@3) OR (Zline@4 OR Zsync@5))" },
303 { "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))" },
304 { "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))" },
305 { "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))" },
306 { "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))" },
307 { "\"arp -s\" ip veranderen", "((arp@1 PHRASE 2 s@2) OR (Zip@3 OR Zveranderen@4))" },
308 { "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))" },
309 { "$datum = date(\"d-m-Y\");", "((Zdatum@1 OR date@2) OR (d@3 PHRASE 3 m@4 PHRASE 3 y@5))" },
310 { "\"'\" +asp", "Zasp@1" },
311 { "+session +[", "Zsession@1" },
312 { "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))" },
313 { "\"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)" },
314 { "\"+irq +veranderen +xp\"", "(irq@1 PHRASE 3 veranderen@2 PHRASE 3 xp@3)" },
315 { "\"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)" },
316 { "mkdir() failed (File exists) php", "(((mkdir@1 OR Zfail@2) OR (file@3 OR Zexist@4)) OR Zphp@5)" },
317 { "laatsteIndex(int n)", "(laatsteindex@1 OR (Zint@2 OR Zn@3))" },
318 { "\"line+in\" OR \"c8783\"", "((line@1 PHRASE 2 in@2) OR c8783@3)" },
319 { "if ($_POST['Submit'])", "(Zif@1 OR (_post@2 OR submit@3))" },
320 { "NEC DVD+-RW ND-1300A", "((nec@1 OR (dvd+@2 PHRASE 2 rw@3)) OR (nd@4 PHRASE 2 1300a@5))" },
321 { "*String not found* (*String not found*.)", "((string@1 OR Znot@2 OR found@3) OR (string@4 OR Znot@5 OR found@6))" },
322 { "MSI G4Ti4200-TD 128MB (GeForce4 Ti4200)", "(((msi@1 OR (g4ti4200@2 PHRASE 2 td@3)) OR 128mb@4) OR (geforce4@5 OR ti4200@6))" },
323 { "href=\"#\"", "href@1" },
324 { "Request.ServerVariables(\"REMOTE_USER\") javascript", "(((request@1 PHRASE 2 servervariables@2) OR remote_user@3) OR Zjavascript@4)" },
325 { "XF86Config(-4) waar", "((xf86config@1 OR 4@2) OR Zwaar@3)" },
326 { "Unknown (tag 2000)", "(unknown@1 OR (Ztag@2 OR 2000@3))" },
327 { "KT4V(MS-6712)", "(kt4v@1 OR (ms@2 PHRASE 2 6712@3))" },
328 { "scheduled+AND+nieuwsgroepen+AND+updaten", "((Zschedul@1 AND Znieuwsgroepen@2) AND Zupdaten@3)" },
329 { "137(netbios-ns)", "(137@1 OR (netbios@2 PHRASE 2 ns@3))" },
330 { "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))" },
331 { "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)" },
332 { "wat is code van \" teken", "((Zwat@1 OR Zis@2 OR Zcode@3 OR Zvan@4) OR teken@5)" },
333 { "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))" },
334 { "Permission denied (publickey,password,keyboard-interactive).", "((permission@1 OR Zdeni@2) OR ((Zpublickey@3 OR Zpassword@4) OR (keyboard@5 PHRASE 2 interactive@6)))" },
335 { "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))" },
336 { "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)" },
337 { "\"2020 NEAR zoom\"", "(2020@1 PHRASE 3 near@2 PHRASE 3 zoom@3)" },
338 { "setcookie(\"naam\",\"$user\");", "((setcookie@1 OR naam@2) OR user@3)" },
339 { "MSI 645 Ultra (MS-6547) Ver1", "(((msi@1 OR 645@2 OR ultra@3) OR (ms@4 PHRASE 2 6547@5)) OR ver1@6)" },
340 { "if ($HTTP", "(Zif@1 OR http@2)" },
341 { "data error(cyclic redundancy check)", "((Zdata@1 OR error@2) OR (Zcyclic@3 OR Zredund@4 OR Zcheck@5))" },
342 { "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)" },
343 { "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))" },
344 { "Call Shell(\"notepad.exe\",", "((call@1 OR shell@2) OR (notepad@3 PHRASE 2 exe@4))" },
345 { "2.5\" harddisk converter", "(2.5@1 OR (harddisk@2 PHRASE 2 converter@3))" },
346 { "creative labs \"dvd+rw\"", "((Zcreativ@1 OR Zlab@2) OR (dvd@3 PHRASE 2 rw@4))" },
347 { "\"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)" },
348 { "ati radeon \"driver cleaner", "((Zati@1 OR Zradeon@2) OR (driver@3 PHRASE 2 cleaner@4))" },
349 { "\"../\" path", "Zpath@1" },
350 { "(novell client) workstation only", "((Znovel@1 OR Zclient@2) OR (Zworkstat@3 OR Zonli@4))" },
351 { "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)" },
352 { "\"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)" },
353 { "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)" },
354 { "Forwarden van domeinnaam (naar HTTP adres)", "((forwarden@1 OR Zvan@2 OR Zdomeinnaam@3) OR (Znaar@4 OR http@5 OR Zadr@6))" },
355 { "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))" },
356 { "httpd (no pid file) not running", "((Zhttpd@1 OR (Zno@2 OR Zpid@3 OR Zfile@4)) OR (Znot@5 OR Zrun@6))" },
357 { "apache httpd (pid file) not running", "(((Zapach@1 OR Zhttpd@2) OR (Zpid@3 OR Zfile@4)) OR (Znot@5 OR Zrun@6))" },
358 { "Klasse is niet geregistreerd (Fout=80040154).", "((klasse@1 OR Zis@2 OR Zniet@3 OR Zgeregistreerd@4) OR (fout@5 OR 80040154@6))" },
359 { "\"dvd+r\" \"dvd-r\"", "((dvd@1 PHRASE 2 r@2) OR (dvd@3 PHRASE 2 r@4))" },
360 { "\"=\" tekens uit csvfile", "(Zteken@1 OR Zuit@2 OR Zcsvfile@3)" },
361 { "libc.so.6(GLIBC_2.3)", "((libc@1 PHRASE 3 so@2 PHRASE 3 6@3) OR glibc_2.3@4)" },
362 { "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))" },
363 { "(t-mobile) bereik", "((t@1 PHRASE 2 mobile@2) OR Zbereik@3)" },
364 { "error LNK2001: unresolved external symbol \"public", "((Zerror@1 OR lnk2001@2 OR Zunresolv@3 OR Zextern@4 OR Zsymbol@5) OR public@6)" },
365 { "patch linux exploit -p)", "((Zpatch@1 OR Zlinux@2 OR Zexploit@3) OR Zp@4)" },
366 { "MYD not found (Errcode: 2)", "((myd@1 OR Znot@2 OR Zfound@3) OR (errcode@4 OR 2@5))" },
367 { "ob_start(\"ob_gzhandler\"); file download", "((ob_start@1 OR ob_gzhandler@2) OR (Zfile@3 OR Zdownload@4))" },
368 { "ECS Elitegroup K7VZA (VIA VT8363/VT8363A)", "((ecs@1 OR elitegroup@2 OR k7vza@3) OR (via@4 OR (vt8363@5 PHRASE 2 vt8363a@6)))" },
369 { "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))" },
370 { "Javascript:history.go(-1)", "((javascript@1 PHRASE 3 history@2 PHRASE 3 go@3) OR 1@4)" },
371 { "java :) als icon", "(Zjava@1 OR (Zal@2 OR Zicon@3))" },
372 { "onmouseover=setPointer(this", "((onmouseover@1 OR setpointer@2) OR Zthis@3)" },
373 { "\" in vbscript", "(in@1 PHRASE 2 vbscript@2)" },
374 { "IRC (FAQ OR (hulp NEAR bij))", "(irc@1 OR (faq@2 OR (hulp@3 NEAR 11 bij@4)))" },
375 { "setProperty(\"McSquare\"+i, _xscale, _xscale++);", "((((setproperty@1 OR mcsquare@2) OR Zi@3) OR _xscale@4) OR _xscale++@5)" },
376 { "[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)" },
377 { "(php.ini) (memory_limit)", "((php@1 PHRASE 2 ini@2) OR Zmemory_limit@3)" },
378 { "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)" },
379 { "VXD NAVEX()@)", "(vxd@1 OR navex@2)" },
380 { "\"Iiyama AS4314UT 17\" \"", "(iiyama@1 PHRASE 3 as4314ut@2 PHRASE 3 17@3)" },
381 { "include (\"$id.html\");", "(Zinclud@1 OR (id@2 PHRASE 2 html@3))" },
382 { "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)" },
383 { "(program files\\common) opstarten", "((Zprogram@1 OR (files@2 PHRASE 2 common@3)) OR Zopstarten@4)" },
384 { "java \" string", "(Zjava@1 OR string@2)" },
385 { "+=", "" },
386 { "php +=", "Zphp@1" },
387 { "[php] ereg_replace(\".\"", "(Zphp@1 OR ereg_replace@2)" },
388 { "\"echo -e\" kleur", "((echo@1 PHRASE 2 e@2) OR Zkleur@3)" },
389 { "adobe premiere \"-1\"", "((Zadob@1 OR Zpremier@2) OR 1@3)" },
390 { "DVD brander \"+\" en \"-\"", "((dvd@1 OR Zbrander@2) OR Zen@3)" },
391 { "inspirion \"dvd+R\"", "(Zinspirion@1 OR (dvd@2 PHRASE 2 r@3))" },
392 { "asp 0x80040E14)", "(Zasp@1 OR 0x80040e14@2)" },
393 { "\"e-tech motorola router", "(e@1 PHRASE 4 tech@2 PHRASE 4 motorola@3 PHRASE 4 router@4)" },
394 { "bluetooth '1.3.2.19\"", "(Zbluetooth@1 OR 1.3.2.19@2)" },
395 { "ms +-connect", "(Zms@1 OR Zconnect@2)" },
396 { "php+print+\"", "(Zphp@1 OR print+@2)" },
397 { "athlon 1400 :welke videokaart\"", "((Zathlon@1 OR 1400@2) OR (Zwelk@3 OR videokaart@4))" },
398 { "+-dvd", "Zdvd@1" },
399 { "glftpd \"-new-\"", "(Zglftpd@1 OR new@2)" },
400 { "\"scandisk + dos5.0", "(scandisk@1 PHRASE 2 dos5.0@2)" },
401 { "socket\\(\\)", "socket@1" },
402 { "msn (e-tech) router", "((Zmsn@1 OR (e@2 PHRASE 2 tech@3)) OR Zrouter@4)" },
403 { "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)" },
404 { "\"CF+bluetooth\"", "(cf@1 PHRASE 2 bluetooth@2)" },
405 { "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))" },
406 { "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)" },
407 { "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))" },
408 { "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)))" },
409 { "\"muziek 2x zo snel\"\"", "(muziek@1 PHRASE 4 2x@2 PHRASE 4 zo@3 PHRASE 4 snel@4)" },
410 { "execCommand('inserthorizontalrule'", "(execcommand@1 OR Zinserthorizontalrul@2)" },
411 { "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))" },
412 { "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)" },
413 { "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))" },
414 { "'\"><br>bla</br>", "(br@1 PHRASE 3 bla@2 PHRASE 3 br@3)" },
415 { "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))" },
416 { "\"(P5A-b)\"", "(p5a@1 PHRASE 2 b@2)" },
417 { "(13,5 > 13) == no-go!", "((13,5@1 OR 13@2) OR (no@3 PHRASE 2 go@4))" },
418 { "eth not found \"ifconfig -a\"", "((Zeth@1 OR Znot@2 OR Zfound@3) OR (ifconfig@4 PHRASE 2 a@5))" },
419 { "<META NAME=\"ROBOTS", "((meta@1 OR name@2) OR robots@3)" },
420 { "lp0: using parport0 (interrupt-driven)", "((Zlp0@1 OR (Zuse@2 OR Zparport0@3)) OR (interrupt@4 PHRASE 2 driven@5))" },
421 { "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)" },
422 { "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))" },
423 { "header(\"Content Type: text/html\");", "((header@1 OR (content@2 OR type@3)) OR (text@4 PHRASE 2 html@5))" },
424 { "\"-RW\" \"+RW\"", "(rw@1 OR rw@2)" },
425 { "\"cresta digital answering machine", "(cresta@1 PHRASE 4 digital@2 PHRASE 4 answering@3 PHRASE 4 machine@4)" },
426 { "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)))" },
427 { "c++ fopen \"r+t\"", "((Zc++@1 OR Zfopen@2) OR (r@3 PHRASE 2 t@4))" },
428 { "c++ fopen (r+t)", "((Zc++@1 OR Zfopen@2) OR (Zr@3 OR Zt@4))" },
429 { "\"DVD+R\"", "(dvd@1 PHRASE 2 r@2)" },
430 { "Class.forName(\"jdbc.odbc.JdbcOdbcDriver\");", "((class@1 PHRASE 2 forname@2) OR (jdbc@3 PHRASE 3 odbc@4 PHRASE 3 jdbcodbcdriver@5))" },
431 { "perl(find.pl)", "(perl@1 OR (find@2 PHRASE 2 pl@3))" },
432 { "\"-5v\" voeding", "(5v@1 OR Zvoed@2)" },
433 { "\"-5v\" power supply", "(5v@1 OR (Zpower@2 OR Zsuppli@3))" },
434 { "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))" },
435 { "(error $2108) Borland", "((Zerror@1 OR 2108@2) OR borland@3)" },
436 { "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))" },
437 { "Elektrotechniek + \"hoe bevalt het?\"\"", "(elektrotechniek@1 OR (hoe@2 PHRASE 3 bevalt@3 PHRASE 3 het@4))" },
438 { "Shortcuts in menu (java", "((shortcuts@1 OR Zin@2 OR Zmenu@3) OR Zjava@4)" },
439 { "detonator+settings\"", "(Zdeton@1 OR settings@2)" },
440 { "(ez-bios) convert", "((ez@1 PHRASE 2 bios@2) OR Zconvert@3)" },
441 { "Sparkle 7100M4 64MB (GeForce4 MX440)", "((sparkle@1 OR 7100m4@2 OR 64mb@3) OR (geforce4@4 OR mx440@5))" },
442 { "freebsd \"boek OR newbie\"", "(Zfreebsd@1 OR (boek@2 PHRASE 3 or@3 PHRASE 3 newbie@4))" },
443 { "for (;;) c++", "(Zfor@1 OR Zc++@2)" },
444 { "1700+-2100+", "(1700+@1 PHRASE 2 2100+@2)" },
445 { "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))" },
446 { "NEC DV-5800B (Bul", "((nec@1 OR (dv@2 PHRASE 2 5800b@3)) OR bul@4)" },
447 { "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))" },
448 { "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))" },
449 { "'q ben\"", "(Zq@1 OR ben@2)" },
450 { "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))" },
451 { "\xc3\xb6ude onderdelen\"", "(Z\xc3\xb6ude@1 OR onderdelen@2)" },
452 { "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))" },
453 { "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))" },
454 { "cybercom \"dvd+r\"", "(Zcybercom@1 OR (dvd@2 PHRASE 2 r@3))" },
455 { "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))" },
456 { "relais +/-", "Zrelai@1" },
457 { "formules (slepen OR doortrekken) excel", "((Zformul@1 OR (Zslepen@2 OR Zdoortrekken@3)) OR Zexcel@4)" },
458 { "\"%English", "english@1" },
459 { "select max( mysql", "((Zselect@1 OR max@2) OR Zmysql@3)" },
460 { "leejow(saait", "(leejow@1 OR Zsaait@2)" },
461 { "'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))" },
462 { "K7T Turbo 2 (MS-6330)", "((k7t@1 OR turbo@2 OR 2@3) OR (ms@4 PHRASE 2 6330@5))" },
463 { "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))" },
464 { "\"cannot find -lz\"", "(cannot@1 PHRASE 3 find@2 PHRASE 3 lz@3)" },
465 { "undefined reference to `mysql_drop_db'\"", "((Zundefin@1 OR Zrefer@2 OR Zto@3) OR Zmysql_drop_db@4)" },
466 { "search form asp \"%'", "(Zsearch@1 OR Zform@2 OR Zasp@3)" },
467 { "(dvd+r) kwaliteit", "((Zdvd@1 OR Zr@2) OR Zkwaliteit@3)" },
468 { "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))" },
469 { "geluid (schokt OR hapert)", "(Zgeluid@1 OR (Zschokt@2 OR Zhapert@3))" },
470 { "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)" },
471 { "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))" },
472 { "(library qt-mt) not found", "((Zlibrari@1 OR (qt@2 PHRASE 2 mt@3)) OR (Znot@4 OR Zfound@5))" },
473 { "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))" },
474 { "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))" },
475 { "Titan TTC-D5TB(4/CU35)", "((titan@1 OR (ttc@2 PHRASE 2 d5tb@3)) OR (4@4 PHRASE 2 cu35@5))" },
476 { "[php] date( min", "((Zphp@1 OR date@2) OR Zmin@3)" },
477 { "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)" },
478 { "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))" },
479 { "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))" },
480 { "ati linux drivers (4.3.0)", "((Zati@1 OR Zlinux@2 OR Zdriver@3) OR 4.3.0@4)" },
481 { "ENCAPSED_AND_WHITESPACE", "encapsed_and_whitespace@1" },
482 { "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))" },
483 { "welke dvd \"+r\" media", "(((Zwelk@1 OR Zdvd@2) OR r@3) OR Zmedia@4)" },
484 { "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))" },
485 { "dvd +/-", "Zdvd@1" },
486 { "7vaxp +voltage mod\"", "(Zvoltag@2 AND_MAYBE (7vaxp@1 OR mod@3))" },
487 { "lpt port (SPP/EPP) is enabled", "(((Zlpt@1 OR Zport@2) OR (spp@3 PHRASE 2 epp@4)) OR (Zis@5 OR Zenabl@6))" },
488 { "getenv(\"HTTP_REFERER\")", "(getenv@1 OR http_referer@2)" },
489 { "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)" },
490 { "Exception number: c0000005 (access violation)", "((exception@1 OR Znumber@2 OR Zc0000005@3) OR (Zaccess@4 OR Zviolat@5))" },
491 { "header(\"Content-type:application/octetstream\");", "(header@1 OR (content@2 PHRASE 4 type@3 PHRASE 4 application@4 PHRASE 4 octetstream@5))" },
492 { "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)))" },
493 { "(001.part.met", "(001@1 PHRASE 3 part@2 PHRASE 3 met@3)" },
494 { "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))" },
495 { "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)" },
496 { "dvd \"+\" \"-\"", "Zdvd@1" },
497 { "bericht ( %)", "Zbericht@1" },
498 { "2500+ of 2600+ (niett OC)", "((2500+@1 OR Zof@2 OR 2600+@3) OR (Zniett@4 OR oc@5))" },
499 { "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))" },
500 { "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))" },
501 { "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))" },
502 { "GA-8IHXP(3.0)", "((ga@1 PHRASE 2 8ihxp@2) OR 3.0@3)" },
503 { "8IHXP(3.0)", "(8ihxp@1 OR 3.0@2)" },
504 { "na\xc2\xb7si (de ~ (m.))", "(Zna\xc2\xb7si@1 OR (Zde@2 OR Zm@3))" },
505 { "header(\"Content-Disposition: attachment;", "(header@1 OR (content@2 PHRASE 3 disposition@3 PHRASE 3 attachment@4))" },
506 { "\"header(\"Content-Disposition: attachment;\"", "((header@1 OR (content@2 PHRASE 2 disposition@3)) OR Zattach@4)" },
507 { "\"Beep -f\"", "(beep@1 PHRASE 2 f@2)" },
508 { "kraan NEAR (Elektrisch OR Electrisch)", "((Zkraan@1 OR near@2) OR (elektrisch@3 OR or@4 OR electrisch@5))" },
509 { "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))" },
510 { "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))" },
511 { "ac3 (0x2000) \"Dolby Laboratories,", "((Zac3@1 OR 0x2000@2) OR (dolby@3 PHRASE 2 laboratories@4))" },
512 { "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))" },
513 { "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))" },
514 { "Motion JPEG (MJPEG codec)", "((motion@1 OR jpeg@2) OR (mjpeg@3 OR Zcodec@4))" },
515 { ": zoomtext\"", "zoomtext@1" },
516 { "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))" },
517 { "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))" },
518 { "\"\"wa is fase\"", "(Zwa@1 OR Zis@2 OR fase@3)" },
519 { "<v:imagedata src=\"", "((v@1 PHRASE 2 imagedata@2) OR src@3)" },
520 { "system(play ringin.wav); ?>", "((system@1 OR Zplay@2) OR (ringin@3 PHRASE 2 wav@4))" },
521 { "\"perfect NEAR systems\"", "(perfect@1 PHRASE 3 near@2 PHRASE 3 systems@3)" },
522 { "LoadLibrary(\"mainta/gamex86.dll\") failed", "((loadlibrary@1 OR (mainta@2 PHRASE 3 gamex86@3 PHRASE 3 dll@4)) OR Zfail@5)" },
523 { "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)" },
524 { "secundaire IDE-controller (dubbele fifo)", "((Zsecundair@1 OR (ide@2 PHRASE 2 controller@3)) OR (Zdubbel@4 OR Zfifo@5))" },
525 { "\"Postal2+Explorer.exe\"", "(postal2@1 PHRASE 3 explorer@2 PHRASE 3 exe@3)" },
526 { "COUNT(*)", "count@1" },
527 { "Nuttige Windows progs (1/11)", "((nuttige@1 OR windows@2 OR Zprog@3) OR (1@4 PHRASE 2 11@5))" },
528 { "if(usercode==passcode==)", "((if@1 OR usercode@2) OR passcode@3)" },
529 { "lg 8160b (dvd+r)", "((Zlg@1 OR 8160b@2) OR (Zdvd@3 OR Zr@4))" },
530 { "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)))" },
531 { "'ipod pakt tags niet\"", "(Zipod@1 OR Zpakt@2 OR Ztag@3 OR niet@4)" },
532 { "\"DVD+/-R\"", "(dvd+@1 PHRASE 2 r@2)" },
533 { "\"DVD+R DVD-R\"", "(dvd@1 PHRASE 4 r@2 PHRASE 4 dvd@3 PHRASE 4 r@4)" },
534 { "php ;) in een array zetten", "(Zphp@1 OR (Zin@2 OR Zeen@3 OR Zarray@4 OR Zzetten@5))" },
535 { "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)" },
536 { "creative (soundblaster OR sb) 128", "((Zcreativ@1 OR (Zsoundblast@2 OR Zsb@3)) OR 128@4)" },
537 { "Can't open file: (errno: 145)", "((can't@1 OR Zopen@2 OR Zfile@3) OR (Zerrno@4 OR 145@5))" },
538 { "Formateren lukt niet(98,XP)", "(((formateren@1 OR Zlukt@2 OR niet@3) OR 98@4) OR xp@5)" },
539 { "access denied (java.io.", "((Zaccess@1 OR Zdeni@2) OR (java@3 PHRASE 2 io@4))" },
540 { "(access denied (java.io.)", "((Zaccess@1 OR Zdeni@2) OR (java@3 PHRASE 2 io@4))" },
541 { "wil niet installeren ( crc fouten)", "((Zwil@1 OR Zniet@2 OR Zinstalleren@3) OR (Zcrc@4 OR Zfouten@5))" },
542 { "(DVD+RW) brandsoftware meerdere", "((dvd@1 OR rw@2) OR (Zbrandsoftwar@3 OR Zmeerder@4))" },
543 { "(database OF databases) EN geheugen", "((Zdatabas@1 OR of@2 OR Zdatabas@3) OR (en@4 OR Zgeheugen@5))" },
544 { "(server 2003) winroute", "((Zserver@1 OR 2003@2) OR Zwinrout@3)" },
545 { "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))" },
546 { "(draadloos OR wireless) netwerk", "((Zdraadloo@1 OR Zwireless@2) OR Znetwerk@3)" },
547 { "localtime(time(NULL));", "((localtime@1 OR time@2) OR null@3)" },
548 { "ob_start(\"ob_gzhandler\");", "(ob_start@1 OR ob_gzhandler@2)" },
549 { "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))" },
550 { "COM+-gebeurtenissysteem", "(com+@1 PHRASE 2 gebeurtenissysteem@2)" },
551 { "rcpthosts (#5.7.1)", "(Zrcpthost@1 OR 5.7.1@2)" },
552 { "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))" },
553 { "window.open( scrollbar", "((window@1 PHRASE 2 open@2) OR Zscrollbar@3)" },
554 { "T68i truc ->", "(t68i@1 OR Ztruc@2)" },
555 { "T68i ->", "t68i@1" },
556 { "\"de lijn is bezet\"\"", "(de@1 PHRASE 4 lijn@2 PHRASE 4 is@3 PHRASE 4 bezet@4)" },
557 { "if (eregi(\"", "(Zif@1 OR eregi@2)" },
558 { "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))" },
559 { "execCommand(\"Paste\");", "(execcommand@1 OR paste@2)" },
560 { "\"-1 unread\"", "(1@1 PHRASE 2 unread@2)" },
561 { "\"www.historical-fire-engines", "(www@1 PHRASE 4 historical@2 PHRASE 4 fire@3 PHRASE 4 engines@4)" },
562 { "\"DVD+RW\" erase", "((dvd@1 PHRASE 2 rw@2) OR Zeras@3)" },
563 { "[showjekamer)", "Zshowjekam@1" },
564 { "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)" },
565 { "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))" },
566 { "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))" },
567 { "vervangen # \"/", "Zvervangen@1" },
568 { "vervangen # /\"", "Zvervangen@1" },
569 { "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)" },
570 { "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))" },
571 { "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))" },
572 { "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))" },
573 { "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))" },
574 { "mag mijn waarschuwing nu weg ? ;)", "(Zmag@1 OR Zmijn@2 OR Zwaarschuw@3 OR Znu@4 OR Zweg@5)" },
575 { "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))" },
576 { "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))" },
577 { "(browser 19) citrix", "((Zbrowser@1 OR 19@2) OR Zcitrix@3)" },
578 { "preg_replace (.*?)", "Zpreg_replac@1" },
579 { "formule excel #naam\"?\"", "((Zformul@1 OR Zexcel@2) OR naam@3)" },
580 { "->", "" },
581 { "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))" },
582 { "<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))" },
583 { "\"rpm -e httpd\"", "(rpm@1 PHRASE 3 e@2 PHRASE 3 httpd@3)" },
584 { "automatisch op All Flis (*.*)", "(Zautomatisch@1 OR Zop@2 OR all@3 OR flis@4)" },
585 { "(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))" },
586 { "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))" },
587 { "\"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)" },
588 { "(./) chmod.sh", "(chmod@1 PHRASE 2 sh@2)" },
589 { "document.write(ssg(\" html", "(((document@1 PHRASE 2 write@2) OR ssg@3) OR html@4)" },
590 { "superstack \"mac+adressen\"", "(Zsuperstack@1 OR (mac@2 PHRASE 2 adressen@3))" },
591 { "IIS getenv(REMOTE_HOST)_", "(((iis@1 OR getenv@2) OR remote_host@3) OR _@4)" },
592 { "IIS en getenv(REMOTE_HOST)", "((iis@1 OR Zen@2 OR getenv@3) OR remote_host@4)" },
593 { "php getenv(\"HTTP_REFERER\")", "((Zphp@1 OR getenv@2) OR http_referer@3)" },
594 { "nec+-1300", "(nec+@1 PHRASE 2 1300@2)" },
595 { "smbpasswd script \"-s\"", "((Zsmbpasswd@1 OR Zscript@2) OR s@3)" },
596 { "leestekens \" \xc3\xb6 \xc3\xab", "(Zleesteken@1 OR (\xc3\xb6@2 PHRASE 2 \xc3\xab@3))" },
597 { "freesco and (all seeing eye)", "((Zfreesco@1 OR Zand@2) OR (Zall@3 OR Zsee@4 OR Zeye@5))" },
598 { "('all seeing eye') and freesco", "((Zall@1 OR Zsee@2 OR Zeye@3) OR (Zand@4 OR Zfreesco@5))" },
599 { "\"[......\"", "" },
600 { "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)))" },
601 { "gegevensfout (cyclishe redundantiecontrole)", "(Zgegevensfout@1 OR (Zcyclish@2 OR Zredundantiecontrol@3))" },
602 { "firmware versie waar NEC\"", "(Zfirmwar@1 OR Zversi@2 OR Zwaar@3 OR nec@4)" },
603 { "nu.nl \"-1\"", "((nu@1 PHRASE 2 nl@2) OR 1@3)" },
604 { "provider+-webspace", "(provider+@1 PHRASE 2 webspace@2)" },
605 { "verschil \"dvd+rw\" \"dvd-rw\"", "((Zverschil@1 OR (dvd@2 PHRASE 2 rw@3)) OR (dvd@4 PHRASE 2 rw@5))" },
606 { "(dhcp client) + hangt", "((Zdhcp@1 OR Zclient@2) OR Zhangt@3)" },
607 { "MSI 875P Neo-FIS2R (Intel 875P)", "(((msi@1 OR 875p@2) OR (neo@3 PHRASE 2 fis2r@4)) OR (intel@5 OR 875p@6))" },
608 { "voeding passief gekoeld\"", "(Zvoed@1 OR Zpassief@2 OR gekoeld@3)" },
609 { "if (mysql_num_rows($resultaat)==1)", "(((Zif@1 OR mysql_num_rows@2) OR Zresultaat@3) OR 1@4)" },
610 { "Server.CreateObject(\"Persits.Upload.1\")", "((server@1 PHRASE 2 createobject@2) OR (persits@3 PHRASE 3 upload@4 PHRASE 3 1@5))" },
611 { "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))" },
612 { "if (cod>9999999", "(Zif@1 OR (cod@2 OR 9999999@3))" },
613 { "\"rm -rf /bin/laden\"", "(rm@1 PHRASE 4 rf@2 PHRASE 4 bin@3 PHRASE 4 laden@4)" },
614 { "\">>> 0) & 0xFF\"", "(0@1 PHRASE 2 0xff@2)" },
615 { "<!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))" },
616 { "<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)" },
617 { "linux humor :)", "(Zlinux@1 OR Zhumor@2)" },
618 { "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))" },
619 { "remote_smtp defer (-44)", "((Zremote_smtp@1 OR Zdefer@2) OR 44@3)" },
620 { "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)" },
621 { "Koper + amoniak (NH2", "((koper@1 OR Zamoniak@2) OR nh2@3)" },
622 { "nec dvd -/+r", "((Znec@1 OR Zdvd@2) AND_NOT Zr@3)" },
623 { "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)" },
624 { "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))" },
625 { "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))" },
626 { "\"~\" + \"c:\\\"", "Zc@1" },
627 { "mysql count(*)", "(Zmysql@1 OR count@2)" },
628 { "for %f in (*.*) do", "((Zfor@1 OR (Zf@2 OR Zin@3)) OR Zdo@4)" },
629 { "raar \"~\" bestand", "(Zraar@1 OR Zbestand@2)" },
630 { "NEC DVD +-R/RW 1300", "(((nec@1 OR dvd@2) OR (r@3 PHRASE 2 rw@4)) OR 1300@5)" },
631 { "approved (ref: 38446-263)", "(Zapprov@1 OR (Zref@2 OR (38446@3 PHRASE 2 263@4)))" },
632 { "GA-7VRXP(2.0)", "((ga@1 PHRASE 2 7vrxp@2) OR 2.0@3)" },
633 { "~ Could not retrieve directory listing for \"/\"", "(could@1 OR Znot@2 OR Zretriev@3 OR Zdirectori@4 OR Zlist@5 OR Zfor@6)" },
634 { "asp CreateObject(\"Word.Document\")", "((Zasp@1 OR createobject@2) OR (word@3 PHRASE 2 document@4))" },
635 { "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))" },
636 { "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)))" },
637 { "parent.document.getElementById(\\\"leftmenu\\\").cols", "(((parent@1 PHRASE 3 document@2 PHRASE 3 getelementbyid@3) OR leftmenu@4) OR Zcol@5)" },
638 { "<% if not isEmpty(Request.QueryString) then", "(((Zif@1 OR Znot@2 OR isempty@3) OR (request@4 PHRASE 2 querystring@5)) OR Zthen@6)" },
639 { "Active Desktop (Hier issie)", "((active@1 OR desktop@2) OR (hier@3 OR Zissi@4))" },
640 { "Asus A7V8X (LAN + Sound)", "((asus@1 OR a7v8x@2) OR (lan@3 OR sound@4))" },
641 { "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)" },
642 { "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)" },
643 { "session_register(\"login\");", "(session_register@1 OR login@2)" },
644 { "\"kylix+ndmb\"", "(kylix@1 PHRASE 2 ndmb@2)" },
645 { "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))" },
646 { "If ($_SESSION[\"Login\"] == 1)", "(if@1 OR ((_session@2 OR login@3) OR 1@4))" },
647 { "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))" },
648 { "ASRock K7VT2 (incl. LAN)", "((asrock@1 OR k7vt2@2) OR (Zincl@3 OR lan@4))" },
649 { "+windows98 +(geen communicatie) +ie5", "((Zwindows98@1 AND (Zgeen@2 OR Zcommunicati@3)) AND Zie5@4)" },
650 { "\"xterm -fn\"", "(xterm@1 PHRASE 2 fn@2)" },
651 { "IRQL_NOT_LESS_OR_EQUAL", "irql_not_less_or_equal@1" },
652 { "access query \"NOT IN\"", "((Zaccess@1 OR Zqueri@2) OR (not@3 PHRASE 2 in@4))" },
653 { "\"phrase one \"phrase two\"", "((phrase@1 PHRASE 2 one@2) OR (Zphrase@3 OR two@4))" },
654 { "NEAR 207 46 249 27", "(near@1 OR 207@2 OR 46@3 OR 249@4 OR 27@5)" },
655 { "- NEAR 12V voeding", "(near@1 OR 12v@2 OR Zvoed@3)" },
656 { "waarom \"~\" in directorynaam", "(Zwaarom@1 OR (Zin@2 OR Zdirectorynaam@3))" },
657 { "cd'r NEAR toebehoren", "(cd'r@1 NEAR 11 toebehoren@2)" },
658 { "site:1 site:2", "0 * (H1 OR H2)" },
659 { "site:1 site2:2", "0 * (H1 AND J2)" },
660 { "site:1 site:2 site2:2", "0 * ((H1 OR H2) AND J2)" },
661 { "site:1 OR site:2", "(0 * H1 OR 0 * H2)" },
662 { "site:1 AND site:2", "(0 * H1 AND 0 * H2)" },
663 { "foo AND site:2", "(Zfoo@1 AND 0 * H2)" },
664 // Non-exclusive boolean prefixes feature tests (ticket#402):
665 { "category:1 category:2", "0 * (XCAT1 AND XCAT2)" },
666 { "category:1 site2:2", "0 * (XCAT1 AND J2)" },
667 { "category:1 category:2 site2:2", "0 * ((XCAT1 AND XCAT2) AND J2)" },
668 { "category:1 OR category:2", "(0 * XCAT1 OR 0 * XCAT2)" },
669 { "category:1 AND category:2", "(0 * XCAT1 AND 0 * XCAT2)" },
670 { "foo AND category:2", "(Zfoo@1 AND 0 * XCAT2)" },
671 // Regression test for combining multiple non-exclusive prefixes, fixed in
672 // 1.2.22 and 1.3.4.
673 { "category:1 dogegory:2", "0 * (XCAT1 AND XDOG2)" },
674 { "A site:1 site:2", "(a@1 FILTER (H1 OR H2))" },
675 #if 0
676 { "A (site:1 OR site:2)", "(a@1 FILTER (H1 OR H2))" },
677 #endif
678 { "A site:1 site2:2", "(a@1 FILTER (H1 AND J2))" },
679 { "A site:1 site:2 site2:2", "(a@1 FILTER ((H1 OR H2) AND J2))" },
680 #if 0
681 { "A site:1 OR site:2", "(a@1 FILTER (H1 OR H2))" },
682 #endif
683 { "A site:1 AND site:2", "((a@1 FILTER H1) AND 0 * H2)" },
684 { "site:xapian.org OR site:www.xapian.org", "(0 * Hxapian.org OR 0 * Hwww.xapian.org)" },
685 { "site:xapian.org site:www.xapian.org", "0 * (Hxapian.org OR Hwww.xapian.org)" },
686 { "site:xapian.org AND site:www.xapian.org", "(0 * Hxapian.org AND 0 * Hwww.xapian.org)" },
687 { "Xapian site:xapian.org site:www.xapian.org", "(xapian@1 FILTER (Hxapian.org OR Hwww.xapian.org))" },
688 { "author:richard author:olly writer:charlie", "(ZArichard@1 OR ZAolli@2 OR ZAcharli@3)"},
689 { "author:richard NEAR title:book", "(Arichard@1 NEAR 11 XTbook@2)"},
690 // FIXME: This throws an exception as of 1.3.6, but once implemented we
691 // should re-enable it.
692 // { "authortitle:richard NEAR title:book", "((Arichard@1 OR XTrichard@1) NEAR 11 XTbook@2)" },
693 { "multisite:xapian.org", "0 * (Hxapian.org OR Jxapian.org)"},
694 { "authortitle:richard", "(ZArichard@1 OR ZXTrichard@1)"},
695 { "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))" },
696 { "authortitle:richard-boulton", "((Arichard@1 PHRASE 2 Aboulton@2) OR (XTrichard@1 PHRASE 2 XTboulton@2))"},
697 { "authortitle:\"richard boulton\"", "((Arichard@1 PHRASE 2 Aboulton@2) OR (XTrichard@1 PHRASE 2 XTboulton@2))"},
698 // Test FLAG_CJK_NGRAM isn't on by default:
699 { "久有归天愿", "Z久有归天愿@1" },
700 { NULL, "CJK" }, // Enable FLAG_CJK_NGRAM
701 // Test non-CJK queries still parse the same:
702 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
703 { "“curly quotes”", "(curly@1 PHRASE 2 quotes@2)" },
704 // Test n-gram generation:
705 { "久有归天愿", "(久@1 AND 久有@1 AND 有@1 AND 有归@1 AND 归@1 AND 归天@1 AND 天@1 AND 天愿@1 AND 愿@1)" },
706 { "久有 归天愿", "((久@1 AND 久有@1 AND 有@1) OR (归@2 AND 归天@2 AND 天@2 AND 天愿@2 AND 愿@2))" },
707 { "久有!归天愿", "((久@1 AND 久有@1 AND 有@1) OR (归@2 AND 归天@2 AND 天@2 AND 天愿@2 AND 愿@2))" },
708 { "title:久有 归 天愿", "(((XT久@1 AND XT久有@1 AND XT有@1) OR 归@2) OR (天@3 AND 天愿@3 AND 愿@3))" },
709 { "h众ello万众", "(((Zh@1 OR 众@2) OR Zello@3) OR (万@4 AND 万众@4 AND 众@4))" },
710 { "世(の中)TEST_tm", "((世@1 OR (の@2 AND の中@2 AND 中@2)) OR test_tm@3)" },
711 { "다녀 AND 와야", "((다@1 AND 다녀@1 AND 녀@1) AND (와@2 AND 와야@2 AND 야@2))" },
712 { "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))" },
713 // FIXME: These should really filter by bigrams to accelerate:
714 { "\"久有归\"", "(久@1 PHRASE 3 有@1 PHRASE 3 归@1)" },
715 { "\"久有test归\"", "(久@1 PHRASE 4 有@1 PHRASE 4 test@2 PHRASE 4 归@3)" },
716 // FIXME: this should work: { "久 NEAR 有", "(久@1 NEAR 11 有@2)" },
717 { NULL, NULL }
720 DEFINE_TESTCASE(queryparser1, !backend) {
721 Xapian::QueryParser queryparser;
722 queryparser.set_stemmer(Xapian::Stem("english"));
723 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
724 queryparser.add_prefix("author", "A");
725 queryparser.add_prefix("writer", "A");
726 queryparser.add_prefix("title", "XT");
727 queryparser.add_prefix("subject", "XT");
728 queryparser.add_prefix("authortitle", "A");
729 queryparser.add_prefix("authortitle", "XT");
730 queryparser.add_boolean_prefix("site", "H");
731 queryparser.add_boolean_prefix("site2", "J");
732 queryparser.add_boolean_prefix("multisite", "H");
733 queryparser.add_boolean_prefix("multisite", "J");
734 queryparser.add_boolean_prefix("category", "XCAT", false);
735 queryparser.add_boolean_prefix("dogegory", "XDOG", false);
736 TEST_EXCEPTION(Xapian::InvalidOperationError,
737 queryparser.add_boolean_prefix("authortitle", "B");
739 TEST_EXCEPTION(Xapian::InvalidOperationError,
740 queryparser.add_prefix("multisite", "B");
742 unsigned flags = queryparser.FLAG_DEFAULT;
743 for (const test *p = test_or_queries; ; ++p) {
744 if (!p->query) {
745 if (!p->expect) break;
746 if (strcmp(p->expect, "CJK") == 0) {
747 flags = queryparser.FLAG_DEFAULT|queryparser.FLAG_CJK_NGRAM;
748 continue;
750 FAIL_TEST("Unknown flag code: " << p->expect);
752 string expect, parsed;
753 if (p->expect)
754 expect = p->expect;
755 else
756 expect = "parse error";
757 try {
758 Xapian::Query qobj = queryparser.parse_query(p->query, flags);
759 parsed = qobj.get_description();
760 expect = string("Query(") + expect + ')';
761 } catch (const Xapian::QueryParserError &e) {
762 parsed = e.get_msg();
763 } catch (const Xapian::Error &e) {
764 parsed = e.get_description();
765 } catch (...) {
766 parsed = "Unknown exception!";
768 tout << "Query: " << p->query << '\n';
769 TEST_STRINGS_EQUAL(parsed, expect);
771 return true;
774 static const test test_and_queries[] = {
775 { "internet explorer title:(http www)", "((Zinternet@1 AND Zexplor@2) AND (ZXThttp@3 AND ZXTwww@4))" },
776 // Regression test for bug in 0.9.2 and earlier - this would give
777 // (two@2 AND_MAYBE (one@1 AND three@3))
778 { "one +two three", "((Zone@1 AND Ztwo@2) AND Zthree@3)" },
779 { "hello -title:\"hello world\"", "(Zhello@1 AND_NOT (XThello@2 PHRASE 2 XTworld@3))" },
780 // Regression test for bug fixed in 1.0.4 - the '-' would be ignored there
781 // because the whitespace after the '"' wasn't noticed.
782 { "\"hello world\" -C++", "((hello@1 PHRASE 2 world@2) AND_NOT c++@3)" },
783 // Regression tests for bug fixed in 1.0.4 - queries with only boolean
784 // filter and HATE terms weren't accepted.
785 { "-cup site:world", "(0 * Hworld AND_NOT Zcup@1)" },
786 { "site:world -cup", "(0 * Hworld AND_NOT Zcup@1)" },
787 // Regression test for bug fixed in 1.0.4 - the KET token for ')' was lost.
788 { "(site:world) -cup", "(0 * Hworld AND_NOT Zcup@1)" },
789 // Regression test for bug fixed in 1.0.4 - a boolean filter term between
790 // probabilistic terms caused a parse error (probably broken during the
791 // addition of synonym support in 1.0.2).
792 { "foo site:xapian.org bar", "((Zfoo@1 AND Zbar@2) FILTER Hxapian.org)" },
793 // Add coverage for other cases similar to the above.
794 { "a b site:xapian.org", "((Za@1 AND Zb@2) FILTER Hxapian.org)" },
795 { "site:xapian.org a b", "((Za@1 AND Zb@2) FILTER Hxapian.org)" },
796 { NULL, "CJK" }, // Enable FLAG_CJK_NGRAM
797 // Test n-gram generation:
798 { "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)))" },
799 { "洛伊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)" },
800 { NULL, NULL }
803 // With default_op = OP_AND.
804 DEFINE_TESTCASE(qp_default_op1, !backend) {
805 Xapian::QueryParser queryparser;
806 queryparser.set_stemmer(Xapian::Stem("english"));
807 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
808 queryparser.add_prefix("author", "A");
809 queryparser.add_prefix("title", "XT");
810 queryparser.add_prefix("subject", "XT");
811 queryparser.add_boolean_prefix("site", "H");
812 queryparser.set_default_op(Xapian::Query::OP_AND);
813 unsigned flags = queryparser.FLAG_DEFAULT;
814 for (const test *p = test_and_queries; ; ++p) {
815 if (!p->query) {
816 if (!p->expect) break;
817 if (strcmp(p->expect, "CJK") == 0) {
818 flags = queryparser.FLAG_DEFAULT|queryparser.FLAG_CJK_NGRAM;
819 continue;
821 FAIL_TEST("Unknown flag code: " << p->expect);
823 string expect, parsed;
824 if (p->expect)
825 expect = p->expect;
826 else
827 expect = "parse error";
828 try {
829 Xapian::Query qobj = queryparser.parse_query(p->query, flags);
830 parsed = qobj.get_description();
831 expect = string("Query(") + expect + ')';
832 } catch (const Xapian::QueryParserError &e) {
833 parsed = e.get_msg();
834 } catch (const Xapian::Error &e) {
835 parsed = e.get_description();
836 } catch (...) {
837 parsed = "Unknown exception!";
839 tout << "Query: " << p->query << '\n';
840 TEST_STRINGS_EQUAL(parsed, expect);
842 return true;
845 // Feature test for specify the default prefix (new in Xapian 1.0.0).
846 DEFINE_TESTCASE(qp_default_prefix1, !backend) {
847 Xapian::QueryParser qp;
848 qp.set_stemmer(Xapian::Stem("english"));
849 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
850 qp.add_prefix("title", "XT");
852 Xapian::Query qobj;
853 qobj = qp.parse_query("hello world", 0, "A");
854 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAhello@1 OR ZAworld@2))");
855 qobj = qp.parse_query("me title:stuff", 0, "A");
856 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAme@1 OR ZXTstuff@2))");
857 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "A");
858 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZAme@2))");
859 qobj = qp.parse_query("英国 title:文森hello", qp.FLAG_CJK_NGRAM, "A");
860 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))");
861 return true;
864 // Feature test for setting the default prefix with add_prefix()
865 // (new in Xapian 1.0.3).
866 DEFINE_TESTCASE(qp_default_prefix2, !backend) {
867 Xapian::QueryParser qp;
868 qp.set_stemmer(Xapian::Stem("english"));
869 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
871 // test that default prefixes can only be set with add_prefix().
872 TEST_EXCEPTION(Xapian::UnimplementedError,
873 qp.add_boolean_prefix("", "B");
876 qp.add_prefix("title", "XT");
877 qp.add_prefix("", "A");
879 Xapian::Query qobj;
880 qobj = qp.parse_query("hello world", 0);
881 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAhello@1 OR ZAworld@2))");
882 qobj = qp.parse_query("me title:stuff", 0);
883 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZAme@1 OR ZXTstuff@2))");
884 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN);
885 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZAme@2))");
887 qobj = qp.parse_query("hello world", 0, "B");
888 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZBhello@1 OR ZBworld@2))");
889 qobj = qp.parse_query("me title:stuff", 0, "B");
890 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZBme@1 OR ZXTstuff@2))");
891 qobj = qp.parse_query("title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "B");
892 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((ZXTstuff@1 OR ZBme@2))");
894 qp.add_prefix("", "B");
895 qobj = qp.parse_query("me-us title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN);
896 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)))");
897 qobj = qp.parse_query("me-us title:(stuff) me", Xapian::QueryParser::FLAG_BOOLEAN, "C");
898 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((((Cme@1 PHRASE 2 Cus@2) OR ZXTstuff@3) OR ZCme@4))");
900 qobj = qp.parse_query("me-us title:\"not-me\"", Xapian::QueryParser::FLAG_PHRASE);
901 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)))");
902 return true;
905 // Test query with odd characters in.
906 DEFINE_TESTCASE(qp_odd_chars1, !backend) {
907 Xapian::QueryParser qp;
908 string query("\x01weird\x00stuff\x7f", 13);
909 Xapian::Query qobj = qp.parse_query(query);
910 tout << "Query: " << query << '\n';
911 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((weird@1 OR stuff@2))"); // FIXME: should these be stemmed?
912 return true;
915 // Test right truncation.
916 DEFINE_TESTCASE(qp_flag_wildcard1, writable) {
917 Xapian::WritableDatabase db = get_writable_database();
918 Xapian::Document doc;
919 doc.add_term("abc");
920 doc.add_term("main");
921 doc.add_term("muscat");
922 doc.add_term("muscle");
923 doc.add_term("musclebound");
924 doc.add_term("muscular");
925 doc.add_term("mutton");
926 db.add_document(doc);
927 Xapian::QueryParser qp;
928 qp.set_database(db);
929 Xapian::Query qobj = qp.parse_query("ab*", Xapian::QueryParser::FLAG_WILDCARD);
930 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR ab))");
931 qobj = qp.parse_query("muscle*", Xapian::QueryParser::FLAG_WILDCARD);
932 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR muscle))");
933 qobj = qp.parse_query("meat*", Xapian::QueryParser::FLAG_WILDCARD);
934 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR meat))");
935 qobj = qp.parse_query("musc*", Xapian::QueryParser::FLAG_WILDCARD);
936 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR musc))");
937 qobj = qp.parse_query("mutt*", Xapian::QueryParser::FLAG_WILDCARD);
938 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR mutt))");
939 // Regression test (we weren't lowercasing terms before checking if they
940 // were in the database or not):
941 qobj = qp.parse_query("mUTTON++");
942 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(mutton@1)");
943 // Regression test: check that wildcards work with +terms.
944 unsigned flags = Xapian::QueryParser::FLAG_WILDCARD |
945 Xapian::QueryParser::FLAG_LOVEHATE;
946 qobj = qp.parse_query("+mai* main", flags);
947 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR mai) AND_MAYBE main@2))");
948 // Regression test (if we had a +term which was a wildcard and wasn't
949 // present, the query could still match documents).
950 qobj = qp.parse_query("foo* main", flags);
951 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) OR main@2))");
952 qobj = qp.parse_query("main foo*", flags);
953 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 OR (SYNONYM WILDCARD OR foo)))");
954 qobj = qp.parse_query("+foo* main", flags);
955 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE main@2))");
956 qobj = qp.parse_query("main +foo*", flags);
957 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE main@1))");
958 qobj = qp.parse_query("foo* +main", flags);
959 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@2 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
960 qobj = qp.parse_query("+main foo*", flags);
961 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
962 qobj = qp.parse_query("+foo* +main", flags);
963 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND main@2))");
964 qobj = qp.parse_query("+main +foo*", flags);
965 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND (SYNONYM WILDCARD OR foo)))");
966 qobj = qp.parse_query("foo* mai", flags);
967 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) OR mai@2))");
968 qobj = qp.parse_query("mai foo*", flags);
969 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@1 OR (SYNONYM WILDCARD OR foo)))");
970 qobj = qp.parse_query("+foo* mai", flags);
971 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE mai@2))");
972 qobj = qp.parse_query("mai +foo*", flags);
973 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_MAYBE mai@1))");
974 qobj = qp.parse_query("foo* +mai", flags);
975 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@2 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
976 qobj = qp.parse_query("+mai foo*", flags);
977 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@1 AND_MAYBE (SYNONYM WILDCARD OR foo)))");
978 qobj = qp.parse_query("+foo* +mai", flags);
979 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND mai@2))");
980 qobj = qp.parse_query("+mai +foo*", flags);
981 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((mai@1 AND (SYNONYM WILDCARD OR foo)))");
982 qobj = qp.parse_query("-foo* main", flags);
983 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@2 AND_NOT (SYNONYM WILDCARD OR foo)))");
984 qobj = qp.parse_query("main -foo*", flags);
985 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT (SYNONYM WILDCARD OR foo)))");
986 qobj = qp.parse_query("main -foo* -bar", flags);
987 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT ((SYNONYM WILDCARD OR foo) OR bar@3)))");
988 qobj = qp.parse_query("main -bar -foo*", flags);
989 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT (bar@2 OR (SYNONYM WILDCARD OR foo))))");
990 // Check with OP_AND too.
991 qp.set_default_op(Xapian::Query::OP_AND);
992 qobj = qp.parse_query("foo* main", flags);
993 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND main@2))");
994 qobj = qp.parse_query("main foo*", flags);
995 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND (SYNONYM WILDCARD OR foo)))");
996 qp.set_default_op(Xapian::Query::OP_AND);
997 qobj = qp.parse_query("+foo* main", flags);
998 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND main@2))");
999 qobj = qp.parse_query("main +foo*", flags);
1000 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND (SYNONYM WILDCARD OR foo)))");
1001 qobj = qp.parse_query("-foo* main", flags);
1002 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@2 AND_NOT (SYNONYM WILDCARD OR foo)))");
1003 qobj = qp.parse_query("main -foo*", flags);
1004 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((main@1 AND_NOT (SYNONYM WILDCARD OR foo)))");
1005 // Check empty wildcard followed by negation.
1006 qobj = qp.parse_query("foo* -main", Xapian::QueryParser::FLAG_LOVEHATE|Xapian::QueryParser::FLAG_WILDCARD);
1007 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR foo) AND_NOT main@2))");
1008 // Regression test for bug#484 fixed in 1.2.1 and 1.0.21.
1009 qobj = qp.parse_query("abc muscl* main", flags);
1010 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((abc@1 AND (SYNONYM WILDCARD OR muscl)) AND main@3))");
1011 return true;
1014 // Test right truncation with prefixes.
1015 DEFINE_TESTCASE(qp_flag_wildcard2, writable) {
1016 Xapian::WritableDatabase db = get_writable_database();
1017 Xapian::Document doc;
1018 doc.add_term("Aheinlein");
1019 doc.add_term("Ahuxley");
1020 doc.add_term("hello");
1021 db.add_document(doc);
1022 Xapian::QueryParser qp;
1023 qp.set_database(db);
1024 qp.add_prefix("author", "A");
1025 Xapian::Query qobj;
1026 qobj = qp.parse_query("author:h*", Xapian::QueryParser::FLAG_WILDCARD);
1027 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((SYNONYM WILDCARD OR Ah))");
1028 qobj = qp.parse_query("author:h* test", Xapian::QueryParser::FLAG_WILDCARD);
1029 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR Ah) OR test@2))");
1030 return true;
1033 static void
1034 test_qp_flag_wildcard1_helper(const Xapian::Database &db,
1035 Xapian::termcount max_expansion,
1036 const string & query_string)
1038 Xapian::QueryParser qp;
1039 qp.set_database(db);
1040 qp.set_max_expansion(max_expansion);
1041 Xapian::Enquire e(db);
1042 e.set_query(qp.parse_query(query_string, Xapian::QueryParser::FLAG_WILDCARD));
1043 // The exception for expanding too much may happen at parse time or later
1044 // so we need to calculate the MSet too.
1045 e.get_mset(0, 10);
1048 // Test right truncation with a limit on expansion.
1049 DEFINE_TESTCASE(qp_flag_wildcard3, writable) {
1050 Xapian::WritableDatabase db = get_writable_database();
1051 Xapian::Document doc;
1052 doc.add_term("abc");
1053 doc.add_term("main");
1054 doc.add_term("muscat");
1055 doc.add_term("muscle");
1056 doc.add_term("musclebound");
1057 doc.add_term("muscular");
1058 doc.add_term("mutton");
1059 db.add_document(doc);
1061 // Test that a max of 0 doesn't set a limit.
1062 test_qp_flag_wildcard1_helper(db, 0, "z*");
1063 test_qp_flag_wildcard1_helper(db, 0, "m*");
1065 // These cases should expand to the limit given.
1066 test_qp_flag_wildcard1_helper(db, 1, "z*");
1067 test_qp_flag_wildcard1_helper(db, 1, "ab*");
1068 test_qp_flag_wildcard1_helper(db, 2, "muscle*");
1069 test_qp_flag_wildcard1_helper(db, 4, "musc*");
1070 test_qp_flag_wildcard1_helper(db, 4, "mus*");
1071 test_qp_flag_wildcard1_helper(db, 5, "mu*");
1072 test_qp_flag_wildcard1_helper(db, 6, "m*");
1074 // These cases should expand to one more than the limit.
1075 TEST_EXCEPTION(Xapian::WildcardError,
1076 test_qp_flag_wildcard1_helper(db, 1, "muscle*"));
1077 TEST_EXCEPTION(Xapian::WildcardError,
1078 test_qp_flag_wildcard1_helper(db, 3, "musc*"));
1079 TEST_EXCEPTION(Xapian::WildcardError,
1080 test_qp_flag_wildcard1_helper(db, 3, "mus*"));
1081 TEST_EXCEPTION(Xapian::WildcardError,
1082 test_qp_flag_wildcard1_helper(db, 4, "mu*"));
1083 TEST_EXCEPTION(Xapian::WildcardError,
1084 test_qp_flag_wildcard1_helper(db, 5, "m*"));
1086 return true;
1089 // Test partial queries.
1090 DEFINE_TESTCASE(qp_flag_partial1, writable) {
1091 Xapian::WritableDatabase db = get_writable_database();
1092 Xapian::Document doc;
1093 Xapian::Stem stemmer("english");
1094 doc.add_term("abc");
1095 doc.add_term("main");
1096 doc.add_term("muscat");
1097 doc.add_term("muscle");
1098 doc.add_term("musclebound");
1099 doc.add_term("muscular");
1100 doc.add_term("mutton");
1101 doc.add_term("Z" + stemmer("outside"));
1102 doc.add_term("Z" + stemmer("out"));
1103 doc.add_term("outside");
1104 doc.add_term("out");
1105 doc.add_term("XTcove");
1106 doc.add_term("XTcows");
1107 doc.add_term("XTcowl");
1108 doc.add_term("XTcox");
1109 doc.add_term("ZXTcow");
1110 doc.add_term("XONEpartial");
1111 doc.add_term("XONEpartial2");
1112 doc.add_term("XTWOpartial3");
1113 doc.add_term("XTWOpartial4");
1114 db.add_document(doc);
1115 Xapian::QueryParser qp;
1116 qp.set_database(db);
1117 qp.set_stemmer(stemmer);
1118 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1119 qp.add_prefix("title", "XT");
1120 qp.add_prefix("double", "XONE");
1121 qp.add_prefix("double", "XTWO");
1123 // Check behaviour with unstemmed terms
1124 Xapian::Query qobj = qp.parse_query("a", Xapian::QueryParser::FLAG_PARTIAL);
1125 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR a) OR Za@1))");
1126 qobj = qp.parse_query("ab", Xapian::QueryParser::FLAG_PARTIAL);
1127 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR ab) OR Zab@1))");
1128 qobj = qp.parse_query("muscle", Xapian::QueryParser::FLAG_PARTIAL);
1129 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR muscle) OR Zmuscl@1))");
1130 qobj = qp.parse_query("meat", Xapian::QueryParser::FLAG_PARTIAL);
1131 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR meat) OR Zmeat@1))");
1132 qobj = qp.parse_query("musc", Xapian::QueryParser::FLAG_PARTIAL);
1133 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR musc) OR Zmusc@1))");
1134 qobj = qp.parse_query("mutt", Xapian::QueryParser::FLAG_PARTIAL);
1135 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR mutt) OR Zmutt@1))");
1136 qobj = qp.parse_query("abc musc", Xapian::QueryParser::FLAG_PARTIAL);
1137 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((Zabc@1 OR ((SYNONYM WILDCARD OR musc) OR Zmusc@2)))");
1138 qobj = qp.parse_query("a* mutt", Xapian::QueryParser::FLAG_PARTIAL | Xapian::QueryParser::FLAG_WILDCARD);
1139 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR a) OR ((SYNONYM WILDCARD OR mutt) OR Zmutt@2)))");
1141 // Check behaviour with stemmed terms, and stem strategy STEM_SOME.
1142 qobj = qp.parse_query("o", Xapian::QueryParser::FLAG_PARTIAL);
1143 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR o) OR Zo@1))");
1144 qobj = qp.parse_query("ou", Xapian::QueryParser::FLAG_PARTIAL);
1145 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR ou) OR Zou@1))");
1146 qobj = qp.parse_query("out", Xapian::QueryParser::FLAG_PARTIAL);
1147 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR Zout@1))");
1148 qobj = qp.parse_query("outs", Xapian::QueryParser::FLAG_PARTIAL);
1149 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR Zout@1))");
1150 qobj = qp.parse_query("outsi", Xapian::QueryParser::FLAG_PARTIAL);
1151 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outsi) OR Zoutsi@1))");
1152 qobj = qp.parse_query("outsid", Xapian::QueryParser::FLAG_PARTIAL);
1153 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outsid) OR Zoutsid@1))");
1154 qobj = qp.parse_query("outside", Xapian::QueryParser::FLAG_PARTIAL);
1155 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR Zoutsid@1))");
1157 // Check behaviour with capitalised terms, and stem strategy STEM_SOME.
1158 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1159 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1160 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1161 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR outs@1))");
1162 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1163 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outside@1))");
1164 // FIXME: Used to be this, but we aren't currently doing this change:
1165 // TEST_STRINGS_EQUAL(qobj.get_description(), "Query(outside@1#2)");
1167 // And now with stemming strategy STEM_SOME_FULL_POS.
1168 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME_FULL_POS);
1169 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1170 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1171 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1172 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR outs@1))");
1173 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1174 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outside@1))");
1176 // And now with stemming strategy STEM_ALL.
1177 qp.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
1178 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1179 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR out@1))");
1180 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1181 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR out@1))");
1182 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1183 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR outsid@1))");
1185 // And now with stemming strategy STEM_ALL_Z.
1186 qp.set_stemming_strategy(Xapian::QueryParser::STEM_ALL_Z);
1187 qobj = qp.parse_query("Out", Xapian::QueryParser::FLAG_PARTIAL);
1188 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR out) OR Zout@1))");
1189 qobj = qp.parse_query("Outs", Xapian::QueryParser::FLAG_PARTIAL);
1190 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outs) OR Zout@1))");
1191 qobj = qp.parse_query("Outside", Xapian::QueryParser::FLAG_PARTIAL);
1192 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR outside) OR Zoutsid@1))");
1194 // Check handling of a case with a prefix.
1195 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
1196 qobj = qp.parse_query("title:cow", Xapian::QueryParser::FLAG_PARTIAL);
1197 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcow) OR ZXTcow@1))");
1198 qobj = qp.parse_query("title:cows", Xapian::QueryParser::FLAG_PARTIAL);
1199 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcows) OR ZXTcow@1))");
1200 qobj = qp.parse_query("title:Cow", Xapian::QueryParser::FLAG_PARTIAL);
1201 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcow) OR XTcow@1))");
1202 qobj = qp.parse_query("title:Cows", Xapian::QueryParser::FLAG_PARTIAL);
1203 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((SYNONYM WILDCARD OR XTcows) OR XTcows@1))");
1204 // FIXME: Used to be this, but we aren't currently doing this change:
1205 // TEST_STRINGS_EQUAL(qobj.get_description(), "Query(XTcows@1#2)");
1207 // Regression test - the initial version of the multi-prefix code would
1208 // inflate the wqf of the "parsed as normal" version of a partial term
1209 // by multiplying it by the number of prefixes mapped to.
1210 qobj = qp.parse_query("double:vision", Xapian::QueryParser::FLAG_PARTIAL);
1211 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEvision SYNONYM WILDCARD OR XTWOvision) OR (ZXONEvision@1 SYNONYM ZXTWOvision@1)))");
1213 // Test handling of FLAG_PARTIAL when there's more than one prefix.
1214 qobj = qp.parse_query("double:part", Xapian::QueryParser::FLAG_PARTIAL);
1215 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpart SYNONYM WILDCARD OR XTWOpart) OR (ZXONEpart@1 SYNONYM ZXTWOpart@1)))");
1217 // Test handling of FLAG_PARTIAL when there's more than one prefix, without
1218 // stemming.
1219 qp.set_stemming_strategy(Xapian::QueryParser::STEM_NONE);
1220 qobj = qp.parse_query("double:part", Xapian::QueryParser::FLAG_PARTIAL);
1221 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpart SYNONYM WILDCARD OR XTWOpart) OR (XONEpart@1 SYNONYM XTWOpart@1)))");
1222 qobj = qp.parse_query("double:partial", Xapian::QueryParser::FLAG_PARTIAL);
1223 TEST_STRINGS_EQUAL(qobj.get_description(), "Query(((WILDCARD OR XONEpartial SYNONYM WILDCARD OR XTWOpartial) OR (XONEpartial@1 SYNONYM XTWOpartial@1)))");
1225 return true;
1228 // Tests for document counts for wildcard queries.
1229 // Regression test for bug fixed in 1.0.0.
1230 DEFINE_TESTCASE(wildquery1, backend) {
1231 Xapian::QueryParser queryparser;
1232 unsigned flags = Xapian::QueryParser::FLAG_WILDCARD |
1233 Xapian::QueryParser::FLAG_LOVEHATE;
1234 queryparser.set_stemmer(Xapian::Stem("english"));
1235 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
1236 Xapian::Database db = get_database("apitest_simpledata");
1237 queryparser.set_database(db);
1238 Xapian::Enquire enquire(db);
1240 Xapian::Query qobj = queryparser.parse_query("th*", flags);
1241 tout << qobj.get_description() << endl;
1242 enquire.set_query(qobj);
1243 Xapian::MSet mymset = enquire.get_mset(0, 10);
1244 // Check that 6 documents were returned.
1245 TEST_MSET_SIZE(mymset, 6);
1247 qobj = queryparser.parse_query("notindb* \"this\"", flags);
1248 tout << qobj.get_description() << endl;
1249 enquire.set_query(qobj);
1250 mymset = enquire.get_mset(0, 10);
1251 // Check that 6 documents were returned.
1252 TEST_MSET_SIZE(mymset, 6);
1254 qobj = queryparser.parse_query("+notindb* \"this\"", flags);
1255 tout << qobj.get_description() << endl;
1256 enquire.set_query(qobj);
1257 mymset = enquire.get_mset(0, 10);
1258 // Check that 0 documents were returned.
1259 TEST_MSET_SIZE(mymset, 0);
1261 return true;
1264 DEFINE_TESTCASE(qp_flag_bool_any_case1, !backend) {
1265 using Xapian::QueryParser;
1266 Xapian::QueryParser qp;
1267 Xapian::Query qobj;
1268 qobj = qp.parse_query("to and fro", QueryParser::FLAG_BOOLEAN | QueryParser::FLAG_BOOLEAN_ANY_CASE);
1269 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 AND fro@2))");
1270 qobj = qp.parse_query("to and fro", QueryParser::FLAG_BOOLEAN);
1271 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 OR and@2 OR fro@3))");
1272 // Regression test for bug in 0.9.4 and earlier.
1273 qobj = qp.parse_query("to And fro", QueryParser::FLAG_BOOLEAN | QueryParser::FLAG_BOOLEAN_ANY_CASE);
1274 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 AND fro@2))");
1275 qobj = qp.parse_query("to And fro", QueryParser::FLAG_BOOLEAN);
1276 TEST_STRINGS_EQUAL(qobj.get_description(), "Query((to@1 OR and@2 OR fro@3))");
1277 return true;
1280 static const test test_stop_queries[] = {
1281 { "test the queryparser", "(test@1 AND queryparser@3)" },
1282 // Regression test for bug in 0.9.6 and earlier. This would fail to
1283 // parse.
1284 { "test AND the AND queryparser", "((test@1 AND the@2) AND queryparser@3)" },
1285 // 0.9.6 and earlier ignored a stopword even if it was the only term.
1286 // More recent versions don't ever treat a single term as a stopword.
1287 { "the", "the@1" },
1288 // 1.2.2 and earlier ignored an all-stopword query with multiple terms,
1289 // which prevents 'to be or not to be' for being searchable unless the
1290 // user made it into a phrase query or prefixed all terms with '+'
1291 // (ticket#245).
1292 { "an the a", "(an@1 AND the@2 AND a@3)" },
1293 // Regression test for bug in initial version of the patch for the
1294 // "all-stopword" case.
1295 { "the AND a an", "(the@1 AND (a@2 AND an@3))" },
1296 { NULL, NULL }
1299 DEFINE_TESTCASE(qp_stopper1, !backend) {
1300 Xapian::QueryParser qp;
1301 static const char * const stopwords[] = { "a", "an", "the" };
1302 Xapian::SimpleStopper stop(stopwords, stopwords + 3);
1303 qp.set_stopper(&stop);
1304 qp.set_default_op(Xapian::Query::OP_AND);
1305 for (const test *p = test_stop_queries; p->query; ++p) {
1306 string expect, parsed;
1307 if (p->expect)
1308 expect = p->expect;
1309 else
1310 expect = "parse error";
1311 try {
1312 Xapian::Query qobj = qp.parse_query(p->query);
1313 parsed = qobj.get_description();
1314 expect = string("Query(") + expect + ')';
1315 } catch (const Xapian::QueryParserError &e) {
1316 parsed = e.get_msg();
1317 } catch (const Xapian::Error &e) {
1318 parsed = e.get_description();
1319 } catch (...) {
1320 parsed = "Unknown exception!";
1322 tout << "Query: " << p->query << '\n';
1323 TEST_STRINGS_EQUAL(parsed, expect);
1325 return true;
1328 static const test test_pure_not_queries[] = {
1329 { "NOT windows", "(<alldocuments> AND_NOT Zwindow@1)" },
1330 { "a AND (NOT b)", "(Za@1 AND (<alldocuments> AND_NOT Zb@2))" },
1331 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
1332 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
1333 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
1334 { NULL, NULL }
1337 DEFINE_TESTCASE(qp_flag_pure_not1, !backend) {
1338 using Xapian::QueryParser;
1339 Xapian::QueryParser qp;
1340 qp.set_stemmer(Xapian::Stem("english"));
1341 qp.set_stemming_strategy(QueryParser::STEM_SOME);
1342 for (const test *p = test_pure_not_queries; p->query; ++p) {
1343 string expect, parsed;
1344 if (p->expect)
1345 expect = p->expect;
1346 else
1347 expect = "parse error";
1348 try {
1349 Xapian::Query qobj = qp.parse_query(p->query,
1350 QueryParser::FLAG_BOOLEAN |
1351 QueryParser::FLAG_PURE_NOT);
1352 parsed = qobj.get_description();
1353 expect = string("Query(") + expect + ')';
1354 } catch (const Xapian::QueryParserError &e) {
1355 parsed = e.get_msg();
1356 } catch (const Xapian::Error &e) {
1357 parsed = e.get_description();
1358 } catch (...) {
1359 parsed = "Unknown exception!";
1361 tout << "Query: " << p->query << '\n';
1362 TEST_STRINGS_EQUAL(parsed, expect);
1364 return true;
1367 // Debatable if this is a regression test or a feature test, as it's not
1368 // obvious is this was a bug fix or a new feature. Either way, it first
1369 // appeared in Xapian 1.0.0.
1370 DEFINE_TESTCASE(qp_unstem_boolean_prefix, !backend) {
1371 Xapian::QueryParser qp;
1372 qp.add_boolean_prefix("test", "XTEST");
1373 Xapian::Query q = qp.parse_query("hello test:foo");
1374 TEST_STRINGS_EQUAL(q.get_description(), "Query((hello@1 FILTER XTESTfoo))");
1375 Xapian::TermIterator u = qp.unstem_begin("XTESTfoo");
1376 TEST(u != qp.unstem_end("XTESTfoo"));
1377 TEST_EQUAL(*u, "test:foo");
1378 ++u;
1379 TEST(u == qp.unstem_end("XTESTfoo"));
1380 return true;
1383 static const test test_value_range1_queries[] = {
1384 { "a..b", "VALUE_RANGE 1 a b" },
1385 { "$50..100", "VALUE_RANGE 1 $50 100" },
1386 { "$50..$99", "VALUE_RANGE 1 $50 $99" },
1387 { "$50..$100", "" }, // start > range
1388 { "02/03/1979..10/12/1980", "VALUE_RANGE 1 02/03/1979 10/12/1980" },
1389 { "a..b hello", "(hello@1 FILTER VALUE_RANGE 1 a b)" },
1390 { "hello a..b", "(hello@1 FILTER VALUE_RANGE 1 a b)" },
1391 { "hello a..b world", "((hello@1 OR world@2) FILTER VALUE_RANGE 1 a b)" },
1392 { "hello a..b test:foo", "(hello@1 FILTER (VALUE_RANGE 1 a b AND XTESTfoo))" },
1393 { "hello a..b test:foo test:bar", "(hello@1 FILTER (VALUE_RANGE 1 a b AND (XTESTfoo OR XTESTbar)))" },
1394 { "hello a..b c..d test:foo", "(hello@1 FILTER ((VALUE_RANGE 1 a b OR VALUE_RANGE 1 c d) AND XTESTfoo))" },
1395 { "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)))" },
1396 { "-5..7", "VALUE_RANGE 1 -5 7" },
1397 { "hello -5..7", "(hello@1 FILTER VALUE_RANGE 1 -5 7)" },
1398 { "-5..7 hello", "(hello@1 FILTER VALUE_RANGE 1 -5 7)" },
1399 { "\"time flies\" 09:00..12:30", "((time@1 PHRASE 2 flies@2) FILTER VALUE_RANGE 1 09:00 12:30)" },
1400 // Feature test for single-ended ranges (ticket#480):
1401 { "..b", "VALUE_LE 1 b" },
1402 { "a..", "VALUE_GE 1 a" },
1403 // Test for expanded set of characters allowed in range start:
1404 { "10:30+1300..11:00+1300", "VALUE_RANGE 1 10:30+1300 11:00+1300" },
1405 { NULL, NULL }
1408 // Simple test of ValueRangeProcessor class.
1409 DEFINE_TESTCASE(qp_value_range1, !backend) {
1410 Xapian::QueryParser qp;
1411 qp.add_boolean_prefix("test", "XTEST");
1412 Xapian::StringValueRangeProcessor vrp(1);
1413 qp.add_valuerangeprocessor(&vrp);
1414 for (const test *p = test_value_range1_queries; p->query; ++p) {
1415 string expect, parsed;
1416 if (p->expect)
1417 expect = p->expect;
1418 else
1419 expect = "parse error";
1420 try {
1421 Xapian::Query qobj = qp.parse_query(p->query);
1422 parsed = qobj.get_description();
1423 expect = string("Query(") + expect + ')';
1424 } catch (const Xapian::QueryParserError &e) {
1425 parsed = e.get_msg();
1426 } catch (const Xapian::Error &e) {
1427 parsed = e.get_description();
1428 } catch (...) {
1429 parsed = "Unknown exception!";
1431 tout << "Query: " << p->query << '\n';
1432 TEST_STRINGS_EQUAL(parsed, expect);
1434 return true;
1437 // Simple test of RangeProcessor class.
1438 DEFINE_TESTCASE(qp_range1, !backend) {
1439 Xapian::QueryParser qp;
1440 qp.add_boolean_prefix("test", "XTEST");
1441 Xapian::RangeProcessor rp(1);
1442 qp.add_rangeprocessor(&rp);
1443 for (const test *p = test_value_range1_queries; p->query; ++p) {
1444 string expect, parsed;
1445 if (p->expect)
1446 expect = p->expect;
1447 else
1448 expect = "parse error";
1449 try {
1450 Xapian::Query qobj = qp.parse_query(p->query);
1451 parsed = qobj.get_description();
1452 expect = string("Query(") + expect + ')';
1453 } catch (const Xapian::QueryParserError &e) {
1454 parsed = e.get_msg();
1455 } catch (const Xapian::Error &e) {
1456 parsed = e.get_description();
1457 } catch (...) {
1458 parsed = "Unknown exception!";
1460 tout << "Query: " << p->query << '\n';
1461 TEST_STRINGS_EQUAL(parsed, expect);
1463 return true;
1466 static const test test_value_range2_queries[] = {
1467 { "a..b", "VALUE_RANGE 3 a b" },
1468 { "1..12", "VALUE_RANGE 2 \\xa0 \\xae" },
1469 { "20070201..20070228", "VALUE_RANGE 1 20070201 20070228" },
1470 { "$10..20", "VALUE_RANGE 4 \\xad \\xb1" },
1471 { "$10..$20", "VALUE_RANGE 4 \\xad \\xb1" },
1472 // Feature test for single-ended ranges (ticket#480):
1473 { "$..20", "VALUE_LE 4 \\xb1" },
1474 { "..$20", "VALUE_LE 3 $20" }, // FIXME: probably should parse as $..20
1475 { "$10..", "VALUE_GE 4 \\xad" },
1476 { "12..42kg", "VALUE_RANGE 5 \\xae \\xb5@" },
1477 { "12kg..42kg", "VALUE_RANGE 5 \\xae \\xb5@" },
1478 { "12kg..42", "VALUE_RANGE 3 12kg 42" },
1479 { "10..$20", "" }, // start > end
1480 { "1999-03-12..2020-12-30", "VALUE_RANGE 1 19990312 20201230" },
1481 { "1999/03/12..2020/12/30", "VALUE_RANGE 1 19990312 20201230" },
1482 { "1999.03.12..2020.12.30", "VALUE_RANGE 1 19990312 20201230" },
1483 // Feature test for single-ended ranges (ticket#480):
1484 { "..2020.12.30", "VALUE_LE 1 20201230" },
1485 { "1999.03.12..", "VALUE_GE 1 19990312" },
1486 { "12/03/99..12/04/01", "VALUE_RANGE 1 19990312 20010412" },
1487 { "03-12-99..04-14-01", "VALUE_RANGE 1 19990312 20010414" },
1488 { "(test:a..test:b hello)", "(hello@1 FILTER VALUE_RANGE 3 test:a test:b)" },
1489 { "12..42kg 5..6kg 1..12", "0 * (VALUE_RANGE 2 \\xa0 \\xae AND (VALUE_RANGE 5 \\xae \\xb5@ OR VALUE_RANGE 5 \\xa9 \\xaa))" },
1490 // Check that a VRP which fails to match doesn't remove a prefix or suffix.
1491 // 1.0.13/1.1.1 and earlier got this wrong in some cases.
1492 { "$12a..13", "VALUE_RANGE 3 $12a 13" },
1493 { "$12..13b", "VALUE_RANGE 3 $12 13b" },
1494 { "$12..12kg", "VALUE_RANGE 3 $12 12kg" },
1495 { "12..b12kg", "VALUE_RANGE 3 12 b12kg" },
1496 { NULL, NULL }
1499 // Test chaining of ValueRangeProcessor classes.
1500 DEFINE_TESTCASE(qp_value_range2, !backend) {
1501 Xapian::QueryParser qp;
1502 qp.add_boolean_prefix("test", "XTEST");
1503 Xapian::DateValueRangeProcessor vrp_date(1);
1504 Xapian::NumberValueRangeProcessor vrp_num(2);
1505 Xapian::StringValueRangeProcessor vrp_str(3);
1506 Xapian::NumberValueRangeProcessor vrp_cash(4, "$");
1507 Xapian::NumberValueRangeProcessor vrp_weight(5, "kg", false);
1508 qp.add_valuerangeprocessor(&vrp_date);
1509 qp.add_valuerangeprocessor(&vrp_num);
1510 qp.add_valuerangeprocessor(&vrp_cash);
1511 qp.add_valuerangeprocessor(&vrp_weight);
1512 qp.add_valuerangeprocessor(&vrp_str);
1513 for (const test *p = test_value_range2_queries; p->query; ++p) {
1514 string expect, parsed;
1515 if (p->expect)
1516 expect = p->expect;
1517 else
1518 expect = "parse error";
1519 try {
1520 Xapian::Query qobj = qp.parse_query(p->query);
1521 parsed = qobj.get_description();
1522 expect = string("Query(") + expect + ')';
1523 } catch (const Xapian::QueryParserError &e) {
1524 parsed = e.get_msg();
1525 } catch (const Xapian::Error &e) {
1526 parsed = e.get_description();
1527 } catch (...) {
1528 parsed = "Unknown exception!";
1530 tout << "Query: " << p->query << '\n';
1531 TEST_STRINGS_EQUAL(parsed, expect);
1533 return true;
1536 // Test chaining of RangeProcessor classes.
1537 DEFINE_TESTCASE(qp_range2, !backend) {
1538 using Xapian::RP_REPEATED;
1539 using Xapian::RP_SUFFIX;
1540 Xapian::QueryParser qp;
1541 qp.add_boolean_prefix("test", "XTEST");
1542 Xapian::DateRangeProcessor rp_date(1);
1543 Xapian::NumberRangeProcessor rp_num(2);
1544 Xapian::RangeProcessor rp_str(3);
1545 Xapian::NumberRangeProcessor rp_cash(4, "$", RP_REPEATED);
1546 Xapian::NumberRangeProcessor rp_weight(5, "kg", RP_SUFFIX|RP_REPEATED);
1547 qp.add_rangeprocessor(&rp_date);
1548 qp.add_rangeprocessor(&rp_num);
1549 qp.add_rangeprocessor(&rp_cash);
1550 qp.add_rangeprocessor(&rp_weight);
1551 qp.add_rangeprocessor(&rp_str);
1552 for (const test *p = test_value_range2_queries; p->query; ++p) {
1553 string expect, parsed;
1554 if (p->expect)
1555 expect = p->expect;
1556 else
1557 expect = "parse error";
1558 try {
1559 Xapian::Query qobj = qp.parse_query(p->query);
1560 parsed = qobj.get_description();
1561 expect = string("Query(") + expect + ')';
1562 } catch (const Xapian::QueryParserError &e) {
1563 parsed = e.get_msg();
1564 } catch (const Xapian::Error &e) {
1565 parsed = e.get_description();
1566 } catch (...) {
1567 parsed = "Unknown exception!";
1569 tout << "Query: " << p->query << '\n';
1570 TEST_STRINGS_EQUAL(parsed, expect);
1572 return true;
1575 // Test NumberValueRangeProcessors with actual data.
1576 DEFINE_TESTCASE(qp_value_range3, writable) {
1577 Xapian::WritableDatabase db = get_writable_database();
1578 double low = -10;
1579 int steps = 60;
1580 double step = 0.5;
1582 for (int i = 0; i <= steps; ++i) {
1583 double v = low + i * step;
1584 Xapian::Document doc;
1585 doc.add_value(1, Xapian::sortable_serialise(v));
1586 db.add_document(doc);
1589 Xapian::NumberValueRangeProcessor vrp_num(1);
1590 Xapian::QueryParser qp;
1591 qp.add_valuerangeprocessor(&vrp_num);
1593 for (int j = 0; j <= steps; ++j) {
1594 double start = low + j * step;
1595 for (int k = 0; k <= steps; ++k) {
1596 double end = low + k * step;
1597 string query = str(start) + ".." + str(end);
1598 tout << "Query: " << query << '\n';
1599 Xapian::Query qobj = qp.parse_query(query);
1600 Xapian::Enquire enq(db);
1601 enq.set_query(qobj);
1602 Xapian::MSet mset = enq.get_mset(0, steps + 1);
1603 if (end < start) {
1604 TEST_EQUAL(mset.size(), 0);
1605 } else {
1606 TEST_EQUAL(mset.size(), 1u + (k - j));
1607 for (unsigned int m = 0; m != mset.size(); ++m) {
1608 double v = start + m * step;
1609 TEST_EQUAL(mset[m].get_document().get_value(1),
1610 Xapian::sortable_serialise(v));
1615 return true;
1618 // Test NumberRangeProcessors with actual data.
1619 DEFINE_TESTCASE(qp_range3, writable) {
1620 Xapian::WritableDatabase db = get_writable_database();
1621 double low = -10;
1622 int steps = 60;
1623 double step = 0.5;
1625 for (int i = 0; i <= steps; ++i) {
1626 double v = low + i * step;
1627 Xapian::Document doc;
1628 doc.add_value(1, Xapian::sortable_serialise(v));
1629 db.add_document(doc);
1632 Xapian::NumberRangeProcessor rp_num(1);
1633 Xapian::QueryParser qp;
1634 qp.add_rangeprocessor(&rp_num);
1636 for (int j = 0; j <= steps; ++j) {
1637 double start = low + j * step;
1638 for (int k = 0; k <= steps; ++k) {
1639 double end = low + k * step;
1640 string query = str(start) + ".." + str(end);
1641 tout << "Query: " << query << '\n';
1642 Xapian::Query qobj = qp.parse_query(query);
1643 Xapian::Enquire enq(db);
1644 enq.set_query(qobj);
1645 Xapian::MSet mset = enq.get_mset(0, steps + 1);
1646 if (end < start) {
1647 TEST_EQUAL(mset.size(), 0);
1648 } else {
1649 TEST_EQUAL(mset.size(), 1u + (k - j));
1650 for (unsigned int m = 0; m != mset.size(); ++m) {
1651 double v = start + m * step;
1652 TEST_EQUAL(mset[m].get_document().get_value(1),
1653 Xapian::sortable_serialise(v));
1658 return true;
1661 static const test test_value_range4_queries[] = {
1662 { "id:19254@foo..example.com", "0 * Q19254@foo..example.com" },
1663 { "hello:world", "0 * XHELLOworld" },
1664 { "hello:mum..world", "VALUE_RANGE 1 mum world" },
1665 { NULL, NULL }
1668 /** Test a boolean filter which happens to contain "..".
1670 * Regression test for bug fixed in 1.2.3.
1672 * Also test that the same prefix can be set for a valuerange and filter.
1674 DEFINE_TESTCASE(qp_value_range4, !backend) {
1675 Xapian::QueryParser qp;
1676 qp.add_boolean_prefix("id", "Q");
1677 qp.add_boolean_prefix("hello", "XHELLO");
1678 Xapian::StringValueRangeProcessor vrp_str(1, "hello:");
1679 qp.add_valuerangeprocessor(&vrp_str);
1680 for (const test *p = test_value_range4_queries; p->query; ++p) {
1681 string expect, parsed;
1682 if (p->expect)
1683 expect = p->expect;
1684 else
1685 expect = "parse error";
1686 try {
1687 Xapian::Query qobj = qp.parse_query(p->query);
1688 parsed = qobj.get_description();
1689 expect = string("Query(") + expect + ')';
1690 } catch (const Xapian::QueryParserError &e) {
1691 parsed = e.get_msg();
1692 } catch (const Xapian::Error &e) {
1693 parsed = e.get_description();
1694 } catch (...) {
1695 parsed = "Unknown exception!";
1697 tout << "Query: " << p->query << '\n';
1698 TEST_STRINGS_EQUAL(parsed, expect);
1700 return true;
1703 /** Test a boolean filter which happens to contain "..".
1705 * Regression test for bug fixed in 1.2.3.
1707 * Also test that the same prefix can be set for a range and filter.
1709 DEFINE_TESTCASE(qp_range4, !backend) {
1710 Xapian::QueryParser qp;
1711 qp.add_boolean_prefix("id", "Q");
1712 qp.add_boolean_prefix("hello", "XHELLO");
1713 Xapian::RangeProcessor rp_str(1, "hello:");
1714 qp.add_rangeprocessor(&rp_str);
1715 for (const test *p = test_value_range4_queries; p->query; ++p) {
1716 string expect, parsed;
1717 if (p->expect)
1718 expect = p->expect;
1719 else
1720 expect = "parse error";
1721 try {
1722 Xapian::Query qobj = qp.parse_query(p->query);
1723 parsed = qobj.get_description();
1724 expect = string("Query(") + expect + ')';
1725 } catch (const Xapian::QueryParserError &e) {
1726 parsed = e.get_msg();
1727 } catch (const Xapian::Error &e) {
1728 parsed = e.get_description();
1729 } catch (...) {
1730 parsed = "Unknown exception!";
1732 tout << "Query: " << p->query << '\n';
1733 TEST_STRINGS_EQUAL(parsed, expect);
1735 return true;
1738 static const test test_unitrange1_queries[] = {
1739 { "financial report size:100K..1M", "((financial@1 OR report@2) FILTER VALUE_RANGE 1 \\xe0&@ \\xe04)" },
1740 { "size:1B..1G", "VALUE_RANGE 1 \\xa0 \\xe0\\x5c" },
1741 // Interpret this as size:10K..100K
1742 { "size:10..100K", "VALUE_RANGE 1 \\xd9 \\xe0&@" },
1743 // Feature test for single-ended ranges
1744 { "size:10K..", "VALUE_GE 1 \\xd9" },
1745 { "size:..2M", "VALUE_LE 1 \\xe08" },
1746 // Forbidden ranges
1747 { "size:10B..100", "Unknown range operation" },
1748 { "size:10..100", "Unknown range operation" },
1749 { "size:..100", "Unknown range operation" },
1750 { "size:10..", "Unknown range operation" },
1751 { NULL, NULL }
1754 // Simple Test of UnitRangeProcessor class.
1755 DEFINE_TESTCASE(qp_range5, !backend) {
1756 Xapian::QueryParser qp;
1757 Xapian::UnitRangeProcessor rp_size(1, "size:");
1758 qp.add_rangeprocessor(&rp_size);
1759 for (const test *p = test_unitrange1_queries; p->query; ++p) {
1760 string expect, parsed;
1761 if (p->expect)
1762 expect = p->expect;
1763 else
1764 expect = "parse error";
1765 try {
1766 Xapian::Query qobj = qp.parse_query(p->query);
1767 parsed = qobj.get_description();
1768 expect = string("Query(") + expect + ')';
1769 } catch (const Xapian::QueryParserError &e) {
1770 parsed = e.get_msg();
1771 } catch (const Xapian::Error &e) {
1772 parsed = e.get_description();
1773 } catch (...) {
1774 parsed = "Unknown exception!";
1776 tout << "Query: " << p->query << '\n';
1777 TEST_STRINGS_EQUAL(parsed, expect);
1779 return true;
1782 static const test test_value_daterange1_queries[] = {
1783 { "12/03/99..12/04/01", "VALUE_RANGE 1 19991203 20011204" },
1784 { "03-12-99..04-14-01", "VALUE_RANGE 1 19990312 20010414" },
1785 { "01/30/60..02/02/59", "VALUE_RANGE 1 19600130 20590202" },
1786 { "1999-03-12..2001-04-14", "VALUE_RANGE 1 19990312 20010414" },
1787 { "12/03/99..02", "Unknown range operation" },
1788 { "1999-03-12..2001", "Unknown range operation" },
1789 { NULL, NULL }
1792 // Test DateValueRangeProcessor
1793 DEFINE_TESTCASE(qp_value_daterange1, !backend) {
1794 Xapian::QueryParser qp;
1795 Xapian::DateValueRangeProcessor vrp_date(1, true, 1960);
1796 qp.add_valuerangeprocessor(&vrp_date);
1797 for (const test *p = test_value_daterange1_queries; p->query; ++p) {
1798 string expect, parsed;
1799 if (p->expect)
1800 expect = p->expect;
1801 else
1802 expect = "parse error";
1803 try {
1804 Xapian::Query qobj = qp.parse_query(p->query);
1805 parsed = qobj.get_description();
1806 expect = string("Query(") + expect + ')';
1807 } catch (const Xapian::QueryParserError &e) {
1808 parsed = e.get_msg();
1809 } catch (const Xapian::Error &e) {
1810 parsed = e.get_description();
1811 } catch (...) {
1812 parsed = "Unknown exception!";
1814 tout << "Query: " << p->query << '\n';
1815 TEST_STRINGS_EQUAL(parsed, expect);
1817 return true;
1820 // Test DateRangeProcessor
1821 DEFINE_TESTCASE(qp_daterange1, !backend) {
1822 Xapian::QueryParser qp;
1823 Xapian::DateRangeProcessor rp_date(1, Xapian::RP_DATE_PREFER_MDY, 1960);
1824 qp.add_rangeprocessor(&rp_date);
1825 for (const test *p = test_value_daterange1_queries; p->query; ++p) {
1826 string expect, parsed;
1827 if (p->expect)
1828 expect = p->expect;
1829 else
1830 expect = "parse error";
1831 try {
1832 Xapian::Query qobj = qp.parse_query(p->query);
1833 parsed = qobj.get_description();
1834 expect = string("Query(") + expect + ')';
1835 } catch (const Xapian::QueryParserError &e) {
1836 parsed = e.get_msg();
1837 } catch (const Xapian::Error &e) {
1838 parsed = e.get_description();
1839 } catch (...) {
1840 parsed = "Unknown exception!";
1842 tout << "Query: " << p->query << '\n';
1843 TEST_STRINGS_EQUAL(parsed, expect);
1845 return true;
1848 static const test test_value_daterange2_queries[] = {
1849 { "created:12/03/99..12/04/01", "VALUE_RANGE 1 19991203 20011204" },
1850 { "modified:03-12-99..04-14-01", "VALUE_RANGE 2 19990312 20010414" },
1851 { "accessed:01/30/70..02/02/69", "VALUE_RANGE 3 19700130 20690202" },
1852 // In <=1.2.12, and in 1.3.0, this gave "Unknown range operation":
1853 { "deleted:12/03/99..12/04/01", "VALUE_RANGE 4 19990312 20010412" },
1854 { "1999-03-12..2001-04-14", "Unknown range operation" },
1855 { "12/03/99..created:12/04/01", "Unknown range operation" },
1856 { "12/03/99created:..12/04/01", "Unknown range operation" },
1857 { "12/03/99..12/04/01created:", "Unknown range operation" },
1858 { "12/03/99..02", "Unknown range operation" },
1859 { "1999-03-12..2001", "Unknown range operation" },
1860 { NULL, NULL }
1863 // Feature test DateValueRangeProcessor with prefixes (added in 1.1.2).
1864 DEFINE_TESTCASE(qp_value_daterange2, !backend) {
1865 Xapian::QueryParser qp;
1866 Xapian::DateValueRangeProcessor vrp_cdate(1, "created:", true, true, 1970);
1867 Xapian::DateValueRangeProcessor vrp_mdate(2, "modified:", true, true, 1970);
1868 Xapian::DateValueRangeProcessor vrp_adate(3, "accessed:", true, true, 1970);
1869 // Regression test - here a const char * was taken as a bool rather than a
1870 // std::string when resolving the overloaded forms. Fixed in 1.2.13 and
1871 // 1.3.1.
1872 Xapian::DateValueRangeProcessor vrp_ddate(4, "deleted:");
1873 qp.add_valuerangeprocessor(&vrp_cdate);
1874 qp.add_valuerangeprocessor(&vrp_mdate);
1875 qp.add_valuerangeprocessor(&vrp_adate);
1876 qp.add_valuerangeprocessor(&vrp_ddate);
1877 for (const test *p = test_value_daterange2_queries; p->query; ++p) {
1878 string expect, parsed;
1879 if (p->expect)
1880 expect = p->expect;
1881 else
1882 expect = "parse error";
1883 try {
1884 Xapian::Query qobj = qp.parse_query(p->query);
1885 parsed = qobj.get_description();
1886 expect = string("Query(") + expect + ')';
1887 } catch (const Xapian::QueryParserError &e) {
1888 parsed = e.get_msg();
1889 } catch (const Xapian::Error &e) {
1890 parsed = e.get_description();
1891 } catch (...) {
1892 parsed = "Unknown exception!";
1894 tout << "Query: " << p->query << '\n';
1895 TEST_STRINGS_EQUAL(parsed, expect);
1897 return true;
1900 // Feature test DateRangeProcessor with prefixes (added in 1.1.2).
1901 DEFINE_TESTCASE(qp_daterange2, !backend) {
1902 using Xapian::RP_DATE_PREFER_MDY;
1903 Xapian::QueryParser qp;
1904 Xapian::DateRangeProcessor rp_cdate(1, "created:", RP_DATE_PREFER_MDY, 1970);
1905 Xapian::DateRangeProcessor rp_mdate(2, "modified:", RP_DATE_PREFER_MDY, 1970);
1906 Xapian::DateRangeProcessor rp_adate(3, "accessed:", RP_DATE_PREFER_MDY, 1970);
1907 // Regression test - here a const char * was taken as a bool rather than a
1908 // std::string when resolving the overloaded forms. Fixed in 1.2.13 and
1909 // 1.3.1.
1910 Xapian::DateRangeProcessor rp_ddate(4, "deleted:");
1911 qp.add_rangeprocessor(&rp_cdate);
1912 qp.add_rangeprocessor(&rp_mdate);
1913 qp.add_rangeprocessor(&rp_adate);
1914 qp.add_rangeprocessor(&rp_ddate);
1915 for (const test *p = test_value_daterange2_queries; p->query; ++p) {
1916 string expect, parsed;
1917 if (p->expect)
1918 expect = p->expect;
1919 else
1920 expect = "parse error";
1921 try {
1922 Xapian::Query qobj = qp.parse_query(p->query);
1923 parsed = qobj.get_description();
1924 expect = string("Query(") + expect + ')';
1925 } catch (const Xapian::QueryParserError &e) {
1926 parsed = e.get_msg();
1927 } catch (const Xapian::Error &e) {
1928 parsed = e.get_description();
1929 } catch (...) {
1930 parsed = "Unknown exception!";
1932 tout << "Query: " << p->query << '\n';
1933 TEST_STRINGS_EQUAL(parsed, expect);
1935 return true;
1938 static const test test_value_stringrange1_queries[] = {
1939 { "tag:bar..foo", "VALUE_RANGE 1 bar foo" },
1940 { "bar..foo", "VALUE_RANGE 0 bar foo" },
1941 { NULL, NULL }
1944 // Feature test StringValueRangeProcessor with prefixes (added in 1.1.2).
1945 DEFINE_TESTCASE(qp_value_stringrange1, !backend) {
1946 Xapian::QueryParser qp;
1947 Xapian::StringValueRangeProcessor vrp_default(0);
1948 Xapian::StringValueRangeProcessor vrp_tag(1, "tag:", true);
1949 qp.add_valuerangeprocessor(&vrp_tag);
1950 qp.add_valuerangeprocessor(&vrp_default);
1951 for (const test *p = test_value_stringrange1_queries; p->query; ++p) {
1952 string expect, parsed;
1953 if (p->expect)
1954 expect = p->expect;
1955 else
1956 expect = "parse error";
1957 try {
1958 Xapian::Query qobj = qp.parse_query(p->query);
1959 parsed = qobj.get_description();
1960 expect = string("Query(") + expect + ')';
1961 } catch (const Xapian::QueryParserError &e) {
1962 parsed = e.get_msg();
1963 } catch (const Xapian::Error &e) {
1964 parsed = e.get_description();
1965 } catch (...) {
1966 parsed = "Unknown exception!";
1968 tout << "Query: " << p->query << '\n';
1969 TEST_STRINGS_EQUAL(parsed, expect);
1971 return true;
1974 // Feature test RangeProcessor with prefixes.
1975 DEFINE_TESTCASE(qp_stringrange1, !backend) {
1976 Xapian::QueryParser qp;
1977 Xapian::RangeProcessor rp_default(0);
1978 Xapian::RangeProcessor rp_tag(1, "tag:");
1979 qp.add_rangeprocessor(&rp_tag);
1980 qp.add_rangeprocessor(&rp_default);
1981 for (const test *p = test_value_stringrange1_queries; p->query; ++p) {
1982 string expect, parsed;
1983 if (p->expect)
1984 expect = p->expect;
1985 else
1986 expect = "parse error";
1987 try {
1988 Xapian::Query qobj = qp.parse_query(p->query);
1989 parsed = qobj.get_description();
1990 expect = string("Query(") + expect + ')';
1991 } catch (const Xapian::QueryParserError &e) {
1992 parsed = e.get_msg();
1993 } catch (const Xapian::Error &e) {
1994 parsed = e.get_description();
1995 } catch (...) {
1996 parsed = "Unknown exception!";
1998 tout << "Query: " << p->query << '\n';
1999 TEST_STRINGS_EQUAL(parsed, expect);
2001 return true;
2004 static const test test_value_customrange1_queries[] = {
2005 { "mars author:Asimov..Bradbury", "(mars@1 FILTER VALUE_RANGE 4 asimov bradbury)" },
2006 { NULL, NULL }
2009 struct AuthorValueRangeProcessor : public Xapian::ValueRangeProcessor {
2010 AuthorValueRangeProcessor() {}
2012 Xapian::valueno operator()(std::string &begin, std::string &end) {
2013 if (!startswith(begin, "author:"))
2014 return Xapian::BAD_VALUENO;
2015 begin.erase(0, 7);
2016 begin = Xapian::Unicode::tolower(begin);
2017 end = Xapian::Unicode::tolower(end);
2018 return 4;
2022 // Test custom ValueRangeProcessor subclass.
2023 DEFINE_TESTCASE(qp_value_customrange1, !backend) {
2024 Xapian::QueryParser qp;
2025 AuthorValueRangeProcessor vrp_author;
2026 qp.add_valuerangeprocessor(&vrp_author);
2027 for (const test *p = test_value_customrange1_queries; p->query; ++p) {
2028 string expect, parsed;
2029 if (p->expect)
2030 expect = p->expect;
2031 else
2032 expect = "parse error";
2033 try {
2034 Xapian::Query qobj = qp.parse_query(p->query);
2035 parsed = qobj.get_description();
2036 expect = string("Query(") + expect + ')';
2037 } catch (const Xapian::QueryParserError &e) {
2038 parsed = e.get_msg();
2039 } catch (const Xapian::Error &e) {
2040 parsed = e.get_description();
2041 } catch (...) {
2042 parsed = "Unknown exception!";
2044 tout << "Query: " << p->query << '\n';
2045 TEST_STRINGS_EQUAL(parsed, expect);
2047 return true;
2050 struct AuthorRangeProcessor : public Xapian::RangeProcessor {
2051 AuthorRangeProcessor() : Xapian::RangeProcessor(4, "author:") { }
2053 Xapian::Query operator()(const std::string& b, const std::string& e)
2055 string begin = Xapian::Unicode::tolower(b);
2056 string end = Xapian::Unicode::tolower(e);
2057 return Xapian::RangeProcessor::operator()(begin, end);
2061 // Test custom RangeProcessor subclass.
2062 DEFINE_TESTCASE(qp_customrange1, !backend) {
2063 Xapian::QueryParser qp;
2064 AuthorRangeProcessor rp_author;
2065 qp.add_rangeprocessor(&rp_author);
2066 for (const test *p = test_value_customrange1_queries; p->query; ++p) {
2067 string expect, parsed;
2068 if (p->expect)
2069 expect = p->expect;
2070 else
2071 expect = "parse error";
2072 try {
2073 Xapian::Query qobj = qp.parse_query(p->query);
2074 parsed = qobj.get_description();
2075 expect = string("Query(") + expect + ')';
2076 } catch (const Xapian::QueryParserError &e) {
2077 parsed = e.get_msg();
2078 } catch (const Xapian::Error &e) {
2079 parsed = e.get_description();
2080 } catch (...) {
2081 parsed = "Unknown exception!";
2083 tout << "Query: " << p->query << '\n';
2084 TEST_STRINGS_EQUAL(parsed, expect);
2086 return true;
2089 class TitleFieldProcessor : public Xapian::FieldProcessor {
2090 Xapian::Query operator()(const std::string & str) {
2091 if (str == "all")
2092 return Xapian::Query::MatchAll;
2093 return Xapian::Query("S" + str);
2097 class HostFieldProcessor : public Xapian::FieldProcessor {
2098 Xapian::Query operator()(const std::string & str) {
2099 if (str == "*")
2100 return Xapian::Query::MatchAll;
2101 string res = "H";
2102 for (string::const_iterator i = str.begin(); i != str.end(); ++i)
2103 res += C_tolower(*i);
2104 return Xapian::Query(res);
2108 static const test test_fieldproc1_queries[] = {
2109 { "title:test", "Stest" },
2110 { "title:all", "<alldocuments>" },
2111 { "host:Xapian.org", "0 * Hxapian.org" },
2112 { "host:*", "0 * <alldocuments>" },
2113 { "host:\"Space Station.Example.Org\"", "0 * Hspace station.example.org" },
2114 { NULL, NULL }
2117 // FieldProcessor test.
2118 DEFINE_TESTCASE(qp_fieldproc1, !backend) {
2119 Xapian::QueryParser qp;
2120 TitleFieldProcessor title_fproc;
2121 HostFieldProcessor host_fproc;
2122 qp.add_prefix("title", &title_fproc);
2123 qp.add_boolean_prefix("host", &host_fproc);
2124 for (const test *p = test_fieldproc1_queries; p->query; ++p) {
2125 string expect, parsed;
2126 if (p->expect)
2127 expect = p->expect;
2128 else
2129 expect = "parse error";
2130 try {
2131 Xapian::Query qobj = qp.parse_query(p->query);
2132 parsed = qobj.get_description();
2133 expect = string("Query(") + expect + ')';
2134 } catch (const Xapian::QueryParserError &e) {
2135 parsed = e.get_msg();
2136 } catch (const Xapian::Error &e) {
2137 parsed = e.get_description();
2138 } catch (...) {
2139 parsed = "Unknown exception!";
2141 tout << "Query: " << p->query << '\n';
2142 TEST_STRINGS_EQUAL(parsed, expect);
2144 return true;
2147 class DateRangeFieldProcessor : public Xapian::FieldProcessor {
2148 Xapian::Query operator()(const std::string & str) {
2149 // In reality, these would be built from the current date, but for
2150 // testing it is much simpler to fix the date.
2151 if (str == "today")
2152 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120725");
2153 if (str == "this week")
2154 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120723");
2155 if (str == "this month")
2156 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120701");
2157 if (str == "this year")
2158 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20120101");
2159 if (str == "this decade")
2160 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20100101");
2161 if (str == "this century")
2162 return Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "20000101");
2163 throw Xapian::QueryParserError("Didn't understand date specification '" + str + "'");
2167 static const test test_fieldproc2_queries[] = {
2168 { "date:\"this week\"", "VALUE_GE 1 20120723" },
2169 { "date:23/7/2012..25/7/2012", "VALUE_RANGE 1 20120723 20120725" },
2170 { NULL, NULL }
2173 // Test using FieldProcessor and ValueRangeProcessor together.
2174 DEFINE_TESTCASE(qp_fieldproc2, !backend) {
2175 Xapian::QueryParser qp;
2176 DateRangeFieldProcessor date_fproc;
2177 qp.add_boolean_prefix("date", &date_fproc);
2178 Xapian::DateValueRangeProcessor vrp_date(1, "date:");
2179 qp.add_valuerangeprocessor(&vrp_date);
2180 for (const test *p = test_fieldproc2_queries; p->query; ++p) {
2181 string expect, parsed;
2182 if (p->expect)
2183 expect = p->expect;
2184 else
2185 expect = "parse error";
2186 try {
2187 Xapian::Query qobj = qp.parse_query(p->query);
2188 parsed = qobj.get_description();
2189 expect = string("Query(") + expect + ')';
2190 } catch (const Xapian::QueryParserError &e) {
2191 parsed = e.get_msg();
2192 } catch (const Xapian::Error &e) {
2193 parsed = e.get_description();
2194 } catch (...) {
2195 parsed = "Unknown exception!";
2197 tout << "Query: " << p->query << '\n';
2198 TEST_STRINGS_EQUAL(parsed, expect);
2200 return true;
2203 // Test using FieldProcessor and RangeProcessor together.
2204 DEFINE_TESTCASE(qp_fieldproc3, !backend) {
2205 Xapian::QueryParser qp;
2206 DateRangeFieldProcessor date_fproc;
2207 qp.add_boolean_prefix("date", &date_fproc);
2208 Xapian::DateRangeProcessor rp_date(1, "date:");
2209 qp.add_rangeprocessor(&rp_date);
2210 for (const test *p = test_fieldproc2_queries; p->query; ++p) {
2211 string expect, parsed;
2212 if (p->expect)
2213 expect = p->expect;
2214 else
2215 expect = "parse error";
2216 try {
2217 Xapian::Query qobj = qp.parse_query(p->query);
2218 parsed = qobj.get_description();
2219 expect = string("Query(") + expect + ')';
2220 } catch (const Xapian::QueryParserError &e) {
2221 parsed = e.get_msg();
2222 } catch (const Xapian::Error &e) {
2223 parsed = e.get_description();
2224 } catch (...) {
2225 parsed = "Unknown exception!";
2227 tout << "Query: " << p->query << '\n';
2228 TEST_STRINGS_EQUAL(parsed, expect);
2230 return true;
2233 DEFINE_TESTCASE(qp_stoplist1, !backend) {
2234 Xapian::QueryParser qp;
2235 static const char * const stopwords[] = { "a", "an", "the" };
2236 Xapian::SimpleStopper stop(stopwords, stopwords + 3);
2237 qp.set_stopper(&stop);
2239 Xapian::TermIterator i;
2241 Xapian::Query query1 = qp.parse_query("some mice");
2242 i = qp.stoplist_begin();
2243 TEST(i == qp.stoplist_end());
2245 Xapian::Query query2 = qp.parse_query("the cat");
2246 i = qp.stoplist_begin();
2247 TEST(i != qp.stoplist_end());
2248 TEST_EQUAL(*i, "the");
2249 ++i;
2250 TEST(i == qp.stoplist_end());
2252 // Regression test - prior to Xapian 1.0.0 the stoplist wasn't being cleared
2253 // when a new query was parsed.
2254 Xapian::Query query3 = qp.parse_query("an aardvark");
2255 i = qp.stoplist_begin();
2256 TEST(i != qp.stoplist_end());
2257 TEST_EQUAL(*i, "an");
2258 ++i;
2259 TEST(i == qp.stoplist_end());
2261 return true;
2264 static const test test_mispelled_queries[] = {
2265 { "doucment search", "document search" },
2266 { "doucment seeacrh", "document search" },
2267 { "docment seeacrh test", "document search test" },
2268 { "\"paragahp pineapple\"", "\"paragraph pineapple\"" },
2269 { "\"paragahp pineapple\"", "\"paragraph pineapple\"" },
2270 { "test S.E.A.R.C.", "" },
2271 { "this AND that", "" },
2272 { "documento", "document" },
2273 { "documento-documento", "document-document" },
2274 { "documento-searcho", "document-search" },
2275 { "test saerch", "test search" },
2276 { "paragraf search", "paragraph search" },
2277 { NULL, NULL }
2280 // Test spelling correction in the QueryParser.
2281 DEFINE_TESTCASE(qp_spell1, spelling) {
2282 Xapian::WritableDatabase db = get_writable_database();
2284 Xapian::Document doc;
2285 doc.add_term("document", 6);
2286 doc.add_term("search", 7);
2287 doc.add_term("saerch", 1);
2288 doc.add_term("paragraph", 8);
2289 doc.add_term("paragraf", 2);
2290 db.add_document(doc);
2292 db.add_spelling("document");
2293 db.add_spelling("search");
2294 db.add_spelling("paragraph");
2295 db.add_spelling("band");
2297 Xapian::QueryParser qp;
2298 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2299 qp.set_database(db);
2301 for (const test *p = test_mispelled_queries; p->query; ++p) {
2302 Xapian::Query q;
2303 q = qp.parse_query(p->query,
2304 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2305 Xapian::QueryParser::FLAG_BOOLEAN);
2306 tout << "Query: " << p->query << endl;
2307 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2310 return true;
2313 // Test spelling correction in the QueryParser with multiple databases.
2314 DEFINE_TESTCASE(qp_spell2, spelling)
2316 Xapian::WritableDatabase db1 = get_writable_database();
2318 db1.add_spelling("document");
2319 db1.add_spelling("search");
2320 db1.commit();
2322 Xapian::WritableDatabase db2 = get_named_writable_database("qp_spell2a");
2324 db2.add_spelling("document");
2325 db2.add_spelling("paragraph");
2326 db2.add_spelling("band");
2328 Xapian::Database db;
2329 db.add_database(db1);
2330 db.add_database(db2);
2332 Xapian::QueryParser qp;
2333 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2334 qp.set_database(db);
2336 for (const test *p = test_mispelled_queries; p->query; ++p) {
2337 Xapian::Query q;
2338 q = qp.parse_query(p->query,
2339 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2340 Xapian::QueryParser::FLAG_BOOLEAN);
2341 tout << "Query: " << p->query << endl;
2342 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2345 return true;
2348 static const test test_mispelled_wildcard_queries[] = {
2349 { "doucment", "document" },
2350 { "doucment*", "" },
2351 { "doucment* seearch", "doucment* search" },
2352 { "doucment* search", "" },
2353 { NULL, NULL }
2356 // Test spelling correction in the QueryParser with wildcards.
2357 // Regression test for bug fixed in 1.1.3 and 1.0.17.
2358 DEFINE_TESTCASE(qp_spellwild1, spelling) {
2359 Xapian::WritableDatabase db = get_writable_database();
2361 db.add_spelling("document");
2362 db.add_spelling("search");
2363 db.add_spelling("paragraph");
2364 db.add_spelling("band");
2366 Xapian::QueryParser qp;
2367 qp.set_database(db);
2369 const test *p;
2370 for (p = test_mispelled_queries; p->query; ++p) {
2371 Xapian::Query q;
2372 q = qp.parse_query(p->query,
2373 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2374 Xapian::QueryParser::FLAG_BOOLEAN |
2375 Xapian::QueryParser::FLAG_WILDCARD);
2376 tout << "Query: " << p->query << endl;
2377 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2379 for (p = test_mispelled_wildcard_queries; p->query; ++p) {
2380 Xapian::Query q;
2381 q = qp.parse_query(p->query,
2382 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2383 Xapian::QueryParser::FLAG_BOOLEAN |
2384 Xapian::QueryParser::FLAG_WILDCARD);
2385 tout << "Query: " << p->query << endl;
2386 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2389 return true;
2392 static const test test_mispelled_partial_queries[] = {
2393 { "doucment", "" },
2394 { "doucment ", "document " },
2395 { "documen", "" },
2396 { "documen ", "document " },
2397 { "seearch documen", "search documen" },
2398 { "search documen", "" },
2399 { NULL, NULL }
2402 // Test spelling correction in the QueryParser with FLAG_PARTIAL.
2403 // Regression test for bug fixed in 1.1.3 and 1.0.17.
2404 DEFINE_TESTCASE(qp_spellpartial1, spelling) {
2405 Xapian::WritableDatabase db = get_writable_database();
2407 db.add_spelling("document");
2408 db.add_spelling("search");
2409 db.add_spelling("paragraph");
2410 db.add_spelling("band");
2412 Xapian::QueryParser qp;
2413 qp.set_database(db);
2415 for (const test *p = test_mispelled_partial_queries; p->query; ++p) {
2416 Xapian::Query q;
2417 q = qp.parse_query(p->query,
2418 Xapian::QueryParser::FLAG_SPELLING_CORRECTION |
2419 Xapian::QueryParser::FLAG_PARTIAL);
2420 tout << "Query: " << p->query << endl;
2421 TEST_STRINGS_EQUAL(qp.get_corrected_query_string(), p->expect);
2424 return true;
2427 static const test test_synonym_queries[] = {
2428 { "searching", "(Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1)" },
2429 { "search", "(Zsearch@1 SYNONYM find@1)" },
2430 { "Search", "(search@1 SYNONYM find@1)" },
2431 { "Searching", "searching@1" },
2432 { "searching OR terms", "((Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1) OR Zterm@2)" },
2433 { "search OR terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2434 { "search +terms", "(Zterm@2 AND_MAYBE (Zsearch@1 SYNONYM find@1))" },
2435 { "search -terms", "((Zsearch@1 SYNONYM find@1) AND_NOT Zterm@2)" },
2436 { "+search terms", "((Zsearch@1 SYNONYM find@1) AND_MAYBE Zterm@2)" },
2437 { "-search terms", "(Zterm@2 AND_NOT (Zsearch@1 SYNONYM find@1))" },
2438 { "search terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2439 // Shouldn't trigger synonyms:
2440 { "\"search terms\"", "(search@1 PHRASE 2 terms@2)" },
2441 // Check that setting FLAG_AUTO_SYNONYMS doesn't enable multi-word
2442 // synonyms. Regression test for bug fixed in 1.3.0 and 1.2.9.
2443 { "regression test", "(Zregress@1 OR Ztest@2)" },
2444 { NULL, NULL }
2447 // Test single term synonyms in the QueryParser.
2448 DEFINE_TESTCASE(qp_synonym1, spelling) {
2449 Xapian::WritableDatabase db = get_writable_database();
2451 db.add_synonym("Zsearch", "Zfind");
2452 db.add_synonym("Zsearch", "Zlocate");
2453 db.add_synonym("search", "find");
2454 db.add_synonym("Zseek", "Zsearch");
2455 db.add_synonym("regression test", "magic");
2457 db.commit();
2459 Xapian::QueryParser qp;
2460 qp.set_stemmer(Xapian::Stem("english"));
2461 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2462 qp.set_database(db);
2464 for (const test *p = test_synonym_queries; p->query; ++p) {
2465 string expect = "Query(";
2466 expect += p->expect;
2467 expect += ')';
2468 Xapian::Query q;
2469 q = qp.parse_query(p->query, qp.FLAG_AUTO_SYNONYMS|qp.FLAG_DEFAULT);
2470 tout << "Query: " << p->query << endl;
2471 TEST_STRINGS_EQUAL(q.get_description(), expect);
2474 return true;
2477 static const test test_multi_synonym_queries[] = {
2478 { "sun OR tan OR cream", "((Zsun@1 OR Ztan@2) OR Zcream@3)" },
2479 { "sun tan", "((Zsun@1 OR Ztan@2) SYNONYM bathe@1)" },
2480 { "sun tan cream", "((Zsun@1 OR Ztan@2 OR Zcream@3) SYNONYM lotion@1)" },
2481 { "beach sun tan holiday", "(Zbeach@1 OR ((Zsun@2 OR Ztan@3) SYNONYM bathe@2) OR Zholiday@4)" },
2482 { "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))" },
2483 { "single", "(Zsingl@1 SYNONYM record@1)" },
2484 { NULL, NULL }
2487 // Test multi term synonyms in the QueryParser.
2488 DEFINE_TESTCASE(qp_synonym2, synonyms) {
2489 Xapian::WritableDatabase db = get_writable_database();
2491 db.add_synonym("sun tan cream", "lotion");
2492 db.add_synonym("sun tan", "bathe");
2493 db.add_synonym("single", "record");
2495 db.commit();
2497 Xapian::QueryParser qp;
2498 qp.set_stemmer(Xapian::Stem("english"));
2499 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2500 qp.set_database(db);
2502 for (const test *p = test_multi_synonym_queries; p->query; ++p) {
2503 string expect = "Query(";
2504 expect += p->expect;
2505 expect += ')';
2506 Xapian::Query q;
2507 q = qp.parse_query(p->query,
2508 Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS |
2509 Xapian::QueryParser::FLAG_DEFAULT);
2510 tout << "Query: " << p->query << endl;
2511 TEST_STRINGS_EQUAL(q.get_description(), expect);
2514 return true;
2517 static const test test_synonym_op_queries[] = {
2518 { "searching", "Zsearch@1" },
2519 { "~searching", "(Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1)" },
2520 { "~search", "(Zsearch@1 SYNONYM find@1)" },
2521 { "~Search", "(search@1 SYNONYM find@1)" },
2522 { "~Searching", "searching@1" },
2523 { "~searching OR terms", "((Zsearch@1 SYNONYM Zfind@1 SYNONYM Zlocate@1) OR Zterm@2)" },
2524 { "~search OR terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2525 { "~search +terms", "(Zterm@2 AND_MAYBE (Zsearch@1 SYNONYM find@1))" },
2526 { "~search -terms", "((Zsearch@1 SYNONYM find@1) AND_NOT Zterm@2)" },
2527 { "+~search terms", "((Zsearch@1 SYNONYM find@1) AND_MAYBE Zterm@2)" },
2528 { "-~search terms", "(Zterm@2 AND_NOT (Zsearch@1 SYNONYM find@1))" },
2529 { "~search terms", "((Zsearch@1 SYNONYM find@1) OR Zterm@2)" },
2530 { "~foo:search", "(ZXFOOsearch@1 SYNONYM prefixated@1)" },
2531 // FIXME: should look for multi-term synonym...
2532 { "~\"search terms\"", "(search@1 PHRASE 2 terms@2)" },
2533 { NULL, NULL }
2536 // Test the synonym operator in the QueryParser.
2537 DEFINE_TESTCASE(qp_synonym3, synonyms) {
2538 Xapian::WritableDatabase db = get_writable_database();
2540 db.add_synonym("Zsearch", "Zfind");
2541 db.add_synonym("Zsearch", "Zlocate");
2542 db.add_synonym("search", "find");
2543 db.add_synonym("Zseek", "Zsearch");
2544 db.add_synonym("ZXFOOsearch", "prefixated");
2546 db.commit();
2548 Xapian::QueryParser qp;
2549 qp.set_stemmer(Xapian::Stem("english"));
2550 qp.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2551 qp.set_database(db);
2552 qp.add_prefix("foo", "XFOO");
2554 for (const test *p = test_synonym_op_queries; p->query; ++p) {
2555 string expect = "Query(";
2556 expect += p->expect;
2557 expect += ')';
2558 Xapian::Query q;
2559 q = qp.parse_query(p->query,
2560 Xapian::QueryParser::FLAG_SYNONYM |
2561 Xapian::QueryParser::FLAG_BOOLEAN |
2562 Xapian::QueryParser::FLAG_LOVEHATE |
2563 Xapian::QueryParser::FLAG_PHRASE);
2564 tout << "Query: " << p->query << endl;
2565 TEST_STRINGS_EQUAL(q.get_description(), expect);
2568 return true;
2571 static const test test_stem_all_queries[] = {
2572 { "\"chemical engineers\"", "(chemic@1 PHRASE 2 engin@2)" },
2573 { "chemical NEAR engineers", "(chemic@1 NEAR 11 engin@2)" },
2574 { "chemical engineers", "(chemic@1 OR engin@2)" },
2575 { "title:(chemical engineers)", "(XTchemic@1 OR XTengin@2)" },
2576 { NULL, NULL }
2579 DEFINE_TESTCASE(qp_stem_all1, !backend) {
2580 Xapian::QueryParser qp;
2581 qp.set_stemmer(Xapian::Stem("english"));
2582 qp.set_stemming_strategy(qp.STEM_ALL);
2583 qp.add_prefix("title", "XT");
2584 for (const test *p = test_stem_all_queries; p->query; ++p) {
2585 string expect, parsed;
2586 if (p->expect)
2587 expect = p->expect;
2588 else
2589 expect = "parse error";
2590 try {
2591 Xapian::Query qobj = qp.parse_query(p->query);
2592 parsed = qobj.get_description();
2593 expect = string("Query(") + expect + ')';
2594 } catch (const Xapian::QueryParserError &e) {
2595 parsed = e.get_msg();
2596 } catch (const Xapian::Error &e) {
2597 parsed = e.get_description();
2598 } catch (...) {
2599 parsed = "Unknown exception!";
2601 tout << "Query: " << p->query << '\n';
2602 TEST_STRINGS_EQUAL(parsed, expect);
2604 return true;
2607 static const test test_stem_all_z_queries[] = {
2608 { "\"chemical engineers\"", "(Zchemic@1 PHRASE 2 Zengin@2)" },
2609 { "chemical NEAR engineers", "(Zchemic@1 NEAR 11 Zengin@2)" },
2610 { "chemical engineers", "(Zchemic@1 OR Zengin@2)" },
2611 { "title:(chemical engineers)", "(ZXTchemic@1 OR ZXTengin@2)" },
2612 { NULL, NULL }
2615 DEFINE_TESTCASE(qp_stem_all_z1, !backend) {
2616 Xapian::QueryParser qp;
2617 qp.set_stemmer(Xapian::Stem("english"));
2618 qp.set_stemming_strategy(qp.STEM_ALL_Z);
2619 qp.add_prefix("title", "XT");
2620 for (const test *p = test_stem_all_z_queries; p->query; ++p) {
2621 string expect, parsed;
2622 if (p->expect)
2623 expect = p->expect;
2624 else
2625 expect = "parse error";
2626 try {
2627 Xapian::Query qobj = qp.parse_query(p->query);
2628 parsed = qobj.get_description();
2629 expect = string("Query(") + expect + ')';
2630 } catch (const Xapian::QueryParserError &e) {
2631 parsed = e.get_msg();
2632 } catch (const Xapian::Error &e) {
2633 parsed = e.get_description();
2634 } catch (...) {
2635 parsed = "Unknown exception!";
2637 tout << "Query: " << p->query << '\n';
2638 TEST_STRINGS_EQUAL(parsed, expect);
2640 return true;
2643 static double
2644 time_query_parse(const Xapian::Database & db, const string & q,
2645 int repetitions, unsigned flags)
2647 Xapian::QueryParser qp;
2648 qp.set_database(db);
2649 CPUTimer timer;
2650 std::vector<Xapian::Query> qs;
2651 qs.reserve(repetitions);
2652 for (int i = 0; i != repetitions; ++i) {
2653 qs.push_back(qp.parse_query(q, flags));
2655 if (repetitions > 1) {
2656 Xapian::Query qc(Xapian::Query::OP_OR, qs.begin(), qs.end());
2658 return timer.get_time();
2661 static void
2662 qp_scale1_helper(const Xapian::Database &db, const string & q, unsigned n,
2663 unsigned flags)
2665 double time1;
2666 while (true) {
2667 time1 = time_query_parse(db, q, n, flags);
2668 if (time1 != 0.0) break;
2670 // The first test completed before the timer ticked at all, so increase
2671 // the number of repetitions and retry.
2672 unsigned n_new = n * 10;
2673 if (n_new < n)
2674 SKIP_TEST("Can't count enough repetitions to be able to time test");
2675 n = n_new;
2678 n /= 5;
2680 string q_n;
2681 q_n.reserve(q.size() * n);
2682 for (unsigned i = n; i != 0; --i) {
2683 q_n += q;
2686 // Time 5 repetitions so we average random variations a bit.
2687 double time2 = time_query_parse(db, q_n, 5, flags);
2688 tout << "small=" << time1 << "s, large=" << time2 << "s\n";
2690 // Allow a factor of 2.15 difference, to cover random variation and a
2691 // native time interval which isn't an exact multiple of 1/CLK_TCK.
2692 TEST_REL(time2,<,time1 * 2.15);
2695 // Regression test: check that query parser doesn't scale very badly with the
2696 // size of the query.
2697 DEFINE_TESTCASE(qp_scale1, synonyms) {
2698 Xapian::WritableDatabase db = get_writable_database();
2700 db.add_synonym("foo", "bar");
2701 db.commit();
2703 string q1("foo ");
2704 string q1b("baz ");
2705 const unsigned repetitions = 5000;
2707 // A long multiword synonym.
2708 string syn;
2709 for (int j = 60; j != 0; --j) {
2710 syn += q1;
2712 syn.resize(syn.size() - 1);
2714 unsigned synflags = Xapian::QueryParser::FLAG_DEFAULT |
2715 Xapian::QueryParser::FLAG_SYNONYM |
2716 Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS;
2718 // First, we test a simple query.
2719 qp_scale1_helper(db, q1, repetitions, Xapian::QueryParser::FLAG_DEFAULT);
2721 // If synonyms are enabled, a different code-path is followed.
2722 // Test a query which has no synonyms.
2723 qp_scale1_helper(db, q1b, repetitions, synflags);
2725 // Test a query which has short synonyms.
2726 qp_scale1_helper(db, q1, repetitions, synflags);
2728 // Add a synonym for the whole query, to test that code path.
2729 db.add_synonym(syn, "bar");
2730 db.commit();
2732 qp_scale1_helper(db, q1, repetitions, synflags);
2734 return true;
2737 static const test test_near_queries[] = {
2738 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
2739 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
2740 // FIXME: these give NEAR 2
2741 // { "foo -baz bar", "((foo@1 NEAR 11 bar@3) AND_NOT Zbaz@2)" },
2742 // { "one +two three", "(Ztwo@2 AND_MAYBE (one@1 NEAR 11 three@3))" },
2743 { "foo bar", "(foo@1 NEAR 11 bar@2)" },
2744 { "foo bar baz", "(foo@1 NEAR 12 bar@2 NEAR 12 baz@3)" },
2745 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
2746 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
2747 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
2748 { "author:orwell animal farm", "(Aorwell@1 NEAR 12 animal@2 NEAR 12 farm@3)" },
2749 { "author:Orwell Animal Farm", "(Aorwell@1 NEAR 12 animal@2 NEAR 12 farm@3)" },
2750 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
2751 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
2752 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2753 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2754 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
2755 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
2756 { "one AND two", "(Zone@1 AND Ztwo@2)" },
2757 { "NOT windows", "Syntax: <expression> NOT <expression>" },
2758 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
2759 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
2760 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
2761 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
2762 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
2763 { "OR foo", "Syntax: <expression> OR <expression>" },
2764 { "XOR", "Syntax: <expression> XOR <expression>" },
2765 { "hard\xa0space", "(hard@1 NEAR 11 space@2)" },
2766 { NULL, NULL }
2769 DEFINE_TESTCASE(qp_near1, !backend) {
2770 Xapian::QueryParser queryparser;
2771 queryparser.set_stemmer(Xapian::Stem("english"));
2772 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2773 queryparser.add_prefix("author", "A");
2774 queryparser.add_prefix("writer", "A");
2775 queryparser.add_prefix("title", "XT");
2776 queryparser.add_prefix("subject", "XT");
2777 queryparser.add_prefix("authortitle", "A");
2778 queryparser.add_prefix("authortitle", "XT");
2779 queryparser.add_boolean_prefix("site", "H");
2780 queryparser.add_boolean_prefix("site2", "J");
2781 queryparser.add_boolean_prefix("multisite", "H");
2782 queryparser.add_boolean_prefix("multisite", "J");
2783 queryparser.add_boolean_prefix("category", "XCAT", false);
2784 queryparser.add_boolean_prefix("dogegory", "XDOG", false);
2785 queryparser.set_default_op(Xapian::Query::OP_NEAR);
2786 for (const test *p = test_near_queries; p->query; ++p) {
2787 string expect, parsed;
2788 if (p->expect)
2789 expect = p->expect;
2790 else
2791 expect = "parse error";
2792 try {
2793 Xapian::Query qobj = queryparser.parse_query(p->query);
2794 parsed = qobj.get_description();
2795 expect = string("Query(") + expect + ')';
2796 } catch (const Xapian::QueryParserError &e) {
2797 parsed = e.get_msg();
2798 } catch (const Xapian::Error &e) {
2799 parsed = e.get_description();
2800 } catch (...) {
2801 parsed = "Unknown exception!";
2803 tout << "Query: " << p->query << '\n';
2804 TEST_STRINGS_EQUAL(parsed, expect);
2806 return true;
2809 static const test test_phrase_queries[] = {
2810 { "simple-example", "(simple@1 PHRASE 2 example@2)" },
2811 { "stock -cooking", "(Zstock@1 AND_NOT Zcook@2)" },
2812 // FIXME: these give PHRASE 2
2813 // { "foo -baz bar", "((foo@1 PHRASE 11 bar@3) AND_NOT Zbaz@2)" },
2814 // { "one +two three", "(Ztwo@2 AND_MAYBE (one@1 PHRASE 11 three@3))" },
2815 { "foo bar", "(foo@1 PHRASE 11 bar@2)" },
2816 { "foo bar baz", "(foo@1 PHRASE 12 bar@2 PHRASE 12 baz@3)" },
2817 { "gtk+ -gnome", "(Zgtk+@1 AND_NOT Zgnome@2)" },
2818 { "c++ -d--", "(Zc++@1 AND_NOT Zd@2)" },
2819 { "\"c++ library\"", "(c++@1 PHRASE 2 library@2)" },
2820 { "author:orwell animal farm", "(Aorwell@1 PHRASE 12 animal@2 PHRASE 12 farm@3)" },
2821 { "author:Orwell Animal Farm", "(Aorwell@1 PHRASE 12 animal@2 PHRASE 12 farm@3)" },
2822 { "beer NOT \"orange juice\"", "(Zbeer@1 AND_NOT (orange@2 PHRASE 2 juice@3))" },
2823 { "beer AND NOT lager", "(Zbeer@1 AND_NOT Zlager@2)" },
2824 { "A OR B NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2825 { "A OR B AND NOT C", "(a@1 OR (b@2 AND_NOT c@3))" },
2826 { "A OR B XOR C", "(a@1 OR (b@2 XOR c@3))" },
2827 { "A XOR B NOT C", "(a@1 XOR (b@2 AND_NOT c@3))" },
2828 { "one AND two", "(Zone@1 AND Ztwo@2)" },
2829 { "NOT windows", "Syntax: <expression> NOT <expression>" },
2830 { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" },
2831 { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" },
2832 { "gordian NOT", "Syntax: <expression> NOT <expression>" },
2833 { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" },
2834 { "foo OR (something AND)", "Syntax: <expression> AND <expression>" },
2835 { "OR foo", "Syntax: <expression> OR <expression>" },
2836 { "XOR", "Syntax: <expression> XOR <expression>" },
2837 { "hard\xa0space", "(hard@1 PHRASE 11 space@2)" },
2838 // FIXME: this isn't what we want, but fixing phrase to work with
2839 // subqueries first might be the best approach.
2840 // FIXME: this isn't currently reimplemented:
2841 // { "(one AND two) three", "((Zone@1 PHRASE 11 Zthree@3) AND (Ztwo@2 PHRASE 11 Zthree@3))" },
2842 { NULL, NULL }
2845 DEFINE_TESTCASE(qp_phrase1, !backend) {
2846 Xapian::QueryParser queryparser;
2847 queryparser.set_stemmer(Xapian::Stem("english"));
2848 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
2849 queryparser.add_prefix("author", "A");
2850 queryparser.add_prefix("writer", "A");
2851 queryparser.add_prefix("title", "XT");
2852 queryparser.add_prefix("subject", "XT");
2853 queryparser.add_prefix("authortitle", "A");
2854 queryparser.add_prefix("authortitle", "XT");
2855 queryparser.add_boolean_prefix("site", "H");
2856 queryparser.add_boolean_prefix("site2", "J");
2857 queryparser.add_boolean_prefix("multisite", "H");
2858 queryparser.add_boolean_prefix("multisite", "J");
2859 queryparser.add_boolean_prefix("category", "XCAT", false);
2860 queryparser.add_boolean_prefix("dogegory", "XDOG", false);
2861 queryparser.set_default_op(Xapian::Query::OP_PHRASE);
2862 for (const test *p = test_phrase_queries; p->query; ++p) {
2863 string expect, parsed;
2864 if (p->expect)
2865 expect = p->expect;
2866 else
2867 expect = "parse error";
2868 try {
2869 Xapian::Query qobj = queryparser.parse_query(p->query);
2870 parsed = qobj.get_description();
2871 expect = string("Query(") + expect + ')';
2872 } catch (const Xapian::QueryParserError &e) {
2873 parsed = e.get_msg();
2874 } catch (const Xapian::Error &e) {
2875 parsed = e.get_description();
2876 } catch (...) {
2877 parsed = "Unknown exception!";
2879 tout << "Query: " << p->query << '\n';
2880 TEST_STRINGS_EQUAL(parsed, expect);
2882 return true;
2885 static const test test_stopword_group_or_queries[] = {
2886 { "this is a test", "test@4" },
2887 { "test*", "(SYNONYM WILDCARD OR test)" },
2888 { "a test*", "(SYNONYM WILDCARD OR test)" },
2889 { "is a test*", "(SYNONYM WILDCARD OR test)" },
2890 { "this is a test*", "(SYNONYM WILDCARD OR test)" },
2891 { "this is a us* test*", "((SYNONYM WILDCARD OR us) OR (SYNONYM WILDCARD OR test))" },
2892 { "this is a user test*", "(user@4 OR (SYNONYM WILDCARD OR test))" },
2893 { NULL, NULL }
2896 static const test test_stopword_group_and_queries[] = {
2897 { "this is a test", "test@4" },
2898 { "test*", "(SYNONYM WILDCARD OR test)" },
2899 { "a test*", "(SYNONYM WILDCARD OR test)" },
2900 // Two stopwords + one wildcard failed in 1.0.16
2901 { "is a test*", "(SYNONYM WILDCARD OR test)" },
2902 // Three stopwords + one wildcard failed in 1.0.16
2903 { "this is a test*", "(SYNONYM WILDCARD OR test)" },
2904 // Three stopwords + two wildcards failed in 1.0.16
2905 { "this is a us* test*", "((SYNONYM WILDCARD OR us) AND (SYNONYM WILDCARD OR test))" },
2906 { "this is a user test*", "(user@4 AND (SYNONYM WILDCARD OR test))" },
2907 { NULL, NULL }
2910 // Regression test for bug fixed in 1.0.17 and 1.1.3.
2911 DEFINE_TESTCASE(qp_stopword_group1, writable) {
2912 Xapian::WritableDatabase db = get_writable_database();
2913 Xapian::Document doc;
2914 doc.add_term("test");
2915 doc.add_term("tester");
2916 doc.add_term("testable");
2917 doc.add_term("user");
2918 db.add_document(doc);
2920 Xapian::SimpleStopper stopper;
2921 stopper.add("this");
2922 stopper.add("is");
2923 stopper.add("a");
2925 Xapian::QueryParser qp;
2926 qp.set_stopper(&stopper);
2927 qp.set_database(db);
2929 // Process test cases with OP_OR first, then with OP_AND.
2930 qp.set_default_op(Xapian::Query::OP_OR);
2931 const test *p = test_stopword_group_or_queries;
2932 for (int i = 1; i <= 2; ++i) {
2933 for ( ; p->query; ++p) {
2934 string expect, parsed;
2935 if (p->expect)
2936 expect = p->expect;
2937 else
2938 expect = "parse error";
2939 try {
2940 Xapian::Query qobj = qp.parse_query(p->query, qp.FLAG_WILDCARD);
2941 parsed = qobj.get_description();
2942 expect = string("Query(") + expect + ')';
2943 } catch (const Xapian::QueryParserError &e) {
2944 parsed = e.get_msg();
2945 } catch (const Xapian::Error &e) {
2946 parsed = e.get_description();
2947 } catch (...) {
2948 parsed = "Unknown exception!";
2950 tout << "Query: " << p->query << '\n';
2951 TEST_STRINGS_EQUAL(parsed, expect);
2954 qp.set_default_op(Xapian::Query::OP_AND);
2955 p = test_stopword_group_and_queries;
2958 return true;
2961 /// Check that QueryParser::set_default_op() rejects inappropriate ops.
2962 DEFINE_TESTCASE(qp_default_op2, !backend) {
2963 Xapian::QueryParser qp;
2964 static const Xapian::Query::op ops[] = {
2965 Xapian::Query::OP_AND_NOT,
2966 Xapian::Query::OP_XOR,
2967 Xapian::Query::OP_AND_MAYBE,
2968 Xapian::Query::OP_FILTER,
2969 Xapian::Query::OP_VALUE_RANGE,
2970 Xapian::Query::OP_SCALE_WEIGHT,
2971 Xapian::Query::OP_VALUE_GE,
2972 Xapian::Query::OP_VALUE_LE
2974 const Xapian::Query::op * p;
2975 for (p = ops; p - ops != sizeof(ops) / sizeof(*ops); ++p) {
2976 tout << *p << endl;
2977 TEST_EXCEPTION(Xapian::InvalidArgumentError,
2978 qp.set_default_op(*p));
2979 TEST_EQUAL(qp.get_default_op(), Xapian::Query::OP_OR);
2981 return true;
2984 struct qp_default_op3_test {
2985 Xapian::Query::op op;
2986 const char *expect;
2989 /// Check that QueryParser::set_default_op() accepts appropriate ops.
2990 DEFINE_TESTCASE(qp_default_op3, !backend) {
2991 Xapian::QueryParser qp;
2992 static const qp_default_op3_test tests[] = {
2993 { Xapian::Query::OP_AND,
2994 "Query((a@1 AND b@2 AND c@3))" },
2995 { Xapian::Query::OP_OR,
2996 "Query((a@1 OR b@2 OR c@3))" },
2997 { Xapian::Query::OP_PHRASE,
2998 "Query((a@1 PHRASE 12 b@2 PHRASE 12 c@3))" },
2999 { Xapian::Query::OP_NEAR,
3000 "Query((a@1 NEAR 12 b@2 NEAR 12 c@3))" },
3001 { Xapian::Query::OP_ELITE_SET,
3002 "Query((a@1 ELITE_SET 10 b@2 ELITE_SET 10 c@3))" },
3003 { Xapian::Query::OP_SYNONYM,
3004 "Query((a@1 SYNONYM b@2 SYNONYM c@3))" },
3006 const qp_default_op3_test * p;
3007 for (p = tests; p - tests != sizeof(tests) / sizeof(*tests); ++p) {
3008 tout << p->op << endl;
3009 qp.set_default_op(p->op);
3010 // Check that get_default_op() returns what we just set.
3011 TEST_EQUAL(qp.get_default_op(), p->op);
3012 TEST_EQUAL(qp.parse_query("A B C").get_description(), p->expect);
3014 return true;
3017 /// Test that the default strategy is now STEM_SOME (as of 1.3.1).
3018 DEFINE_TESTCASE(qp_defaultstrategysome1, !backend) {
3019 Xapian::QueryParser qp;
3020 qp.set_stemmer(Xapian::Stem("en"));
3021 TEST_EQUAL(qp.parse_query("testing").get_description(), "Query(Ztest@1)");
3022 return true;
3025 /// Test STEM_SOME_FULL_POS.
3026 DEFINE_TESTCASE(qp_stemsomefullpos, !backend) {
3027 Xapian::QueryParser qp;
3028 qp.set_stemmer(Xapian::Stem("en"));
3029 qp.set_stemming_strategy(qp.STEM_SOME_FULL_POS);
3030 TEST_EQUAL(qp.parse_query("terms NEAR testing").get_description(), "Query((Zterm@1 NEAR 11 Ztest@2))");
3031 TEST_EQUAL(qp.parse_query("terms ADJ testing").get_description(), "Query((Zterm@1 PHRASE 11 Ztest@2))");
3032 return true;