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