Revision: 14754
          http://gate.svn.sourceforge.net/gate/?rev=14754&view=rev
Author:   valyt
Date:     2011-12-13 12:08:02 +0000 (Tue, 13 Dec 2011)
Log Message:
-----------
Remote Query Runner:
- logic bug fixed: on the remote side the document IDs and document scores are 
always in ranked order.
- added caching for document IDs and document scores on the remote side

Modified Paths:
--------------
    mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java
    
mimir/trunk/mimir-web/grails-app/controllers/gate/mimir/web/SearchController.groovy

Modified: mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java
===================================================================
--- mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java       
2011-12-13 02:17:44 UTC (rev 14753)
+++ mimir/trunk/mimir-client/src/gate/mimir/search/RemoteQueryRunner.java       
2011-12-13 12:08:02 UTC (rev 14754)
@@ -19,10 +19,14 @@
 import gate.mimir.search.query.Binding;
 import gate.mimir.tool.WebUtils;
 import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
+import it.unimi.dsi.fastutil.doubles.DoubleList;
 import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
 
 import java.io.IOException;
 import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.List;
@@ -46,12 +50,12 @@
   
   protected static final String ACTION_DOC_COUNT_BIN = "documentsCountBin";
   
-  protected static final String ACTION_DOC_ID_BIN = "documentIdBin";
+  protected static final String ACTION_DOC_IDS_BIN = "documentIdsBin";
   
+  protected static final String ACTION_DOC_SCORES_BIN = "documentsScoresBin";
+  
   protected static final String ACTION_DOC_HITS_BIN = "documentHitsBin";
   
-  protected static final String ACTION_DOC_SCORES_BIN = "documentsScoresBin";
-  
   protected static final String ACTION_DOC_DATA_BIN = "documentDataBin";
   
   protected static final String ACTION_RENDER_DOCUMENT = "renderDocument";
@@ -89,24 +93,8 @@
             Thread.sleep(500);
           } else {
             // remote side has finished enumerating all documents
-            // download all the scores in one go.
-            double[] docScores = (double[]) webUtils.getObject(
-              getActionBaseUrl(ACTION_DOC_SCORES_BIN), "queryId", 
-              URLEncoder.encode(queryId, "UTF-8"));
-            if(docScores != null && docScores.length > 0) {
-              documentScores = new DoubleArrayList(docScores);
-              if(documentScores.size() != newDocumentsCount) {
-                // malfunction
-                exceptionInBackgroundThread = new RuntimeException(
-                  "Incorrect number of document scores from remote side: " +
-                  "was expecting " + newDocumentsCount + " but got " +
-                  documentScores.size() +"!");
-                return;
-              }              
-            } else {
-              // we're not scoring
-              documentScores = null;
-            }
+            // download the first block of IDs and scores
+            downloadDocIdScores(0);
             // ...and we're done!
             documentsCount = newDocumentsCount;
           }
@@ -124,20 +112,6 @@
             exceptionInBackgroundThread = e;
             return;
           }
-        } catch(ClassNotFoundException e) {
-          if(failuresAllowed > 0) {
-            failuresAllowed --;
-            logger.error("Exception while obtaining remote document data (will 
retry)", e);
-            try {
-              Thread.sleep(100);
-            } catch(InterruptedException e1) {
-              Thread.currentThread().interrupt();
-            }
-          } else {
-            logger.error("Exception while obtaining remote document data.", e);
-            exceptionInBackgroundThread = e;
-            return;
-          }
         } catch(InterruptedException e) {
           Thread.currentThread().interrupt();
           logger.warn("Interrupted while waiting", e);
@@ -146,6 +120,11 @@
     }
   }
   
+  /**
+   * The size of the document block (the number of documents for which the IDs
+   * are downloaded in one operation.
+   */
+  private int docBlockSize = 1000;
   
   /**
    * A cache of MG4J {@link Document}s used for returning the hit text.
@@ -173,14 +152,14 @@
    */
   private volatile int documentsCount;
   
