Index: matcher/phrasepostlist.cc
===================================================================
--- matcher/phrasepostlist.cc	(revision 7520)
+++ matcher/phrasepostlist.cc	(working copy)
@@ -100,7 +100,29 @@
     return "(Near " + om_tostring(window) + " " + source->get_description() + ")";
 }
 
+struct PosListAndIndex {
+    PositionList * p;
+    Xapian::termcount index;
+    Xapian::termcount wdf;
+
+    PosListAndIndex(Xapian::termcount index_, Xapian::termcount wdf_)
+	: p(0), index(index_), wdf(wdf_) { }
+    PositionList * get_poslist(std::vector<PostList *> terms) {
+	if (!p) p = terms[index]->read_position_list();
+	return p;
+    }
+};
 
+/** Class providing an operator which returns true if a has a (strictly)
+ *  smaller number of postings than b.
+ */
+struct PosListAndIndexCmpLt {
+    /** Return true if and only if a has a strictly smaller wdf than b.
+    */
+    bool operator()(const PosListAndIndex &a, const PosListAndIndex &b) {
+	return a.wdf < b.wdf;
+    }
+};
 
 /** Check if terms form a phrase in the current doc
  */
@@ -108,29 +130,35 @@
 PhrasePostList::test_doc()
 {
     DEBUGCALL(MATCH, bool, "PhrasePostList::test_doc", "");
-    std::vector<PositionList *> plists;
+    std::vector<PosListAndIndex> plists;
 
     std::vector<PostList *>::iterator i;
     for (i = terms.begin(); i != terms.end(); i++) {
-	PositionList * p = (*i)->read_position_list();
-	// If p is NULL, the backend doesn't support positionlists
-	if (!p) return false;
-	p->index = i - terms.begin();
-	plists.push_back(p);
+	int idx = i - terms.begin();
+	plists.push_back(PosListAndIndex(idx, (*i)->get_wdf()));
     }
 
-    std::sort(plists.begin(), plists.end(), PositionListCmpLt());
+    if (plists.size() > 2)
+	std::sort(plists.begin(), plists.end(), PosListAndIndexCmpLt());
+
+    // We'll need to read the positionlists for at least two terms, so check
+    // the true positionlist length for the two terms with the lowest wdf and
+    // if necessary swap them so the true shorter one is first.
+    if (plists[0].get_poslist(terms)->get_size() <
+	plists[1].get_poslist(terms)->get_size()) {
+	swap(plists[0], plists[0]);
+    }
 
     Xapian::termpos pos;
     Xapian::termpos idx, min;
     do {
-	plists[0]->next();
-	if (plists[0]->at_end()) {
+	plists[0].get_poslist(terms)->next();
+	if (plists[0].get_poslist(terms)->at_end()) {
 	    DEBUGLINE(MATCH, "--MISS--");
 	    RETURN(false);
 	}
-	pos = plists[0]->get_position();
-	idx = plists[0]->index;
+	pos = plists[0].get_poslist(terms)->get_position();
+	idx = plists[0].index;
 	min = pos + plists.size() - idx;
 	if (min > window) min -= window; else min = 0;
     } while (!do_test(plists, 1, min, pos + window - idx));
@@ -139,12 +167,12 @@
 }
 
 bool
-PhrasePostList::do_test(std::vector<PositionList *> &plists, Xapian::termcount i,
+PhrasePostList::do_test(std::vector<PosListAndIndex> &plists, Xapian::termcount i,
 			Xapian::termcount min, Xapian::termcount max)
 {
     DEBUGCALL(MATCH, bool, "PhrasePostList::do_test", "[plists],  " << i << ", " << min << ", " << max);
     DEBUGLINE(MATCH, "docid = " << get_docid() << ", window = " << window);
-    Xapian::termpos idxi = plists[i]->index;
+    Xapian::termpos idxi = plists[i].index;
     DEBUGLINE(MATCH, "my idx in phrase is " << idxi);
 
     Xapian::termpos mymin = min + idxi;
@@ -153,23 +181,23 @@
     // FIXME: this is worst case O(n^2) where n = length of phrase
     // Can we do better?
     for (Xapian::termcount j = 0; j < i; j++) {
-	Xapian::termpos idxj = plists[j]->index;
+	Xapian::termpos idxj = plists[j].index;
 	if (idxj > idxi) {
-	    Xapian::termpos tmp = plists[j]->get_position() + idxj - idxi;
+	    Xapian::termpos tmp = plists[j].get_poslist(terms)->get_position() + idxj - idxi;
 	    DEBUGLINE(MATCH, "ABOVE " << tmp);
 	    if (tmp < mymax) mymax = tmp;
 	} else {
 	    Assert(idxi != idxj);
-	    Xapian::termpos tmp = plists[j]->get_position() + idxi - idxj;
+	    Xapian::termpos tmp = plists[j].get_poslist(terms)->get_position() + idxi - idxj;
 	    DEBUGLINE(MATCH, "BELOW " << tmp);
 	    if (tmp > mymin) mymin = tmp;
 	}
 	DEBUGLINE(MATCH, "min = " << mymin << " max = " << mymax);
     }
-    plists[i]->skip_to(mymin);
+    plists[i].get_poslist(terms)->skip_to(mymin);
 
-    while (!plists[i]->at_end()) {
-	Xapian::termpos pos = plists[i]->get_position();
+    while (!plists[i].get_poslist(terms)->at_end()) {
+	Xapian::termpos pos = plists[i].get_poslist(terms)->get_position();
 	DEBUGLINE(MATCH, " " << mymin << " " << pos << " " << mymax);
 	if (pos > mymax) RETURN(false);
 	if (i + 1 == plists.size()) RETURN(true);
@@ -181,7 +209,7 @@
 	    if (tmp > min) min = tmp;
 	}
 	if (do_test(plists, i + 1, min, max)) RETURN(true);
-	plists[i]->next();
+	plists[i].get_poslist(terms)->next();
     }
     RETURN(false);
 }
Index: matcher/phrasepostlist.h
===================================================================
--- matcher/phrasepostlist.h	(revision 7520)
+++ matcher/phrasepostlist.h	(working copy)
@@ -62,6 +62,8 @@
 	}
 };
 
+struct PosListAndIndex;
+
 /** A postlist comprising several postlists PHRASEd together.
  *
  *  This postlist returns a posting if and only if it is in all of the
@@ -74,8 +76,8 @@
         Xapian::termpos window;
 	std::vector<PostList *> terms;
 
-    	bool test_doc();
-        bool do_test(std::vector<PositionList *> &plists, Xapian::termcount i,
+	bool test_doc();
+        bool do_test(std::vector<PosListAndIndex> &plists, Xapian::termcount i,
 		     Xapian::termcount min, Xapian::termcount max);
     public:
 	std::string get_description() const;
