HTRACE-107: Support for Greater Than operator in htraced
diff --git a/htrace-core/src/go/src/org/apache/htrace/common/query.go b/htrace-core/src/go/src/org/apache/htrace/common/query.go
index 0c909a1..b59cbbe 100644
--- a/htrace-core/src/go/src/org/apache/htrace/common/query.go
+++ b/htrace-core/src/go/src/org/apache/htrace/common/query.go
@@ -51,6 +51,7 @@
 	EQUALS                 Op = "eq"
 	LESS_THAN_OR_EQUALS    Op = "le"
 	GREATER_THAN_OR_EQUALS Op = "ge"
+	GREATER_THAN           Op = "gt"
 )
 
 func (op Op) IsDescending() bool {
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
index 523b7ab..a4233f4 100644
--- a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
@@ -520,7 +520,8 @@
 
 	// Validate the predicate operation.
 	switch pred.Op {
-	case common.EQUALS, common.LESS_THAN_OR_EQUALS, common.GREATER_THAN_OR_EQUALS:
+	case common.EQUALS, common.LESS_THAN_OR_EQUALS,
+		common.GREATER_THAN_OR_EQUALS, common.GREATER_THAN:
 		break
 	case common.CONTAINS:
 		if p.fieldIsNumeric() {
@@ -618,6 +619,8 @@
 			return intVal <= pred.intKey
 		case common.GREATER_THAN_OR_EQUALS:
 			return intVal >= pred.intKey
+		case common.GREATER_THAN:
+			return intVal > pred.intKey
 		default:
 			panic(fmt.Sprintf("unknown Op type %s should have been caught "+
 				"during normalization", pred.Op))
@@ -632,6 +635,8 @@
 			return strVal <= pred.strKey
 		case common.GREATER_THAN_OR_EQUALS:
 			return strVal >= pred.strKey
+		case common.GREATER_THAN:
+			return strVal > pred.strKey
 		default:
 			panic(fmt.Sprintf("unknown Op type %s should have been caught "+
 				"during normalization", pred.Op))
@@ -679,6 +684,25 @@
 	keyPrefix byte
 }
 
+// Return true if this operation may require skipping the first result we get back from leveldb.
+func mayRequireOneSkip(op common.Op) bool {
+	switch op {
+	// When dealing with descending predicates, the first span we read might not satisfy
+	// the predicate, even though subsequent ones will.  This is because the iter.Seek()
+	// function "moves the iterator the position of the key given or, if the key doesn't
+	// exist, the next key that does exist in the database."  So if we're on that "next
+	// key" it will not satisfy the predicate, but the keys previous to it might.
+	case common.LESS_THAN_OR_EQUALS:
+		return true
+	// iter.Seek basically takes us to the key which is "greater than or equal to" some
+	// value.  Since we want greater than (not greater than or equal to) we may have to
+	// skip the first key.
+	case common.GREATER_THAN:
+		return true
+	}
+	return false
+}
+
 // Fill in the entry in the 'next' array for a specific shard.
 func (src *source) populateNextFromShard(shardIdx int) {
 	lg := src.store.lg
@@ -737,12 +761,7 @@
 		} else {
 			lg.Debugf("Span %016x from shard %d does not satisfy the predicate.\n",
 				sid, shardIdx)
-			if src.numRead[shardIdx] <= 1 && src.pred.Op.IsDescending() {
-				// When dealing with descending predicates, the first span we read might not satisfy
-				// the predicate, even though subsequent ones will.  This is because the iter.Seek()
-				// function "moves the iterator the position of the key given or, if the key doesn't
-				// exist, the next key that does exist in the database."  So if we're on that "next
-				// key" it will not satisfy the predicate, but the keys previous to it might.
+			if src.numRead[shardIdx] <= 1 && mayRequireOneSkip(src.pred.Op) {
 				continue
 			}
 			// This and subsequent entries don't satisfy predicate
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
index 3330723..fd4bfb1 100644
--- a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
@@ -271,6 +271,51 @@
 	}, []common.Span{SIMPLE_TEST_SPANS[1], SIMPLE_TEST_SPANS[0]})
 }
 
+func TestQueries4(t *testing.T) {
+	t.Parallel()
+	htraceBld := &MiniHTracedBuilder{Name: "TestQueries4",
+		WrittenSpans: make(chan *common.Span, 100)}
+	ht, err := htraceBld.Build()
+	if err != nil {
+		panic(err)
+	}
+	defer ht.Close()
+	createSpans(SIMPLE_TEST_SPANS, ht.Store)
+	if ht.Store.GetStatistics().NumSpansWritten < uint64(len(SIMPLE_TEST_SPANS)) {
+		t.Fatal()
+	}
+	testQuery(t, ht, &common.Query{
+		Predicates: []common.Predicate{
+			common.Predicate{
+				Op:    common.GREATER_THAN,
+				Field: common.BEGIN_TIME,
+				Val:   "125",
+			},
+		},
+		Lim: 5,
+	}, []common.Span{SIMPLE_TEST_SPANS[2]})
+	testQuery(t, ht, &common.Query{
+		Predicates: []common.Predicate{
+			common.Predicate{
+				Op:    common.GREATER_THAN_OR_EQUALS,
+				Field: common.DESCRIPTION,
+				Val:   "openFd",
+			},
+		},
+		Lim: 2,
+	}, []common.Span{SIMPLE_TEST_SPANS[1], SIMPLE_TEST_SPANS[2]})
+	testQuery(t, ht, &common.Query{
+		Predicates: []common.Predicate{
+			common.Predicate{
+				Op:    common.GREATER_THAN,
+				Field: common.DESCRIPTION,
+				Val:   "openFd",
+			},
+		},
+		Lim: 2,
+	}, []common.Span{SIMPLE_TEST_SPANS[2]})
+}
+
 func BenchmarkDatastoreWrites(b *testing.B) {
 	htraceBld := &MiniHTracedBuilder{Name: "BenchmarkDatastoreWrites",
 		WrittenSpans: make(chan *common.Span, b.N)}