-  private volatile boolean closed;
-  
   /**
    * The current number of documents. After all documents have been retrieved, 
    * this value is identical to {@link #documentsCount}. 
    */
-  private int currentDocumentsCount;
+  private volatile int currentDocumentsCount;
   
+  private volatile boolean closed;
+  
   /**
    * Shared Logger
    */
@@ -193,15 +172,18 @@
    * the job of any of the interactive methods to report it.
    */
   private Exception exceptionInBackgroundThread;
+
+  /**
+   * The document IDs in ranking order. If ranking is not preformed, then the
+   * document IDs are in the order they are returned by the index. 
+   */
+  protected IntList documentIds;  
   
   /**
-   * If scoring is enabled ({@link #scorer} is not <code>null</code>), this 
list
-   * contains the scores for the documents found to contain hits. This list is 
-   * aligned to {@link #documentIds}.   
+   * The document scores. This list is aligned to {@link #documentIds}.   
    */
-  protected DoubleArrayList documentScores;
+  protected DoubleList documentScores;
   
-  
   public RemoteQueryRunner(String remoteUrl, String queryString, 
       Executor threadSource,  WebUtils webUtils) throws IOException {
     this.remoteUrl = remoteUrl.endsWith("/") ? remoteUrl : (remoteUrl + "/");
@@ -220,8 +202,10 @@
           "an unknown object type!").initCause(e);
     }
     
-    // init the cache
-    documentCache = new Int2ObjectLinkedOpenHashMap<DocumentData>();    
+    // init the caches
+    this.documentIds = new IntArrayList();
+    this.documentScores = new DoubleArrayList();
+    this.documentCache = new Int2ObjectLinkedOpenHashMap<DocumentData>();
     
     // start the background action
     documentsCount = -1;
@@ -245,9 +229,8 @@
       throw (IOException)new IOException(
           "Problem communicating with the remote index", e);
     }
-    
     //an example URL looks like this:
-    
//http://localhost:8080/mimir/bf25398f-f087-4224-bfa6-c2ef00399c04/search/hitCountBin?queryId=c4da799e-9ca2-46ae-8ded-30bdc37ad607
+    
//http://localhost:8080/mimir/bf25398ff0874224/search/documentsCountBin?queryId=c4da799e-9ca2-46ae-8ded-30bdc37ad607
     StringBuilder str = new StringBuilder(remoteUrl);
     str.append(SERVICE_SEARCH);
     str.append('/');
@@ -304,16 +287,18 @@
   public int getDocumentsCurrentCount() {
     return (documentsCount < 0) ? currentDocumentsCount : documentsCount;
   }
