You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by up...@apache.org on 2015/09/22 02:42:54 UTC

incubator-geode git commit: Adding an implementation of LuceneQueryResults

Repository: incubator-geode
Updated Branches:
  refs/heads/feature/GEODE-11 74d293d17 -> 6bb17b531


Adding an implementation of LuceneQueryResults

This implementation does not support project fields yet, but it does
provide pagination by holding a set of all matching keys and fetching
the next set of values using the keys.


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/6bb17b53
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/6bb17b53
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/6bb17b53

Branch: refs/heads/feature/GEODE-11
Commit: 6bb17b531f321debc502c9d0d57fb185bf98ecd0
Parents: 74d293d
Author: Dan Smith <up...@apache.org>
Authored: Mon Sep 21 17:21:39 2015 -0700
Committer: Dan Smith <up...@apache.org>
Committed: Mon Sep 21 17:35:45 2015 -0700

----------------------------------------------------------------------
 .../cache/lucene/LuceneQueryResults.java        |  22 ++--
 .../cache/lucene/LuceneResultStruct.java        |   6 +-
 .../lucene/internal/LuceneQueryResultsImpl.java | 127 +++++++++++--------
 .../lucene/internal/LuceneResultStructImpl.java |  64 ++++++++--
 .../lucene/internal/distributed/EntryScore.java |   2 +-
 .../LuceneQueryResultsImplJUnitTest.java        |  92 ++++++++++++++
 6 files changed, 230 insertions(+), 83 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb17b53/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneQueryResults.java
----------------------------------------------------------------------
diff --git a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneQueryResults.java b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneQueryResults.java
index d597b66..62aada2 100644
--- a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneQueryResults.java
+++ b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneQueryResults.java
@@ -8,14 +8,12 @@ import java.util.List;
  * 
  * @author Xiaojian Zhou
  * @since 8.5
+ * 
+ * @param <K> The type of the key
+ * @param <V> The type of the value
  */
 
