1 /* api_posdb.cc: tests which need a backend with positional information
2  *
3  * Copyright 1999,2000,2001 BrightStation PLC
4  * Copyright 2002 Ananova Ltd
5  * Copyright 2002,2003,2004,2005,2006,2007,2009 Olly Betts
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 
23 #include <config.h>
24 
25 #include "api_posdb.h"
26 
27 #include <string>
28 #include <vector>
29 
30 using namespace std;
31 
32 #include <xapian.h>
33 #include "testsuite.h"
34 #include "testutils.h"
35 
36 #include "apitest.h"
37 
38 /// Simple test of NEAR
DEFINE_TESTCASE(near1,positional)39 DEFINE_TESTCASE(near1, positional) {
40     Xapian::Database mydb(get_database("apitest_phrase"));
41     Xapian::Enquire enquire(mydb);
42     Xapian::Stem stemmer("english");
43     enquire.set_weighting_scheme(Xapian::BoolWeight());
44 
45     // make a query
46     vector<Xapian::Query> subqs;
47     Xapian::Query q;
48     subqs.push_back(Xapian::Query(stemmer("phrase")));
49     subqs.push_back(Xapian::Query(stemmer("fridge")));
50     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
51     enquire.set_query(q);
52 
53     // retrieve the top ten results
54     Xapian::MSet mymset = enquire.get_mset(0, 10);
55     mset_expect_order(mymset);
56 
57     subqs.clear();
58     subqs.push_back(Xapian::Query(stemmer("phrase")));
59     subqs.push_back(Xapian::Query(stemmer("near")));
60     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
61     enquire.set_query(q);
62 
63     // retrieve the top ten results
64     mymset = enquire.get_mset(0, 10);
65     mset_expect_order(mymset, 3);
66 
67     subqs.clear();
68     subqs.push_back(Xapian::Query(stemmer("phrase")));
69     subqs.push_back(Xapian::Query(stemmer("near")));
70     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 3);
71     enquire.set_query(q);
72 
73     // retrieve the top ten results
74     mymset = enquire.get_mset(0, 10);
75     mset_expect_order(mymset, 1, 3);
76 
77     subqs.clear();
78     subqs.push_back(Xapian::Query(stemmer("phrase")));
79     subqs.push_back(Xapian::Query(stemmer("near")));
80     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 5);
81     enquire.set_query(q);
82 
83     // retrieve the top ten results
84     mymset = enquire.get_mset(0, 10);
85     mset_expect_order(mymset, 1, 3);
86 
87     subqs.clear();
88     subqs.push_back(Xapian::Query(stemmer("phrase")));
89     subqs.push_back(Xapian::Query(stemmer("near")));
90     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 6);
91     enquire.set_query(q);
92 
93     // retrieve the top ten results
94     mymset = enquire.get_mset(0, 10);
95     mset_expect_order(mymset, 1, 2, 3);
96 
97     subqs.clear();
98     subqs.push_back(Xapian::Query(stemmer("leave")));
99     subqs.push_back(Xapian::Query(stemmer("fridge")));
100     subqs.push_back(Xapian::Query(stemmer("on")));
101     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 3);
102     enquire.set_query(q);
103 
104     // retrieve the top ten results
105     mymset = enquire.get_mset(0, 10);
106     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9);
107 
108     subqs.clear();
109     subqs.push_back(Xapian::Query(stemmer("leave")));
110     subqs.push_back(Xapian::Query(stemmer("fridge")));
111     subqs.push_back(Xapian::Query(stemmer("on")));
112     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 4);
113     enquire.set_query(q);
114 
115     // retrieve the top ten results
116     mymset = enquire.get_mset(0, 10);
117     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10);
118 
119     subqs.clear();
120     subqs.push_back(Xapian::Query(stemmer("leave")));
121     subqs.push_back(Xapian::Query(stemmer("fridge")));
122     subqs.push_back(Xapian::Query(stemmer("on")));
123     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 5);
124     enquire.set_query(q);
125 
126     // retrieve the top ten results
127     mymset = enquire.get_mset(0, 10);
128     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11);
129 
130     subqs.clear();
131     subqs.push_back(Xapian::Query(stemmer("leave")));
132     subqs.push_back(Xapian::Query(stemmer("fridge")));
133     subqs.push_back(Xapian::Query(stemmer("on")));
134     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 6);
135     enquire.set_query(q);
136 
137     // retrieve the top ten results
138     mymset = enquire.get_mset(0, 10);
139     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12);
140 
141     subqs.clear();
142     subqs.push_back(Xapian::Query(stemmer("leave")));
143     subqs.push_back(Xapian::Query(stemmer("fridge")));
144     subqs.push_back(Xapian::Query(stemmer("on")));
145     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 7);
146     enquire.set_query(q);
147 
148     // retrieve the top twenty results
149     mymset = enquire.get_mset(0, 20);
150     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
151 
152     subqs.clear();
153     subqs.push_back(Xapian::Query(stemmer("leave")));
154     subqs.push_back(Xapian::Query(stemmer("fridge")));
155     subqs.push_back(Xapian::Query(stemmer("on")));
156     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 8);
157     enquire.set_query(q);
158 
159     // retrieve the top ten results
160     mymset = enquire.get_mset(0, 20);
161     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
162 
163     subqs.clear();
164     subqs.push_back(Xapian::Query(stemmer("leave")));
165     subqs.push_back(Xapian::Query(stemmer("fridge")));
166     subqs.push_back(Xapian::Query(stemmer("on")));
167     // test really large window size
168     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 999999999);
169     enquire.set_query(q);
170 
171     // retrieve the top ten results
172     mymset = enquire.get_mset(0, 20);
173     mset_expect_order(mymset, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
174 
175     return true;
176 }
177 
178 /// Test NEAR over operators
DEFINE_TESTCASE(near2,positional)179 DEFINE_TESTCASE(near2, positional) {
180     Xapian::Database mydb(get_database("apitest_phrase"));
181     Xapian::Enquire enquire(mydb);
182     Xapian::Stem stemmer("english");
183     enquire.set_weighting_scheme(Xapian::BoolWeight());
184 
185     // make a query
186     vector<Xapian::Query> subqs;
187     Xapian::Query q;
188     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
189 			    Xapian::Query(stemmer("phrase")),
190 			    Xapian::Query(stemmer("near"))));
191     subqs.push_back(Xapian::Query(stemmer("and")));
192     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
193     enquire.set_query(q);
194 
195     // retrieve the top ten results
196     Xapian::MSet mymset = enquire.get_mset(0, 10);
197     mset_expect_order(mymset, 1);
198 
199     subqs.clear();
200     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
201 			    Xapian::Query(stemmer("phrase")),
202 			    Xapian::Query(stemmer("near"))));
203     subqs.push_back(Xapian::Query(stemmer("operator")));
204     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
205     enquire.set_query(q);
206 
207     // retrieve the top ten results
208     mymset = enquire.get_mset(0, 10);
209     mset_expect_order(mymset, 2);
210 
211     subqs.clear();
212     subqs.push_back(Xapian::Query(stemmer("operator")));
213     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
214 			    Xapian::Query(stemmer("phrase")),
215 			    Xapian::Query(stemmer("near"))));
216     q = Xapian::Query(Xapian::Query::OP_NEAR, subqs.begin(), subqs.end(), 2);
217     enquire.set_query(q);
218 
219     // retrieve the top ten results
220     mymset = enquire.get_mset(0, 10);
221     mset_expect_order(mymset, 2);
222 
223     return true;
224 }
225 
226 /// Simple test of PHRASE
DEFINE_TESTCASE(phrase1,positional)227 DEFINE_TESTCASE(phrase1, positional) {
228     Xapian::Database mydb(get_database("apitest_phrase"));
229     Xapian::Enquire enquire(mydb);
230     Xapian::Stem stemmer("english");
231     enquire.set_weighting_scheme(Xapian::BoolWeight());
232 
233     // make a query
234     vector<Xapian::Query> subqs;
235     Xapian::Query q;
236     subqs.push_back(Xapian::Query(stemmer("phrase")));
237     subqs.push_back(Xapian::Query(stemmer("fridge")));
238     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
239     enquire.set_query(q);
240 
241     // retrieve the top ten results
242     Xapian::MSet mymset = enquire.get_mset(0, 10);
243     mset_expect_order(mymset);
244 
245     subqs.clear();
246     subqs.push_back(Xapian::Query(stemmer("phrase")));
247     subqs.push_back(Xapian::Query(stemmer("near")));
248     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
249     enquire.set_query(q);
250 
251     // retrieve the top ten results
252     mymset = enquire.get_mset(0, 10);
253     mset_expect_order(mymset);
254 
255     subqs.clear();
256     subqs.push_back(Xapian::Query(stemmer("phrase")));
257     subqs.push_back(Xapian::Query(stemmer("near")));
258     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 3);
259     enquire.set_query(q);
260 
261     // retrieve the top ten results
262     mymset = enquire.get_mset(0, 10);
263     mset_expect_order(mymset, 1);
264 
265     subqs.clear();
266     subqs.push_back(Xapian::Query(stemmer("phrase")));
267     subqs.push_back(Xapian::Query(stemmer("near")));
268     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 5);
269     enquire.set_query(q);
270 
271     // retrieve the top ten results
272     mymset = enquire.get_mset(0, 10);
273     mset_expect_order(mymset, 1);
274 
275     subqs.clear();
276     subqs.push_back(Xapian::Query(stemmer("phrase")));
277     subqs.push_back(Xapian::Query(stemmer("near")));
278     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 6);
279     enquire.set_query(q);
280 
281     // retrieve the top ten results
282     mymset = enquire.get_mset(0, 10);
283     mset_expect_order(mymset, 1, 2);
284 
285     subqs.clear();
286     subqs.push_back(Xapian::Query(stemmer("leave")));
287     subqs.push_back(Xapian::Query(stemmer("fridge")));
288     subqs.push_back(Xapian::Query(stemmer("on")));
289     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 3);
290     enquire.set_query(q);
291 
292     // retrieve the top ten results
293     mymset = enquire.get_mset(0, 10);
294     mset_expect_order(mymset, 4);
295 
296     subqs.clear();
297     subqs.push_back(Xapian::Query(stemmer("leave")));
298     subqs.push_back(Xapian::Query(stemmer("fridge")));
299     subqs.push_back(Xapian::Query(stemmer("on")));
300     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 4);
301     enquire.set_query(q);
302 
303     // retrieve the top ten results
304     mymset = enquire.get_mset(0, 10);
305     mset_expect_order(mymset, 4);
306 
307     subqs.clear();
308     subqs.push_back(Xapian::Query(stemmer("leave")));
309     subqs.push_back(Xapian::Query(stemmer("fridge")));
310     subqs.push_back(Xapian::Query(stemmer("on")));
311     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 5);
312     enquire.set_query(q);
313 
314     // retrieve the top ten results
315     mymset = enquire.get_mset(0, 10);
316     mset_expect_order(mymset, 4);
317 
318     subqs.clear();
319     subqs.push_back(Xapian::Query(stemmer("leave")));
320     subqs.push_back(Xapian::Query(stemmer("fridge")));
321     subqs.push_back(Xapian::Query(stemmer("on")));
322     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 6);
323     enquire.set_query(q);
324 
325     // retrieve the top ten results
326     mymset = enquire.get_mset(0, 10);
327     mset_expect_order(mymset, 4);
328 
329     subqs.clear();
330     subqs.push_back(Xapian::Query(stemmer("leave")));
331     subqs.push_back(Xapian::Query(stemmer("fridge")));
332     subqs.push_back(Xapian::Query(stemmer("on")));
333     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 7);
334     enquire.set_query(q);
335 
336     // retrieve the top twenty results
337     mymset = enquire.get_mset(0, 20);
338     mset_expect_order(mymset, 4);
339 
340     subqs.clear();
341     subqs.push_back(Xapian::Query(stemmer("leave")));
342     subqs.push_back(Xapian::Query(stemmer("fridge")));
343     subqs.push_back(Xapian::Query(stemmer("on")));
344     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 8);
345     enquire.set_query(q);
346 
347     // retrieve the top 20 results
348     mymset = enquire.get_mset(0, 20);
349     mset_expect_order(mymset, 4);
350 
351     // test really large window size
352     subqs.clear();
353     subqs.push_back(Xapian::Query(stemmer("leave")));
354     subqs.push_back(Xapian::Query(stemmer("fridge")));
355     subqs.push_back(Xapian::Query(stemmer("on")));
356     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 999999999);
357     enquire.set_query(q);
358 
359     // retrieve the top 20 results
360     mymset = enquire.get_mset(0, 20);
361     mset_expect_order(mymset, 4);
362 
363     // regression test (was matching doc 15, should fail)
364     subqs.clear();
365     subqs.push_back(Xapian::Query(stemmer("first")));
366     subqs.push_back(Xapian::Query(stemmer("second")));
367     subqs.push_back(Xapian::Query(stemmer("third")));
368     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 9);
369     enquire.set_query(q);
370 
371     // retrieve the top ten results
372     mymset = enquire.get_mset(0, 10);
373     mset_expect_order(mymset);
374 
375     // regression test (should match doc 15, make sure still does with fix)
376     subqs.clear();
377     subqs.push_back(Xapian::Query(stemmer("first")));
378     subqs.push_back(Xapian::Query(stemmer("second")));
379     subqs.push_back(Xapian::Query(stemmer("third")));
380     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 10);
381     enquire.set_query(q);
382 
383     // retrieve the top ten results
384     mymset = enquire.get_mset(0, 10);
385     mset_expect_order(mymset, 15);
386 
387     // regression test (phrase matching was getting order wrong when
388     // build_and_tree reordered vector of PostLists)
389     subqs.clear();
390     subqs.push_back(Xapian::Query(stemmer("milk")));
391     subqs.push_back(Xapian::Query(stemmer("rare")));
392     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
393     enquire.set_query(q);
394 
395     // retrieve the top ten results
396     mymset = enquire.get_mset(0, 10);
397     mset_expect_order(mymset, 16);
398 
399     // regression test (phrase matching was getting order wrong when
400     // build_and_tree reordered vector of PostLists)
401     subqs.clear();
402     subqs.push_back(Xapian::Query(stemmer("rare")));
403     subqs.push_back(Xapian::Query(stemmer("milk")));
404     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
405     enquire.set_query(q);
406 
407     // retrieve the top ten results
408     mymset = enquire.get_mset(0, 10);
409     mset_expect_order(mymset, 17);
410 
411     return true;
412 }
413 
414 /// Test PHRASE over operators
DEFINE_TESTCASE(phrase2,positional)415 DEFINE_TESTCASE(phrase2, positional) {
416     Xapian::Database mydb(get_database("apitest_phrase"));
417     Xapian::Enquire enquire(mydb);
418     Xapian::Stem stemmer("english");
419     enquire.set_weighting_scheme(Xapian::BoolWeight());
420 
421     // make a query
422     vector<Xapian::Query> subqs;
423     Xapian::Query q;
424     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
425 			    Xapian::Query(stemmer("phrase")),
426 			    Xapian::Query(stemmer("near"))));
427     subqs.push_back(Xapian::Query(stemmer("and")));
428     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
429     enquire.set_query(q);
430 
431     // retrieve the top ten results
432     Xapian::MSet mymset = enquire.get_mset(0, 10);
433     mset_expect_order(mymset);
434 
435     subqs.clear();
436     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
437 			    Xapian::Query(stemmer("phrase")),
438 			    Xapian::Query(stemmer("near"))));
439     subqs.push_back(Xapian::Query(stemmer("operator")));
440     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
441     enquire.set_query(q);
442 
443     // retrieve the top ten results
444     mymset = enquire.get_mset(0, 10);
445     mset_expect_order(mymset, 2);
446 
447     subqs.clear();
448     subqs.push_back(Xapian::Query(stemmer("operator")));
449     subqs.push_back(Xapian::Query(Xapian::Query::OP_AND,
450 			    Xapian::Query(stemmer("phrase")),
451 			    Xapian::Query(stemmer("near"))));
452     q = Xapian::Query(Xapian::Query::OP_PHRASE, subqs.begin(), subqs.end(), 2);
453     enquire.set_query(q);
454 
455     // retrieve the top ten results
456     mymset = enquire.get_mset(0, 10);
457     mset_expect_order(mymset);
458 
459     return true;
460 }
461 
462 /// Test getting position lists from databases
DEFINE_TESTCASE(poslist1,positional)463 DEFINE_TESTCASE(poslist1, positional) {
464     Xapian::Database mydb(get_database("apitest_poslist"));
465 
466     Xapian::Stem stemmer("english");
467     string term = stemmer("sponge");
468 
469     Xapian::PositionIterator pli = mydb.positionlist_begin(2, term);
470 
471     TEST(pli != mydb.positionlist_end(2, term));
472     TEST(*pli == 1);
473     pli++;
474     TEST(pli != mydb.positionlist_end(2, term));
475     TEST(*pli == 2);
476     pli++;
477     TEST(pli != mydb.positionlist_end(2, term));
478     TEST(*pli == 3);
479     pli++;
480     TEST(pli != mydb.positionlist_end(2, term));
481     TEST(*pli == 5);
482     pli++;
483     TEST(pli != mydb.positionlist_end(2, term));
484     TEST(*pli == 8);
485     pli++;
486     TEST(pli != mydb.positionlist_end(2, term));
487     TEST(*pli == 13);
488     pli++;
489     TEST(pli != mydb.positionlist_end(2, term));
490     TEST(*pli == 21);
491     pli++;
492     TEST(pli != mydb.positionlist_end(2, term));
493     TEST(*pli == 34);
494     pli++;
495     TEST(pli == mydb.positionlist_end(2, term));
496 
497     return true;
498 }
499 
DEFINE_TESTCASE(poslist2,positional && writable)500 DEFINE_TESTCASE(poslist2, positional && writable) {
501     Xapian::WritableDatabase db = get_writable_database();
502 
503     Xapian::Document doc;
504     doc.add_term("nopos");
505     Xapian::docid did = db.add_document(doc);
506 
507     // Check what happens when term doesn't exist - should give an empty list.
508     // Threw RangeError in Xapian < 1.1.0.
509     TEST_EQUAL(db.positionlist_begin(did, "nosuchterm"),
510 	       db.positionlist_end(did, "nosuchterm"));
511 
512     // Check what happens when the document doesn't even exist - should give
513     // an empty list.  Threw DocNotFoundError in Xapian < 1.1.0.
514     TEST_EQUAL(db.positionlist_begin(123, "nosuchterm"),
515 	       db.positionlist_end(123, "nosuchterm"));
516 
517     TEST_EQUAL(db.positionlist_begin(did, "nopos"),
518 	       db.positionlist_end(did, "nopos"));
519 
520     Xapian::Document doc2 = db.get_document(did);
521 
522     Xapian::TermIterator term = doc2.termlist_begin();
523 
524     {
525 	Xapian::PositionIterator i = term.positionlist_begin();
526 	TEST_EQUAL(i, term.positionlist_end());
527     }
528 
529     Xapian::Document doc3;
530     doc3.add_posting("hadpos", 1);
531     Xapian::docid did2 = db.add_document(doc3);
532 
533     Xapian::Document doc4 = db.get_document(did2);
534     doc4.remove_posting("hadpos", 1);
535     db.replace_document(did2, doc4);
536 
537     {
538 	Xapian::PositionIterator i = db.positionlist_begin(did2, "hadpos");
539 	TEST_EQUAL(i, db.positionlist_end(did2, "hadpos"));
540     }
541 
542     db.delete_document(did);
543     // Check what happens when the document doesn't exist (but once did).
544     TEST_EQUAL(db.positionlist_begin(did, "nosuchterm"),
545 	       db.positionlist_end(did, "nosuchterm"));
546 
547     return true;
548 }
549 
550 /// Test playing with a positionlist, testing skip_to in particular.
551 /// (used to be quartztest's test_positionlist1).
DEFINE_TESTCASE(poslist3,positional && writable)552 DEFINE_TESTCASE(poslist3, positional && writable) {
553     Xapian::WritableDatabase db = get_writable_database();
554 
555     Xapian::Document document;
556     document.add_posting("foo", 5);
557     document.add_posting("foo", 8);
558     document.add_posting("foo", 10);
559     document.add_posting("foo", 12);
560     db.add_document(document);
561 
562     Xapian::PositionIterator pl = db.positionlist_begin(1, "foo");
563     Xapian::PositionIterator pl_end = db.positionlist_end(1, "foo");
564 
565     TEST(pl != pl_end);
566     TEST_EQUAL(*pl, 5);
567     ++pl;
568     TEST(pl != pl_end);
569     TEST_EQUAL(*pl, 8);
570     ++pl;
571     TEST(pl != pl_end);
572     TEST_EQUAL(*pl, 10);
573     ++pl;
574     TEST(pl != pl_end);
575     TEST_EQUAL(*pl, 12);
576     ++pl;
577     TEST(pl == pl_end);
578 
579     pl = db.positionlist_begin(1, "foo");
580     pl.skip_to(5);
581     TEST(pl != pl_end);
582     TEST_EQUAL(*pl, 5);
583 
584     pl.skip_to(9);
585     TEST(pl != pl_end);
586     TEST_EQUAL(*pl, 10);
587 
588     ++pl;
589     TEST(pl != pl_end);
590     TEST_EQUAL(*pl, 12);
591 
592     pl.skip_to(12);
593     TEST(pl != pl_end);
594     TEST_EQUAL(*pl, 12);
595 
596     pl.skip_to(13);
597     TEST(pl == pl_end);
598 
599     return true;
600 }
601 
602 // Regression test - in 0.9.4 (and many previous versions) you couldn't get a
603 // PositionIterator from a TermIterator from Database::termlist_begin().
604 //
605 // Also test that positionlist_count() is implemented for this case, which it
606 // wasn't in 1.0.2 and earlier.
DEFINE_TESTCASE(positfromtermit1,positional)607 DEFINE_TESTCASE(positfromtermit1, positional) {
608     Xapian::Database db(get_database("apitest_phrase"));
609     Xapian::TermIterator t(db.termlist_begin(7));
610     TEST_NOT_EQUAL(t, db.termlist_end(7));
611     Xapian::PositionIterator p = t.positionlist_begin();
612     TEST_NOT_EQUAL(p, t.positionlist_end());
613 
614     try {
615 	TEST_EQUAL(t.positionlist_count(), 1);
616 	t.skip_to("on");
617 	TEST_NOT_EQUAL(t, db.termlist_end(7));
618 	TEST_EQUAL(t.positionlist_count(), 2);
619     } catch (const Xapian::UnimplementedError &) {
620 	SKIP_TEST("TermList::positionlist_count() not yet implemented for this backend");
621     }
622 
623     return true;
624 }
625