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