-public interface LuceneQueryResults {
-  /**
-   * @return query result identifier. The identifier can be used to trace the origin of this result
-   */
-  public Object getID();
-
+public interface LuceneQueryResults<K, V> {
   /**
    * @return total number of hits for this query
    */
@@ -26,11 +24,15 @@ public interface LuceneQueryResults {
    */
   public float getMaxScore();
 
-  /*
-   * get next page of result if pagesize is specified in query, otherwise, return null
+  /**
+   * Get the next page of results.
+   * 
+   * @return a page of results, or null if there are no more pages
    */
-  public List<LuceneResultStruct> getNextPage();
+  public List<LuceneResultStruct<K, V>> getNextPage();
 
-  /* Is next page of result available */
+  /**
+   *  True if there another page of results. 
+   */
   public boolean hasNextPage();
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb17b53/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneResultStruct.java
----------------------------------------------------------------------
diff --git a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneResultStruct.java b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneResultStruct.java
index 139fbd4..75ac0a1 100644
--- a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneResultStruct.java
+++ b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/LuceneResultStruct.java
@@ -8,7 +8,7 @@ package com.gemstone.gemfire.cache.lucene;
  * @author Xiaojian Zhou
  * @since 8.5
  */
-public interface LuceneResultStruct {
+public interface LuceneResultStruct<K, V> {
   /**
    * Return the value associated with the given field name
    *
@@ -24,7 +24,7 @@ public interface LuceneResultStruct {
    * @return key
    * @throws IllegalArgumentException If this struct does not contain key
    */
-  public Object getKey();
+  public K getKey();
   
   /**
    * Return value of the entry
@@ -32,7 +32,7 @@ public interface LuceneResultStruct {
    * @return value the whole domain object
    * @throws IllegalArgumentException If this struct does not contain value
    */
-  public Object getValue();
+  public V getValue();
   
   /**
    * Return score of the query 

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb17b53/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImpl.java
----------------------------------------------------------------------
diff --git a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImpl.java b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImpl.java
index 11200c2..93a7f9d 100644
--- a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImpl.java
+++ b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImpl.java
@@ -2,64 +2,85 @@ package com.gemstone.gemfire.cache.lucene.internal;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
+import java.util.Map;
 
-import com.gemstone.gemfire.cache.execute.FunctionException;
-import com.gemstone.gemfire.cache.execute.ResultCollector;
+import com.gemstone.gemfire.cache.Region;
 import com.gemstone.gemfire.cache.lucene.LuceneQueryResults;
 import com.gemstone.gemfire.cache.lucene.LuceneResultStruct;
-import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.cache.lucene.internal.distributed.EntryScore;
 
-public class LuceneQueryResultsImpl implements LuceneQueryResults,
-    ResultCollector<LuceneQueryResults, List<LuceneQueryResults>> {
+/**
+ * Implementation of LuceneQueryResults that fetchs a page at a time
+ * from the server, given a set of EntryScores (key and score).
+ *
+ * @param <K> The type of the key
+ * @param <V> The type of the value
+ */
+public class LuceneQueryResultsImpl<K,V> implements LuceneQueryResults<K,V> {
 
-  // list of docs matching search query
-  private List<LuceneResultStruct> hits = new ArrayList<>();
+  /**
+   *  list of docs matching search query
+   */
+  private final List<EntryScore> hits;
+  
+  /**
+   * The maximum score. Lazily evaluated
+   */
   private float maxScore = Float.MIN_VALUE;
-
-  @Override
-  public List<LuceneQueryResults> getResult() throws FunctionException {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public List<LuceneQueryResults> getResult(long timeout, TimeUnit unit) throws FunctionException, InterruptedException {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public void addResult(DistributedMember memberID, LuceneQueryResults resultOfSingleExecution) {
-    // results.add(resultOfSingleExecution);
-  }
-
-  @Override
-  public void endResults() {
-    // TODO Auto-generated method stub
-
+  
+  /**
+   * The user region where values are stored.
+   */
+  private final Region<K, V> userRegion;
+  
+  /**
+   * The start of the next page of results we want to fetch 
+   */
+  private int currentHit = 0;
+  
+  /**
+   * The page size the user wants.
+   */
+  private int pageSize;
+  
+  public LuceneQueryResultsImpl(List<EntryScore> hits, Region<K,V> userRegion, int pageSize) {
+    this.hits = hits;
+    this.userRegion = userRegion;
+    this.pageSize = pageSize;
   }
 
   @Override
-  public void clearResults() {
-    // TODO Auto-generated method stub
-
-  }
+  public List<LuceneResultStruct<K,V>> getNextPage() {
+    if(!hasNextPage()) {
+      return null;
+    }
+    
+    int end = currentHit + pageSize;
+    end = end > hits.size() ? hits.size() : end;
+    List<EntryScore> scores = hits.subList(currentHit, end);
+    
+    ArrayList<K> keys = new ArrayList<K>(hits.size());
+    for(EntryScore score : scores) {
+      keys.add((K) score.getKey());
+    }
+    
+    Map<K,V> values = userRegion.getAll(keys);
+    
+    ArrayList<LuceneResultStruct<K,V>> results = new ArrayList<LuceneResultStruct<K,V>>(hits.size());
+    for(EntryScore score : scores) {
+      V value = values.get(score.getKey());
+      results.add(new LuceneResultStructImpl(score.getKey(), value, score.getScore()));
+    }
+    
 
-  @Override
-  public List<LuceneResultStruct> getNextPage() {
-    return null;
+    currentHit = end;
+    
+    return results;
   }
 
   @Override
   public boolean hasNextPage() {
-    return false;
-  }
-
-  @Override
-  public Object getID() {
-    // TODO Auto-generated method stub
-    return null;
+    return hits.size() > currentHit;
   }
 
   @Override
@@ -69,18 +90,12 @@ public class LuceneQueryResultsImpl implements LuceneQueryResults,
 
   @Override
   public float getMaxScore() {
-    return maxScore;
-  }
-
-  /**
-   * Adds a result hit to the result set
-   * 
-   * @param hit
-   */
-  public void addHit(LuceneResultStruct hit) {
-    hits.add(hit);
-    if (hit.getScore() > maxScore) {
-      maxScore = hit.getScore();
+    if(maxScore == Float.MIN_VALUE) {
+      for(EntryScore score : hits) {
+        maxScore = Math.max(maxScore, score.getScore());
+      }
     }
+    
+    return maxScore;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb17b53/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneResultStructImpl.java
----------------------------------------------------------------------
diff --git a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneResultStructImpl.java b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneResultStructImpl.java
index 430496a..6417675 100644
--- a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneResultStructImpl.java
+++ b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/LuceneResultStructImpl.java
@@ -2,30 +2,30 @@ package com.gemstone.gemfire.cache.lucene.internal;
 
 import com.gemstone.gemfire.cache.lucene.LuceneResultStruct;
 
-public class LuceneResultStructImpl implements LuceneResultStruct {
-  Object key;
+public class LuceneResultStructImpl<K,V> implements LuceneResultStruct<K,V> {
+  K key;
+  V value;
   float score;
 
-  public LuceneResultStructImpl(Object key, float score) {
+  public LuceneResultStructImpl(K key, V value, float score) {
     this.key = key;
+    this.value = value;
     this.score = score;
   }
 
   @Override
   public Object getProjectedField(String fieldName) {
-    // TODO Auto-generated method stub
-    return null;
+    throw new UnsupportedOperationException();
   }
 
   @Override
-  public Object getKey() {
+  public K getKey() {
     return key;
   }
 
   @Override
-  public Object getValue() {
-    // TODO Auto-generated method stub
-    return null;
+  public V getValue() {
+    return value;
   }
 
   @Override
@@ -35,13 +35,51 @@ public class LuceneResultStructImpl implements LuceneResultStruct {
 
   @Override
   public Object[] getNames() {
-    // TODO Auto-generated method stub
-    return null;
+    throw new UnsupportedOperationException();
   }
 
   @Override
   public Object[] getResultValues() {
-    // TODO Auto-generated method stub
-    return null;
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((key == null) ? 0 : key.hashCode());
+    result = prime * result + Float.floatToIntBits(score);
+    result = prime * result + ((value == null) ? 0 : value.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (obj == null)
+      return false;
+    if (getClass() != obj.getClass())
+      return false;
+    LuceneResultStructImpl other = (LuceneResultStructImpl) obj;
+    if (key == null) {
+      if (other.key != null)
+        return false;
+    } else if (!key.equals(other.key))
+      return false;
+    if (Float.floatToIntBits(score) != Float.floatToIntBits(other.score))
+      return false;
+    if (value == null) {
+      if (other.value != null)
+        return false;
+    } else if (!value.equals(other.value))
+      return false;
+    return true;
+  }
+
+  @Override
+  public String toString() {
+    return "LuceneResultStructImpl [key=" + key + ", value=" + value
+        + ", score=" + score + "]";
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb17b53/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/distributed/EntryScore.java
----------------------------------------------------------------------
diff --git a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/distributed/EntryScore.java b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/distributed/EntryScore.java
index 1c80204..456b3ba 100644
--- a/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/distributed/EntryScore.java
+++ b/gemfire-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/distributed/EntryScore.java
@@ -25,7 +25,7 @@ public class EntryScore implements DataSerializableFixedID {
     this.key = key;
     this.score = score;
   }
-
+  
   public Object getKey() {
     return key;
   }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb17b53/gemfire-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImplJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImplJUnitTest.java b/gemfire-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImplJUnitTest.java
new file mode 100644
index 0000000..000c01e
--- /dev/null
+++ b/gemfire-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/LuceneQueryResultsImplJUnitTest.java
@@ -0,0 +1,92 @@
+package com.gemstone.gemfire.cache.lucene.internal;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mockito;
+import org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.lucene.LuceneResultStruct;
+import com.gemstone.gemfire.cache.lucene.internal.distributed.EntryScore;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class LuceneQueryResultsImplJUnitTest {
+
+  private List<EntryScore> hits;
+  private List<LuceneResultStruct> expected = new ArrayList<LuceneResultStruct>();
+  
+  @Before
+  public void setUp() {
+    hits = new ArrayList<EntryScore>();
+    
+    for(int i =0; i < 23; i++) {
+      hits.add(new EntryScore("key_" + i, i));
+      expected.add(new LuceneResultStructImpl<String, String>("key_" + i, "value_" + i, i));
+    }
+  }
+  
+  @Test
+  public void testMaxStore() {
+
+    hits.set(5, new EntryScore("key_5", 502));
+    
+    LuceneQueryResultsImpl<String, String> results = new LuceneQueryResultsImpl<String, String>(hits, null, 5);
+    
+    assertEquals(502, results.getMaxScore(), 0.1f);
+  }
+  
+  @Test
+  public void testPagination() {
+    Region<String, String> userRegion = Mockito.mock(Region.class);
+    
+    Mockito.when(userRegion.getAll(Mockito.anyCollection())).thenAnswer(new Answer() {
+
+      @Override
+      public Map answer(InvocationOnMock invocation) throws Throwable {
+        Collection<String> keys = invocation.getArgumentAt(0, Collection.class);
+        Map<String, String> results = new HashMap<String, String>();
+        for(String key : keys) {
+          results.put(key, key.replace("key_", "value_"));
+        }
+        
+        return results;
+      }
+    });
+    
+    
+    LuceneQueryResultsImpl<String, String> results = new LuceneQueryResultsImpl<String, String>(hits, userRegion, 10);
+    
+    assertEquals(23, results.size());
+    
+    assertTrue(results.hasNextPage());
+    
+    List<LuceneResultStruct<String, String>> next  = results.getNextPage();
+    assertEquals(expected.subList(0, 10), next);
+    
+    assertTrue(results.hasNextPage());
+    next  = results.getNextPage();
+    assertEquals(expected.subList(10, 20), next);
+    
+    assertTrue(results.hasNextPage());
+    next  = results.getNextPage();
+    assertEquals(expected.subList(20, 23), next);
+    
+    
+    assertFalse(results.hasNextPage());
+    assertNull(results.getNextPage());
+  }
+
+}