]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-6286: Fetch full text search result pages lazily when finding refobjs.
authorRay Lee <rhlee@berkeley.edu>
Mon, 13 Jan 2014 23:44:17 +0000 (15:44 -0800)
committerRay Lee <rhlee@berkeley.edu>
Thu, 23 Jan 2014 01:28:02 +0000 (17:28 -0800)
services/common/src/main/java/org/collectionspace/services/common/vocabulary/LazyAuthorityRefDocList.java [new file with mode: 0644]
services/common/src/main/java/org/collectionspace/services/common/vocabulary/RefNameServiceUtils.java

diff --git a/services/common/src/main/java/org/collectionspace/services/common/vocabulary/LazyAuthorityRefDocList.java b/services/common/src/main/java/org/collectionspace/services/common/vocabulary/LazyAuthorityRefDocList.java
new file mode 100644 (file)
index 0000000..e1810a3
--- /dev/null
@@ -0,0 +1,208 @@
+package org.collectionspace.services.common.vocabulary;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.collectionspace.services.client.PoxPayloadIn;
+import org.collectionspace.services.client.PoxPayloadOut;
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.DocumentException;
+import org.collectionspace.services.common.document.DocumentNotFoundException;
+import org.collectionspace.services.common.repository.RepositoryClient;
+import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthRefConfigInfo;
+import org.collectionspace.services.config.service.ServiceBindingType;
+import org.nuxeo.ecm.core.api.DocumentModel;
+import org.nuxeo.ecm.core.api.DocumentModelList;
+import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
+import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
+
+import com.google.common.collect.AbstractIterator;
+
+/**
+ * A DocumentModelList representing all of the documents that potentially reference an
+ * authority item, found via full text search. This list must be post-processed to
+ * eliminate false positives.
+ *
+ * Documents in this list are lazily fetched one page at a time, as they are accessed through
+ * the list's Iterator, retrieved with the iterator() method. List items may not be accessed
+ * through any other means, including the get() method, and the ListIterator retrieved
+ * with listIterator(). Attempts to do so will result in unspecified behavior.
+ * 
+ */
+public class LazyAuthorityRefDocList extends DocumentModelListImpl {
+       private static final long serialVersionUID = 1L;
+       
+       private ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx;
+       private RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient;
+       private RepositoryInstance repoSession;
+       private List<String> serviceTypes;
+       private String refName;
+       private String refPropName;
+       private Map<String, ServiceBindingType> queriedServiceBindings;
+       private Map<String, List<AuthRefConfigInfo>> authRefFieldsByService;
+       private String whereClauseAdditions;
+       private String orderByClause;
+       private int pageSize;
+       
+       private DocumentModelList firstPageDocList;
+       
+       /**
+        * Creates a LazyAuthorityRefDocList. The method signature is modeled after
+        * RefNameServiceUtils.findAuthorityRefDocs (the non-lazy way of doing this).
+        * 
+        * @param ctx
+        * @param repoClient
+        * @param repoSession
+        * @param serviceTypes
+        * @param refName
+        * @param refPropName
+        * @param queriedServiceBindings
+        * @param authRefFieldsByService
+        * @param whereClauseAdditions
+        * @param orderByClause
+        * @param pageSize                                              The number of documents to retrieve in each page
+        * @param computeTotal
+        * @throws DocumentException
+        * @throws DocumentNotFoundException
+        */
+       public LazyAuthorityRefDocList(
+               ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
+               RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
+               RepositoryInstance repoSession, List<String> serviceTypes,
+               String refName,
+               String refPropName,
+               Map<String, ServiceBindingType> queriedServiceBindings,
+               Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,
+               String whereClauseAdditions,
+               String orderByClause,
+               int pageSize,
+               boolean computeTotal) throws DocumentException, DocumentNotFoundException {
+
+               this.ctx = ctx;
+               this.repoClient = repoClient;
+               this.repoSession = repoSession;
+               this.serviceTypes = serviceTypes;
+               this.refName = refName;
+               this.refPropName = refPropName;
+               this.queriedServiceBindings = queriedServiceBindings;
+               this.authRefFieldsByService = authRefFieldsByService;
+               this.whereClauseAdditions = whereClauseAdditions;
+               this.orderByClause = orderByClause;
+               this.pageSize = pageSize;
+
+               // Fetch the first page immediately. This is necessary so that calls
+               // to totalSize() will work immediately. The computeTotal flag is passed
+               // into this initial page fetch. There's no need to compute totals
+               // when fetching subsequent pages.
+               
+               firstPageDocList = fetchPage(0, computeTotal);
+       }
+
+       /**
+        * Retrieves a page of authority references.
+        * 
+        * @param pageNum               The page number
+        * @param computeTotal  
+        * @return
+        * @throws DocumentNotFoundException
+        * @throws DocumentException
+        */
+       private DocumentModelList fetchPage(int pageNum, boolean computeTotal) throws DocumentNotFoundException, DocumentException {
+               return RefNameServiceUtils.findAuthorityRefDocs(ctx, repoClient, repoSession,
+                       serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,
+                       whereClauseAdditions, orderByClause, pageSize, pageNum, computeTotal);
+       }
+               
+       @Override
+       public long totalSize() {
+               // Return the totalSize from the first page of documents.
+               return firstPageDocList.totalSize();
+       }
+
+       @Override
+       public Iterator<DocumentModel> iterator() {
+               // Create a new iterator that starts with the first page of documents.
+               return new Itr(0, firstPageDocList);
+       }
+       
+       /**
+        * An iterator over a LazyAuthorityRefDocList. The iterator keeps one
+        * page of documents in memory at a time, and traverses that page until
+        * no items remain. A new page is fetched only when the current page is
+        * exhausted.
+        *
+        */
+       private class Itr extends AbstractIterator<DocumentModel> {
+               private int currentPageNum = 0;
+               private DocumentModelList currentPageDocList;
+               private Iterator<DocumentModel> currentPageIterator;
+               
+               /**
+                * Creates a new iterator.
+                * 
+                * @param currentPageNum                The initial page number
+                * @param currentPageDocList    The documents in the initial page
+                */
+               protected Itr(int pageNum, DocumentModelList pageDocList) {
+                       setCurrentPage(pageNum, pageDocList);
+               }
+
+               /**
+                * Changes the current page.
+                * 
+                * @param pageNum               The new page number
+                * @param pageDocList   The documents in the new page
+                */
+               private void setCurrentPage(int pageNum, DocumentModelList pageDocList) {
+                       this.currentPageNum = pageNum;
+                       this.currentPageDocList = pageDocList;
+                       this.currentPageIterator = pageDocList.iterator();
+               }
+               
+               @Override
+               protected DocumentModel computeNext() {
+                       // Find the next document to return, looking first in the current
+                       // page. If the current page is exhausted, fetch the next page.
+                       
+                       if (currentPageIterator.hasNext()) {
+                               // There is still an element to return from the current page.
+                               return currentPageIterator.next();
+                       }
+                       
+                       // The current page is exhausted.
+                       
+                       if (currentPageDocList.size() < pageSize) {
+                               // There are no more pages.
+                               return endOfData();
+                       }
+                       
+                       // There may be more pages. Try to fetch the next one.
+                       
+                       int nextPageNum = currentPageNum + 1;
+                       DocumentModelList nextPageDocList = null;
+                       
+                       try {
+                               nextPageDocList = fetchPage(nextPageNum, false);
+                       }
+                       catch(DocumentException e) {}
+                       
+                       if (nextPageDocList == null || nextPageDocList.size() == 0) {
+                               // There are no more pages.
+                               return endOfData();
+                       }
+
+                       // There is another page. Make it the current page.
+                       
+                       setCurrentPage(nextPageNum, nextPageDocList);
+                       
+                       if (currentPageIterator.hasNext()) {
+                               return currentPageIterator.next();
+                       }
+
+                       // Shouldn't get here.
+
+                       return endOfData();
+               }
+       }
+}
index 96a50c23ecf9251c8878bce3580c9f3d36cc379a..ec2d63d845864238280cdc13621472e9e4196585 100644 (file)
@@ -266,9 +266,16 @@ public class RefNameServiceUtils {
             // the following call, as they pertain to the list of authority\r
             // references to be returned, not to the list of documents to be\r
             // scanned for those references.\r
-            DocumentModelList docList = findAuthorityRefDocs(ctx, repoClient, repoSession,\r
+            \r
+            // Get a list of possibly referencing documents. This list is\r
+            // lazily loaded, page by page. Ideally, only one page will\r
+            // need to be loaded to fill one page of results. Some number\r
+            // of possibly referencing documents will be false positives,\r
+            // so use a page size of double the requested page size to\r
+            // account for those.\r
+            DocumentModelList docList = findAllAuthorityRefDocs(ctx, repoClient, repoSession,\r
                     serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,\r
-                    filter.getWhereClause(), null, 0 /* pageSize */, 0 /* pageNum */, computeTotal);\r
+                    filter.getWhereClause(), null, 2*pageSize, computeTotal);\r
 \r
             if (docList == null) { // found no authRef fields - nothing to process\r
                 return wrapperList;\r
@@ -465,7 +472,25 @@ public class RefNameServiceUtils {
         return nRefsFound;\r
     }\r
 \r
-    private static DocumentModelList findAuthorityRefDocs(\r
+    private static DocumentModelList findAllAuthorityRefDocs(\r
+            ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,\r
+            RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,\r
+            RepositoryInstance repoSession, List<String> serviceTypes,\r
+            String refName,\r
+            String refPropName,\r
+            Map<String, ServiceBindingType> queriedServiceBindings,\r
+            Map<String, List<AuthRefConfigInfo>> authRefFieldsByService,\r
+            String whereClauseAdditions,\r
+            String orderByClause,\r
+            int pageSize,\r
+            boolean computeTotal) throws DocumentException, DocumentNotFoundException {\r
+               \r
+       return new LazyAuthorityRefDocList(ctx, repoClient, repoSession,\r
+                       serviceTypes, refName, refPropName, queriedServiceBindings, authRefFieldsByService,\r
+                       whereClauseAdditions, orderByClause, pageSize, computeTotal);\r
+    }\r
+    \r
+    protected static DocumentModelList findAuthorityRefDocs(\r
             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,\r
             RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,\r
             RepositoryInstance repoSession, List<String> serviceTypes,\r