--- a/src/rcldb/rcldb.cpp
+++ b/src/rcldb/rcldb.cpp
@@ -73,6 +73,7 @@
 #endif
 
 const string pathelt_prefix = "XP";
+static const string ellipsis("...");
 
 string version_string(){
     return string("Recoll ") + string(rclversionstr) + string(" + Xapian ") +
@@ -245,7 +246,7 @@
 //
 // DatabaseModified and other general exceptions are catched and
 // possibly retried by our caller
-string Db::Native::makeAbstract(Xapian::docid docid, Query *query)
+vector<string> Db::Native::makeAbstract(Xapian::docid docid, Query *query)
 {
     Chrono chron;
     LOGDEB2(("makeAbstract:%d: maxlen %d wWidth %d\n", chron.ms(),
@@ -259,7 +260,7 @@
         noPrefixList(iterms, terms);
         if (terms.empty()) {
             LOGDEB(("makeAbstract::Empty term list\n"));
-            return string();
+            return vector<string>();
         }
     }
 //    listList("Match terms: ", terms);
@@ -353,12 +354,11 @@
     // This can't happen, but would crash us
     if (totalweight == 0.0) {
 	LOGERR(("makeAbstract: 0 totalweight!\n"));
-	return string();
+	return vector<string>();
     }
 
     // This is used to mark positions overlapped by a multi-word match term
     const string occupiedmarker("?");
-    const string ellipsis("...");
 
     // Let's go populate
     for (multimap<double, string>::reverse_iterator qit = byQ.rbegin(); 
@@ -439,7 +439,7 @@
     // This can happen if there are term occurences in the keywords
     // etc. but not elsewhere ?
     if (qtermposs.size() == 0) 
-	return string();
+	return vector<string>();
 
     // Walk all document's terms position lists and populate slots
     // around the query terms. We arbitrarily truncate the list to
@@ -504,8 +504,8 @@
     LOGABS(("makeAbstract:%d: extracting\n", chron.millis()));
 
     // Finally build the abstract by walking the map (in order of position)
-    string abstract;
-    abstract.reserve(sparseDoc.size() * 10);
+    vector<string> vabs;
+    string chunk;
     bool incjk = false;
     for (map<unsigned int, string>::const_iterator it = sparseDoc.begin();
 	 it != sparseDoc.end(); it++) {
@@ -517,18 +517,24 @@
 	if (TextSplit::isCJK(*uit))
 	    newcjk = true;
 	if (!incjk || (incjk && !newcjk))
-	    abstract += " ";
+	    chunk += " ";
 	incjk = newcjk;
-	abstract += it->second;
-    }
-
+	if (it->second == ellipsis) {
+	    vabs.push_back(chunk);
+	    chunk.clear();
+	} else {
+	    chunk += it->second;
+	}
+    }
+    if (!chunk.empty())
+	vabs.push_back(chunk);
     // This happens for docs with no terms (only filename) indexed? I'll fix 
     // one day (yeah)
-    if (!abstract.compare("... "))
-	abstract.clear();
+    if (vabs.size() == 1 && !vabs[0].compare("... "))
+	vabs.clear();
 
     LOGDEB2(("makeAbtract: done in %d mS\n", chron.millis()));
-    return abstract;
+    return vabs;
 }
 
 /* Rcl::Db methods ///////////////////////////////// */
@@ -1742,18 +1748,33 @@
     return true;
 }
 
-
-bool Db::makeDocAbstract(Doc &doc, Query *query, string& abstract)
+bool Db::makeDocAbstract(Doc &doc, Query *query, vector<string>& abstract)
 {
     LOGDEB1(("Db::makeDocAbstract: exti %d\n", exti));
     if (!m_ndb || !m_ndb->m_isopen) {
 	LOGERR(("Db::makeDocAbstract: no db\n"));
 	return false;
     }
-
     XAPTRY(abstract = m_ndb->makeAbstract(doc.xdocid, query),
            m_ndb->xrdb, m_reason);
-
+    return m_reason.empty() ? true : false;
+}
+
+bool Db::makeDocAbstract(Doc &doc, Query *query, string& abstract)
+{
+    LOGDEB1(("Db::makeDocAbstract: exti %d\n", exti));
+    if (!m_ndb || !m_ndb->m_isopen) {
+	LOGERR(("Db::makeDocAbstract: no db\n"));
+	return false;
+    }
+    vector<string> vab;
+    XAPTRY(vab = m_ndb->makeAbstract(doc.xdocid, query),
+           m_ndb->xrdb, m_reason);
+    for (vector<string>::const_iterator it = vab.begin(); 
+	 it != vab.end(); it++) {
+	abstract.append(*it);
+	abstract.append(ellipsis);
+    }
     return m_reason.empty() ? true : false;
 }