-
+  
   /* (non-Javadoc)
    * @see gate.mimir.search.QueryRunner#getDocumentID(int)
    */
   @Override
   public int getDocumentID(int rank) throws IndexOutOfBoundsException,
           IOException {
-    return webUtils.getInt(getActionBaseUrl(ACTION_DOC_ID_BIN), 
-            "queryId", URLEncoder.encode(queryId, "UTF-8"), 
-            "documentRank", Integer.toString(rank));
+    if(rank >= documentIds.size()) {
+      // we need to get more document IDs&scores
+      downloadDocIdScores(rank);
+    }
+    return documentIds.getInt(rank);
   }
 
   /* (non-Javadoc)
@@ -322,12 +307,11 @@
   @Override
   public double getDocumentScore(int rank) throws IndexOutOfBoundsException,
           IOException {
-    if(documentsCount < 0) {
-      // premature call
-      throw new IndexOutOfBoundsException(
-        "Score requested before collection of documents has completed.");
-    }
-    return documentScores != null ? documentScores.get(rank) : DEFAULT_SCORE;
+    if(rank >= documentScores.size()) {
+      // we need to get more document IDs&scores
+      downloadDocIdScores(rank);
+    }    
+    return documentScores.get(rank);
   }
 
   /* (non-Javadoc)
@@ -421,4 +405,40 @@
     closed = true;
     documentCache.clear();
   }
+  
+  /**
+   * Gets from the remote end point a range of document IDs and document 
scores,
+   * which is guaranteed to include the document at the given rank.
+   * @param rank
+   * @throws IOException 
+   */
+  protected void downloadDocIdScores(int rank) throws IOException {
+    int firstRank = documentIds.size();
+    if(firstRank != documentScores.size()) {
+      throw new IllegalStateException("Document IDs and scores out of sync.");
+    }
+    int size = rank - firstRank;
+    if(size < docBlockSize) size = docBlockSize;
+    
+    int[] newDocIds;
+    double[] newDocScores;
+    try {
+      newDocIds = (int[]) webUtils.getObject(
+        getActionBaseUrl(ACTION_DOC_IDS_BIN), 
+        "queryId", URLEncoder.encode(queryId, "UTF-8"),
+        "firstRank", Integer.toString(firstRank),
+        "size", Integer.toString(size));
+      documentIds.addElements(firstRank, newDocIds);
+      newDocScores = (double[]) webUtils.getObject(
+        getActionBaseUrl(ACTION_DOC_SCORES_BIN), 
+        "queryId", URLEncoder.encode(queryId, "UTF-8"),
+        "firstRank", Integer.toString(firstRank),
+        "size", Integer.toString(size));
+      documentScores.addElements(firstRank, newDocScores);
+    } catch(ClassNotFoundException e) {
+      // this should really not happen (the 'class' is double)
+      throw new RuntimeException("Error communicating to remote endpoint", e);
+    }
+  }
+  
 }

Modified: 
mimir/trunk/mimir-web/grails-app/controllers/gate/mimir/web/SearchController.groovy
===================================================================
--- 
mimir/trunk/mimir-web/grails-app/controllers/gate/mimir/web/SearchController.groovy
 2011-12-13 02:17:44 UTC (rev 14753)
+++ 
mimir/trunk/mimir-web/grails-app/controllers/gate/mimir/web/SearchController.groovy
 2011-12-13 12:08:02 UTC (rev 14754)
@@ -616,40 +616,110 @@
     }
   }
   
+  // protected static final String ACTION_DOC_IDS_BIN = "documentIdsBin";
   /**
-   * Gets the ID of a document as a serialised String value.
+   * Gets the IDs of a range of documents, in ranking order.
    */
