diff --git a/xapian-core/api/omdocument.cc b/xapian-core/api/omdocument.cc
index 9f009b3..cfaef9c 100644
--- a/xapian-core/api/omdocument.cc
+++ b/xapian-core/api/omdocument.cc
@@ -346,6 +346,7 @@ Xapian::Document::Internal::add_posting(const string & tname, Xapian::termpos tp
 			      Xapian::termcount wdfinc)
 {
     need_terms();
+    positions_modified = true;
 
     map<string, OmDocumentTerm>::iterator i;
     i = terms.find(tname);
@@ -390,6 +391,7 @@ Xapian::Document::Internal::remove_posting(const string & tname,
     }
     i->second.remove_position(tpos);
     if (wdfdec) i->second.dec_wdf(wdfdec);
+    positions_modified = true;
 }
 
 void
@@ -403,6 +405,7 @@ Xapian::Document::Internal::remove_term(const string & tname)
 		"' is not present in document, in "
 		"Xapian::Document::Internal::remove_term()");
     }
+    positions_modified = !i->second.positions.empty();
     terms.erase(i);
 }
 	
@@ -411,6 +414,9 @@ Xapian::Document::Internal::clear_terms()
 {
     terms.clear();
     terms_here = true;
+    // Assume there was a term with positions for now.
+    // FIXME: may be worth checking...
+    positions_modified = true;
 }
 
 Xapian::termcount
diff --git a/xapian-core/backends/chert/chert_database.cc b/xapian-core/backends/chert/chert_database.cc
index 52e4977..40f09fe 100644
--- a/xapian-core/backends/chert/chert_database.cc
+++ b/xapian-core/backends/chert/chert_database.cc
@@ -1340,6 +1340,8 @@ ChertWritableDatabase::replace_document(Xapian::docid did,
 	}
 
 	if (!modifying || document.internal->terms_modified()) {
+	    bool pos_modified = !modifying ||
+				document.internal->term_positions_modified();
 	    intrusive_ptr<const ChertWritableDatabase> ptrtothis(this);
 	    ChertTermList termlist(ptrtothis, did);
 	    Xapian::TermIterator term = document.termlist_begin();
@@ -1370,7 +1372,8 @@ ChertWritableDatabase::replace_document(Xapian::docid did,
 		    termcount old_wdf = termlist.get_wdf();
 		    new_doclen -= old_wdf;
 		    add_freq_delta(old_tname, -1, -old_wdf);
-		    position_table.delete_positionlist(did, old_tname);
+		    if (pos_modified)
+			position_table.delete_positionlist(did, old_tname);
 		    update_mod_plist(did, old_tname, 'D', 0u);
 		    termlist.next();
 		} else if (cmp > 0) {
@@ -1382,11 +1385,13 @@ ChertWritableDatabase::replace_document(Xapian::docid did,
 			throw Xapian::InvalidArgumentError("Term too long (> "STRINGIZE(MAX_SAFE_TERM_LENGTH)"): " + new_tname);
 		    add_freq_delta(new_tname, 1, new_wdf);
 		    update_mod_plist(did, new_tname, 'A', new_wdf);
-		    PositionIterator pos = term.positionlist_begin();
-		    if (pos != term.positionlist_end()) {
-			position_table.set_positionlist(
-			    did, new_tname,
-			    pos, term.positionlist_end(), false);
+		    if (pos_modified) {
+			PositionIterator pos = term.positionlist_begin();
+			if (pos != term.positionlist_end()) {
+			    position_table.set_positionlist(
+				did, new_tname,
+				pos, term.positionlist_end(), false);
+			}
 		    }
 		    ++term;
 		} else if (cmp == 0) {
@@ -1405,13 +1410,15 @@ ChertWritableDatabase::replace_document(Xapian::docid did,
 			update_mod_plist(did, new_tname, 'M', new_wdf);
 		    }
 
-		    PositionIterator pos = term.positionlist_begin();
-		    if (pos != term.positionlist_end()) {
-			position_table.set_positionlist(did, new_tname, pos,
-							term.positionlist_end(),
-							true);
-		    } else {
-			position_table.delete_positionlist(did, new_tname);
+		    if (pos_modified) {
+			PositionIterator pos = term.positionlist_begin();
+			if (pos != term.positionlist_end()) {
+			    position_table.set_positionlist(did, new_tname, pos,
+							    term.positionlist_end(),
+							    true);
+			} else {
+			    position_table.delete_positionlist(did, new_tname);
+			}
 		    }
 
 		    ++term;
diff --git a/xapian-core/common/document.h b/xapian-core/common/document.h
index 189b23e..e748275 100644
--- a/xapian-core/common/document.h
+++ b/xapian-core/common/document.h
@@ -2,7 +2,7 @@
  *
  * Copyright 1999,2000,2001 BrightStation PLC
  * Copyright 2002 Ananova Ltd
- * Copyright 2003,2004,2005,2007,2008,2009,2010 Olly Betts
+ * Copyright 2003,2004,2005,2007,2008,2009,2010,2011 Olly Betts
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -59,6 +59,7 @@ class Xapian::Document::Internal : public Xapian::Internal::intrusive_base {
 	bool data_here;
 	mutable bool values_here; // FIXME mutable is a hack
 	mutable bool terms_here;
+	mutable bool positions_modified;
 
 	/// The (user defined) data associated with this document.
 	string data;
@@ -174,6 +175,11 @@ class Xapian::Document::Internal : public Xapian::Internal::intrusive_base {
 	    return terms_here;
 	}
 
+	/// Return true if term positions may have been modified.
+	bool term_positions_modified() const {
+	    return positions_modified;
+	}
+
 	/// Return true if the document may have been modified.
 	bool modified() const {
 	    return terms_here || values_here || data_here;
@@ -201,11 +207,11 @@ class Xapian::Document::Internal : public Xapian::Internal::intrusive_base {
 	Internal(Xapian::Internal::intrusive_ptr<const Xapian::Database::Internal> database_,
 		 Xapian::docid did_)
 	    : database(database_), data_here(false), values_here(false),
-	      terms_here(false), did(did_) { }
+	      terms_here(false), positions_modified(false), did(did_) { }
 
         Internal()
 	    : database(0), data_here(false), values_here(false),
-	      terms_here(false), did(0) { }
+	      terms_here(false), positions_modified(false), did(0) { }
 
 	/** Destructor.
 	 *

