Index: omega.h
===================================================================
--- omega.h	(revision 7513)
+++ omega.h	(working copy)
@@ -59,6 +59,9 @@
 extern bool sort_numeric;
 extern Xapian::valueno sort_key;
 extern bool sort_ascending;
+extern bool sort_after;
+extern Xapian::Enquire::docid_order docid_order;
+
 extern Xapian::valueno collapse_key;
 extern bool collapse;
 
Index: query.cc
===================================================================
--- query.cc	(revision 7513)
+++ query.cc	(working copy)
@@ -345,101 +345,109 @@
 	}
     }
 
-    if (enquire && error_msg.empty()) {
-	enquire->set_cutoff(threshold);
+    if (!enquire || !error_msg.empty()) return;
 
-	// Temporary bodge to allow experimentation with Xapian::BiasFunctor
-	MCI i;
-	i = cgi_params.find("bias_weight");
+    enquire->set_cutoff(threshold);
+
+    // Temporary bodge to allow experimentation with Xapian::BiasFunctor
+    MCI i;
+    i = cgi_params.find("bias_weight");
+    if (i != cgi_params.end()) {
+	Xapian::weight bias_weight = atof(i->second.c_str());
+	int half_life = 2 * 24 * 60 * 60; // 2 days
+	i = cgi_params.find("bias_halflife");
 	if (i != cgi_params.end()) {
-	    Xapian::weight bias_weight = atof(i->second.c_str());
-	    int half_life = 2 * 24 * 60 * 60; // 2 days
-	    i = cgi_params.find("bias_halflife");
-	    if (i != cgi_params.end()) {
-		half_life = atoi(i->second.c_str());
-	    }
-	    enquire->set_bias(bias_weight, half_life);
+	    half_life = atoi(i->second.c_str());
 	}
-	if (sort_key != Xapian::valueno(-1)) {
+	enquire->set_bias(bias_weight, half_life);
+    }
+
+    if (sort_key != Xapian::valueno(-1)) {
+	if (sort_after) {
+	    enquire->set_sort_by_relevance_then_value(sort_key, sort_ascending);
+	} else {
 	    enquire->set_sort_by_value_then_relevance(sort_key, sort_ascending);
 	}
-	if (collapse) {
-	    enquire->set_collapse_key(collapse_key);
-	}
+    }
 
+    enquire->set_docid_order(docid_order);
+
+    if (collapse) {
+	enquire->set_collapse_key(collapse_key);
+    }
+
 #ifdef HAVE_GETTIMEOFDAY
-	struct timeval tv;
-	if (gettimeofday(&tv, 0) == 0) {
-	    sec = tv.tv_sec;
-	    usec = tv.tv_usec;
-	}
+    struct timeval tv;
+    if (gettimeofday(&tv, 0) == 0) {
+	sec = tv.tv_sec;
+	usec = tv.tv_usec;
+    }
 #elif defined(FTIME_RETURNS_VOID)
-	struct timeb tp;
-	ftime(&tp);
+    struct timeb tp;
+    ftime(&tp);
+    sec = tp.time;
+    usec = tp.millitm * 1000;
+#elif defined(HAVE_FTIME)
+    struct timeb tp;
+    if (ftime(&tp) == 0) {
 	sec = tp.time;
 	usec = tp.millitm * 1000;
-#elif defined(HAVE_FTIME)
-	struct timeb tp;
-	if (ftime(&tp) == 0) {
-	    sec = tp.time;
-	    usec = tp.millitm * 1000;
-	}
+    }
 #else
-	sec = time(NULL);
-	if (sec != (time_t)-1) usec = 0;
+    sec = time(NULL);
+    if (sec != (time_t)-1) usec = 0;
 #endif
-	if (!query.empty()) {
-	    enquire->set_query(query);
-	    // We could use the value of topdoc as first parameter, but we
-	    // need to know the first few items on the mset to fake a
-	    // relevance set for topterms.
-	    //
-	    // If min_hits isn't set, check at least one extra result so we
-	    // know if we've reached the end of the matches or not - then we
-	    // can avoid offering a "next" button which leads to an empty page.
-	    mset = enquire->get_mset(0, topdoc + hits_per_page,
-				     topdoc + max(hits_per_page + 1, min_hits),
-				     &rset);
-	}
-	if (usec != -1) {
+    if (!query.empty()) {
+	enquire->set_query(query);
+	// We could use the value of topdoc as first parameter, but we
+	// need to know the first few items on the mset to fake a
+	// relevance set for topterms.
+	//
+	// If min_hits isn't set, check at least one extra result so we
+	// know if we've reached the end of the matches or not - then we
+	// can avoid offering a "next" button which leads to an empty page.
+	mset = enquire->get_mset(0, topdoc + hits_per_page,
+				 topdoc + max(hits_per_page + 1, min_hits),
+				 &rset);
+    }
+    if (usec != -1) {
 #ifdef HAVE_GETTIMEOFDAY
-	    if (gettimeofday(&tv, 0) == 0) {
-		sec = tv.tv_sec - sec;
-		usec = tv.tv_usec - usec;
-		if (usec < 0) {
-		    --sec;
-		    usec += 1000000;
-		}
-	    } else {
-		usec = -1;
+	if (gettimeofday(&tv, 0) == 0) {
+	    sec = tv.tv_sec - sec;
+	    usec = tv.tv_usec - usec;
+	    if (usec < 0) {
+		--sec;
+		usec += 1000000;
 	    }
+	} else {
+	    usec = -1;
+	}
 #elif defined(FTIME_RETURNS_VOID)
-	    ftime(&tp);
+	ftime(&tp);
+	sec = tp.time - sec;
+	usec = tp.millitm * 1000 - usec;
+	if (usec < 0) {
+	    --sec;
+	    usec += 1000000;
+	}
+#elif defined(HAVE_FTIME)
+	if (ftime(&tp) == 0) {
 	    sec = tp.time - sec;
 	    usec = tp.millitm * 1000 - usec;
 	    if (usec < 0) {
 		--sec;
 		usec += 1000000;
 	    }
-#elif defined(HAVE_FTIME)
-	    if (ftime(&tp) == 0) {
-		sec = tp.time - sec;
-		usec = tp.millitm * 1000 - usec;
-		if (usec < 0) {
-		    --sec;
-		    usec += 1000000;
-		}
-	    } else {
-		usec = -1;
-	    }
+	} else {
+	    usec = -1;
+	}
 #else
-	    usec = time(NULL);
-	    if (usec != -1) {
-		sec = sec - usec;
-		usec = 0;
-	    }
+	usec = time(NULL);
+	if (usec != -1) {
+	    sec = sec - usec;
+	    usec = 0;
+	}
 #endif
-	}
     }
 }
 
Index: omega.cc
===================================================================
--- omega.cc	(revision 7513)
+++ omega.cc	(working copy)
@@ -74,6 +74,9 @@
 bool sort_numeric = false;
 Xapian::valueno sort_key = Xapian::valueno(-1);
 bool sort_ascending = true;
+bool sort_after = false;
+Xapian::Enquire::docid_order docid_order = Xapian::Enquire::ASCENDING;
+
 Xapian::valueno collapse_key = 0;
 bool collapse = false;
 
@@ -304,10 +307,27 @@
 	if (!v.empty()) {
 	    collapse_key = atoi(v.c_str());
 	    collapse = true;
-	    filters += filter_sep + v;
+	    filters += filter_sep + int_to_string(collapse_key);
 	}
     }
 
+    // docid order
+    val = cgi_params.find("DOCIDORDER");
+    if (val != cgi_params.end()) {
+	const string & v = val->second;
+	if (!v.empty()) {
+	    char ch = v[0];
+	    if (ch == 'D') {
+		docid_order = Xapian::Enquire::DESCENDING;
+		filters += 'D';
+	    } else if (ch != 'A') {
+		docid_order = Xapian::Enquire::DONT_CARE;
+	    } else {
+		filters += 'X';
+	    }
+	}
+    }
+
     // sorting
     val = cgi_params.find("SORT");
     if (val != cgi_params.end()) {
@@ -323,8 +343,23 @@
 	if (val != cgi_params.end()) {
 	    sort_ascending = (atoi(val->second.c_str()) == 0);
 	}
-	// FIXME: add SORT and SORTREVERSE to filters too!  But in a compatible
-	// way ideally...
+	val = cgi_params.find("SORTAFTER");
+	if (val != cgi_params.end()) {
+	    sort_after = (atoi(val->second.c_str()) != 0);
+	}
+	// Add the sorting related options to filters too.
+	filters += int_to_string(sort_key);
+	if (sort_after) {
+	    if (sort_ascending) {
+		filters += 'F';
+	    } else {
+		filters += 'R';
+	    }
+	} else {
+	    if (!sort_ascending) {
+		filters += 'r';
+	    }
+	}
     }
 
     // min_hits (fill mset past topdoc+(hits_per_page+1) to
Index: docs/cgiparams.txt
===================================================================
--- docs/cgiparams.txt	(revision 7513)
+++ docs/cgiparams.txt	(working copy)
@@ -120,7 +120,23 @@
 
 SORTREVERSE
 	if non-zero, reverse the sort order so that lower values are better.
+	This parameter has no effect unless SORT is also specified.
 
+SORTAFTER
+	if non-zero, order results by relevance, only sorting by value to
+	order values with the same relevance score.  This parameter has no
+	effect unless SORT is also specified.
+
+DOCIDORDER
+	set the ordering used when a comparison ends up being by docid (i.e.
+	two documents with equal relevance and/or values).  By default (if
+	DOCIDORDER isn't set or is empty) this puts them in ASCENDING order
+	(the lowest document id ranks highest).  If DOCIDORDER is specified
+	and non-empty it can begin with "D" for DESCENDING order, "A" for
+	ASCENDING order or any other character for DONT_CARE (the Xapian
+	database backend will use whichever order is most efficient).  Any
+	characters after the first are ignored.
+
 Display parameters and navigation
 ---------------------------------
 