-  def documentIdBin = {
+  def documentIdsBin = {
     def p = params["request"] ?: params
     //get the query ID
     String queryId = p["queryId"]
     QueryRunner runner = searchService.getQueryRunner(queryId);
     if(runner){
+      if(runner.getDocumentsCount() < 0) {
+        // premature call
+        response.sendError(HttpServletResponse.SC_NOT_FOUND,
+          "Query ID ${queryId} has not completed collecting hits; please try 
later")
+      }
       //get the parameters: int documentRank
-      def documentRankParam = p["documentRank"]
-      if (documentRankParam) {
-        try {
+      def firstRankParam = p["firstRank"]
+      if (firstRankParam) {
+        def sizeParam = p["size"]
+        if(sizeParam) {
           //we have all required parameters
-          int documentRank = documentRankParam.toInteger()
-          int docId = runner.getDocumentID(documentRank)
-          new ObjectOutputStream (response.outputStream).withStream {stream ->
-            stream.writeObject(Integer.toString(docId))
+          try {
+            int from = firstRankParam.toInteger()
+            int resultSize = sizeParam.toInteger()
+            int to = from + resultSize
+            if(to > runner.getDocumentsCount()) {
+              to = runner.getDocumentsCount()
+            }
+            int[] docIds = new int[(to - from)]
+            for(int rank = from; rank < to; rank++) {
+              docIds[rank] = runner.getDocumentID(rank)
+            }
+            new ObjectOutputStream (response.outputStream).withStream {stream 
->
+              stream.writeObject(docIds)
+            }
+          } catch(Exception e){
+            log.warn("Error while sending document ID", e)
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                "Error while obtaining the document ID: \"" +
+                e.getMessage() + "\"!")
           }
-        } catch(Exception e){
-          log.warn("Error while sending document ID", e)
-          response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
-              "Error while obtaining the document ID: \"" +
-              e.getMessage() + "\"!")
+        } else {
+        response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+          "No value provided for parameter size!")
         }
       } else {
         response.sendError(HttpServletResponse.SC_BAD_REQUEST,
-            "No value provided for parameter documentRank!")
+            "No value provided for parameter firstRank!")
       }
     } else {
       response.sendError(HttpServletResponse.SC_NOT_FOUND,
           "Query ID ${queryId} not known!")
     }
   }
+
+  // protected static final String ACTION_DOC_SCORES_BIN = 
"documentsScoresBin";  
+  /**
+   * Retrieves the scores for a range of documents
+   */
+  def documentsScoresBin = {
+    def p = params["request"] ?: params
+    //get the query ID
+    String queryId = p["queryId"]
+    QueryRunner runner = searchService.getQueryRunner(queryId);
+    if(runner){
+      //get the parameters: int documentRank
+      def firstRankParam = p["firstRank"]
+      if (firstRankParam) {
+        def sizeParam = p["size"]
+        if(sizeParam) {
+          //we have all required parameters
+          try {
+            int from = firstRankParam.toInteger()
+            int resultSize = sizeParam.toInteger()
+            int to = from + resultSize
+            if(to > runner.getDocumentsCount()) {
+              to = runner.getDocumentsCount()
+            }
+            double[] docScores = new double[(to - from)]
+            for(int rank = from; rank < to; rank++) {
+              docScores[rank] = runner.getDocumentScore(rank)
+            }
+            new ObjectOutputStream (response.outputStream).withStream {stream 
->
+              stream.writeObject(docScores)
+            }
+          } catch(Exception e){
+            log.warn("Error while sending document ID", e)
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+                "Error while obtaining the document ID: \"" +
+                e.getMessage() + "\"!")
+          }
+        } else {
+        response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+          "No value provided for parameter size!")
+        }
+      } else {
+        response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+            "No value provided for parameter firstRank!")
+      }
+    } else {
+      response.sendError(HttpServletResponse.SC_NOT_FOUND,
+          "Query ID ${queryId} not known!")
+    }
+  }
   
   /**
    * Retrieves the hits within a given result document.
@@ -686,36 +756,6 @@
     }
   }
   
-  /**
-  * Retrieves all the document scores
-  */
-  def documentsScoresBin = {
-    def p = params["request"] ?: params
-    //get the query ID
-    String queryId = p["queryId"]
-    QueryRunner runner = searchService.getQueryRunner(queryId);
-    if(runner){
-      try{
-       int docCount = runner.getDocumentsCount()
-        double[] scores = new double[docCount]
-        for(int i = 0; i < docCount; i++) {
-          scores[i] = runner.getDocumentScore(i)
-        }
-        new ObjectOutputStream (response.outputStream).withStream {stream ->
-         stream.writeObject(scores)
-        }
-      }catch(Exception e){
-        log.warn("Error while sending document scores", e)
-        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
-        "Error while obtaining the scores: \"" + e.getMessage() + "\"!")
-      }
-    } else{
-      response.sendError(HttpServletResponse.SC_NOT_FOUND,
-          "Query ID ${queryId} not known!")
-    }
-  }
-  
-  //
   //  protected static final String ACTION_DOC_DATA_BIN = "documentDataBin";
   /**
    * Gets the document data (title, URI, text) for a given document. The 

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Systems Optimization Self Assessment
Improve efficiency and utilization of IT resources. Drive out cost and 
improve service delivery. Take 5 minutes to use this Systems Optimization 
Self Assessment. http://www.accelacomm.com/jaw/sdnl/114/51450054/
_______________________________________________
GATE-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gate-cvs

Reply via email to