You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by mr...@apache.org on 2018/10/23 03:17:44 UTC

[06/16] usergrid git commit: Add ability to query by name/uuid without using Elasticsearch

Add ability to query by name/uuid without using Elasticsearch

example ql: "direct widget1,56d8fac2-39ef-11e8-b467-0ed5f89f718b,widget3"


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/57afad20
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/57afad20
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/57afad20

Branch: refs/heads/master
Commit: 57afad20a6c152550cc7f9fb3d35d445dbb90a25
Parents: 38c7313
Author: Mike Dunker <md...@google.com>
Authored: Fri Apr 6 16:05:56 2018 -0700
Committer: Keyur Karnik <ke...@gmail.com>
Committed: Tue Aug 28 16:41:44 2018 -0700

----------------------------------------------------------------------
 .../pipeline/PipelineContext.java               |  20 +++-
 .../search/AbstractElasticSearchFilter.java     |   2 +-
 .../read/search/CandidateEntityFilter.java      |  86 ++++++++++++---
 .../index/query/tree/CpQueryFilter.g            | 110 ++++++++++++-------
 .../src/main/java/CpQueryFilter.tokens          |  67 +++++------
 .../persistence/index/CandidateResult.java      |  97 +++++++++++++++-
 .../persistence/index/CandidateResults.java     |  12 ++
 .../usergrid/persistence/index/EntityIndex.java |  20 ++++
 .../usergrid/persistence/index/IndexFig.java    |   6 +
 .../TooManyDirectEntitiesException.java         |  49 +++++++++
 .../index/impl/EsEntityIndexImpl.java           |  56 ++++++++--
 .../persistence/index/impl/EsQueryVistor.java   |  58 +++++++---
 .../persistence/index/query/ParsedQuery.java    |   5 +
 .../index/query/tree/DirectOperand.java         |  82 ++++++++++++++
 .../persistence/index/query/tree/IdLiteral.java |  46 ++++++++
 .../index/query/tree/QueryVisitor.java          |  18 ++-
 16 files changed, 616 insertions(+), 118 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/PipelineContext.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/PipelineContext.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/PipelineContext.java
index 88b5001..7aa614a 100644
--- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/PipelineContext.java
+++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/PipelineContext.java
@@ -28,6 +28,10 @@ import org.apache.usergrid.corepersistence.pipeline.cursor.ResponseCursor;
 import org.apache.usergrid.persistence.core.scope.ApplicationScope;
 
 import com.google.common.base.Optional;
+import org.apache.usergrid.persistence.index.query.ParsedQuery;
+import org.apache.usergrid.persistence.index.query.ParsedQueryBuilder;
+
+import static org.apache.usergrid.persistence.Query.MAX_LIMIT;
 
 
 /**
@@ -43,16 +47,24 @@ public class PipelineContext {
     // it can happen if ES was not updated or has yet to be updated.
     private final boolean keepStaleEntries;
     private String query;
+    private ParsedQuery parsedQuery;
 
 
     public PipelineContext( final ApplicationScope applicationScope, final RequestCursor requestCursor, final int limit, final int id, boolean keepStaleEntries, String query ) {
 
         this.applicationScope = applicationScope;
-        this.requestCursor = requestCursor;
-        this.limit = limit;
         this.id = id;
         this.keepStaleEntries = keepStaleEntries;
         this.query = query;
+        this.parsedQuery = ParsedQueryBuilder.build(query);
+        if (parsedQuery != null && parsedQuery.isDirectQuery()) {
+            // for direct query, use no limit or cursor
+            this.limit = MAX_LIMIT + 1;
+            this.requestCursor = new RequestCursor(Optional.absent());
+        } else {
+            this.limit = limit;
+            this.requestCursor = requestCursor;
+        }
     }
 
 
@@ -98,4 +110,8 @@ public class PipelineContext {
         return query;
     }
 
+    public ParsedQuery getParsedQuery() {
+        return parsedQuery;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/AbstractElasticSearchFilter.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/AbstractElasticSearchFilter.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/AbstractElasticSearchFilter.java
index 7a46507..cc409b0 100644
--- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/AbstractElasticSearchFilter.java
+++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/AbstractElasticSearchFilter.java
@@ -129,7 +129,7 @@ public abstract class AbstractElasticSearchFilter extends AbstractPathFilter<Id,
 
                     try {
                         final CandidateResults candidateResults =
-                            applicationEntityIndex.search( searchEdge, searchTypes, query, limit, currentOffSet,
+                            applicationEntityIndex.search( searchEdge, searchTypes, pipelineContext.getParsedQuery(), limit, currentOffSet,
                                 propertiesWithType, analyzeOnly, returnQuery);
 
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java
index f9c93b0..955b419 100644
--- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java
+++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java
@@ -24,11 +24,14 @@ import java.util.*;
 import java.util.logging.Filter;
 
 import org.apache.usergrid.corepersistence.index.IndexLocationStrategyFactory;
+import org.apache.usergrid.persistence.Schema;
 import org.apache.usergrid.persistence.index.*;
 import org.apache.usergrid.persistence.index.impl.IndexProducer;
+import org.apache.usergrid.persistence.model.entity.SimpleId;
 import org.apache.usergrid.persistence.model.field.DistanceField;
 import org.apache.usergrid.persistence.model.field.EntityObjectField;
 import org.apache.usergrid.persistence.model.field.Field;
+import org.apache.usergrid.persistence.model.field.StringField;
 import org.apache.usergrid.persistence.model.field.value.EntityObject;
 import org.apache.usergrid.utils.DateUtils;
 import org.slf4j.Logger;
@@ -100,6 +103,7 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
 
         boolean keepStaleEntries = pipelineContext.getKeepStaleEntries();
         String query = pipelineContext.getQuery();
+        boolean isDirectQuery = pipelineContext.getParsedQuery().isDirectQuery();
 
         //buffer them to get a page size we can make 1 network hop
         final Observable<FilterResult<Entity>> searchIdSetObservable =
@@ -108,7 +112,12 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
             //load them
             .flatMap( candidateResults -> {
 
-                //flatten toa list of ids to load
+                if (isDirectQuery) {
+                    // add ids for direct query
+                    updateDirectQueryCandidateResults(entityCollectionManager, candidateResults);
+                }
+
+                //flatten to a list of ids to load
                 final Observable<List<Candidate>> candidates =
                     Observable.from(candidateResults)
                         .map(filterResultCandidate -> filterResultCandidate.getValue()).toList();
@@ -119,12 +128,13 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
                         Observable<EntitySet> entitySets = Observable.from(candidatesList)
                             .map(candidateEntry -> candidateEntry.getCandidateResult().getId()).toList()
                             .flatMap(idList -> entityCollectionManager.load(idList));
-                        //now we have a collection, validate our canidate set is correct.
+                        //now we have a collection, validate our candidate set is correct.
                         return entitySets.map(
                             entitySet -> new EntityVerifier(
                                 applicationIndex.createBatch(), entitySet, candidateResults,indexProducer)
                         )
-                            .doOnNext(entityCollector -> entityCollector.merge(keepStaleEntries, query))
+                            .doOnNext(entityCollector -> entityCollector.merge(keepStaleEntries, query,
+                                isDirectQuery))
                             .flatMap(entityCollector -> Observable.from(entityCollector.getResults()))
                             .map(entityFilterResult -> {
                                 final Entity entity = entityFilterResult.getValue();
@@ -223,6 +233,28 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
 
 
     /**
+     * Update direct query candidates to add IDs.
+     */
+    private void updateDirectQueryCandidateResults(
+        EntityCollectionManager entityCollectionManager, List<FilterResult<Candidate>> candidatesList) {
+        for (FilterResult<Candidate> filterCandidate : candidatesList) {
+            Candidate candidate = filterCandidate.getValue();
+            CandidateResult candidateResult = candidate.getCandidateResult();
+            String entityType = candidateResult.getDirectEntityType();
+            Id entityId = null;
+            if (candidateResult.isDirectQueryName()) {
+                entityId = entityCollectionManager.getIdField( entityType,
+                    new StringField( Schema.PROPERTY_NAME, candidateResult.getDirectEntityName() ) )
+                    .toBlocking() .lastOrDefault( null );
+            } else if (candidateResult.isDirectQueryUUID()) {
+                entityId = new SimpleId(candidateResult.getDirectEntityUUID(), entityType);
+            }
+            filterCandidate.getValue().getCandidateResult().setId(entityId);
+        }
+    }
+
+
+    /**
      * Our collector to collect entities.  Not quite a true collector, but works within our operational
      * flow as this state is mutable and difficult to represent functionally
      */
@@ -252,16 +284,30 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
         /**
          * Merge our candidates and our entity set into results
          */
-        public void merge(boolean keepStaleEntries, String query) {
-
-            filterDuplicateCandidates(query);
-
-            for ( final FilterResult<Candidate> candidateResult : dedupedCandidateResults ) {
-                validate( candidateResult , keepStaleEntries, query);
+        public void merge(boolean keepStaleEntries, String query, boolean isDirectQuery) {
+
+            if (!isDirectQuery) {
+                filterDuplicateCandidates(query);
+            } else {
+                // remove direct query duplicates
+                Set<UUID> foundUUIDs = new HashSet<>();
+                for (FilterResult<Candidate> candidateFilterResult : candidateResults) {
+                    UUID uuid = candidateFilterResult.getValue().getCandidateResult().getId().getUuid();
+                    if (!foundUUIDs.contains(uuid)) {
+                        dedupedCandidateResults.add(candidateFilterResult);
+                        foundUUIDs.add(uuid);
+                    }
+                }
             }
 
-            indexProducer.put(batch.build()).toBlocking().lastOrDefault(null); // want to rethrow if batch fails
+            for (final FilterResult<Candidate> candidateResult : dedupedCandidateResults) {
+                validate(candidateResult, keepStaleEntries, query, isDirectQuery);
+            }
 
+            // no index requests made for direct query, so no need to modify index
+            if (!isDirectQuery) {
+                indexProducer.put(batch.build()).toBlocking().lastOrDefault(null); // want to rethrow if batch fails
+            }
         }
 
 
@@ -354,14 +400,15 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
         }
 
 
-        private void validate( final FilterResult<Candidate> filterResult, boolean keepStaleEntries, String query ) {
+        private void validate( final FilterResult<Candidate> filterResult, boolean keepStaleEntries, String query,
+                               boolean isDirectQuery) {
 
             final Candidate candidate = filterResult.getValue();
             final CandidateResult candidateResult = candidate.getCandidateResult();
             final boolean isGeo = candidateResult instanceof GeoCandidateResult;
             final SearchEdge searchEdge = candidate.getSearchEdge();
             final Id candidateId = candidateResult.getId();
-            final UUID candidateVersion = candidateResult.getVersion();
+            UUID candidateVersion = candidateResult.getVersion();
 
 
             final MvccEntity entity = entitySet.getEntity( candidateId );
@@ -369,9 +416,14 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
 
             //doesn't exist warn and drop
             if ( entity == null ) {
-                logger.warn(
-                    "Searched and received candidate with entityId {} and version {}, yet was not found in cassandra.  Ignoring since this could be a region sync issue",
-                    candidateId, candidateVersion );
+                if (!isDirectQuery) {
+                    logger.warn(
+                        "Searched and received candidate with entityId {} and version {}, yet was not found in cassandra.  Ignoring since this could be a region sync issue",
+                        candidateId, candidateVersion);
+                } else {
+                    logger.warn(
+                        "Direct query for entityId {} was not found in cassandra, query=[{}]", candidateId, query);
+                }
 
 
                 //TODO trigger an audit after a fail count where we explicitly try to repair from other regions
@@ -382,6 +434,10 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate
 
 
             final UUID databaseVersion = entity.getVersion();
+            if (isDirectQuery) {
+                // use returned (latest) version for direct query
+                candidateVersion = databaseVersion;
+            }
             final Id entityId = entity.getId();
 
             // The entity is marked as deleted

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/antlr3/org/apache/usergrid/persistence/index/query/tree/CpQueryFilter.g
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/antlr3/org/apache/usergrid/persistence/index/query/tree/CpQueryFilter.g b/stack/corepersistence/queryindex/src/main/antlr3/org/apache/usergrid/persistence/index/query/tree/CpQueryFilter.g
index 605aed4..d75a617 100644
--- a/stack/corepersistence/queryindex/src/main/antlr3/org/apache/usergrid/persistence/index/query/tree/CpQueryFilter.g
+++ b/stack/corepersistence/queryindex/src/main/antlr3/org/apache/usergrid/persistence/index/query/tree/CpQueryFilter.g
@@ -112,7 +112,7 @@ EQ  : '=' | 'eq';
 
 GT  : '>' | 'gt';
 
-GTE : '>=' |  'gte';  
+GTE : '>=' |  'gte';
 
 
 //keywords before var ids
@@ -134,11 +134,14 @@ WITHIN : ('W'|'w')('I'|'i')('T'|'t')('H'|'h')('I'|'i')('N'|'n');
 
 OF : ('O'|'o')('F'|'f');
 
-UUID :  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
-  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-' 
-  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-' 
-  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-' 
-  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-' 
+DIRECT : ('D'|'d')('I'|'i')('R'|'r')('E'|'e')('C'|'c')('T'|'t');
+
+UUID :
+  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
+  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-'
+  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-'
+  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-'
+  HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT '-'
   HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
   HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
   HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
@@ -156,18 +159,18 @@ FLOAT
     |   '.' ('0'..'9')+ EXPONENT?
     |   ('0'..'9')+ EXPONENT)
     ;
-    
+
 STRING
     :  '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
     ;
 
 
-    
+
 WS : (' ' | '\t' | '\n' | '\r' | '\f')+  {$channel=HIDDEN;};
 
 
 
-    
+
 
 
 
@@ -208,12 +211,19 @@ UNICODE_ESC
 
 
 
-property :	ID<Property>;
+property :
+  ID<Property>
+  | ASC<Property>
+  | DESC<Property>
+  | CONTAINS<Property>
+  | WITHIN<Property>
+  | DIRECT<Property>
+  ;
 
 containsproperty : ID<ContainsProperty>;
 
 withinproperty : ID<WithinProperty>;
-	
+
 booleanliteral: BOOLEAN<BooleanLiteral>;
 
 
@@ -225,70 +235,96 @@ uuidliteral :
 
 stringliteral :
   STRING<StringLiteral>;
-  
+
 floatliteral :
   FLOAT<FloatLiteral> ;
 
-//We delegate to each sub class literal so we can get each type	
-value : 
+idliteral :
+  ID<IdLiteral> ;
+
+directnameliteral :
+  ID<IdLiteral> ;
+
+directuuidliteral :
+  UUID<UUIDLiteral>;
+
+//We delegate to each sub class literal so we can get each type
+value :
   booleanliteral
   | longliteral
   | uuidliteral
   | stringliteral
   | floatliteral
   ;
-  
 
+directidliteral :
+  directnameliteral
+  | directuuidliteral
+  ;
 
 //Every operand returns with the name of 'op'.  This is used because all subtrees require operands,
-//this allows us to link the java code easily by using the same name as a converntion
+//this allows us to link the java code easily by using the same name as a convention
 
 //begin search expressions
-  
-//mathmatical equality operations
+
+//mathematical equality operations
 equalityop :
   property LT<LessThan>^ value
   |property LTE<LessThanEqual>^ value
   |property EQ<Equal>^ value
   |property GT<GreaterThan>^ value
   |property GTE<GreaterThanEqual>^ value
-  ; 
+  ;
 
 //geo location search
 locationop :
   withinproperty WITHIN<WithinOperand>^ (floatliteral|longliteral) OF! (floatliteral|longliteral) ','! (floatliteral|longliteral);
-  
+
 //string search
 containsop :
   containsproperty CONTAINS<ContainsOperand>^ stringliteral;
 
+directidlist :
+  directidliteral (','! directidliteral)*;
+
 //
 operation :
- '('! expression ')'!
-   | equalityop 
-   | locationop 
-   | containsop 
+ '('! orexp ')'!
+   | equalityop
+   | locationop
+   | containsop
    ;
 
 //negations of expressions
 notexp :
 //only link if we have the not
- NOT<NotOperand>^ operation  
- |operation 
+ NOT<NotOperand>^ operation
+ |operation
  ;
 
 //and expressions contain operands.  These should always be closer to the leaves of a tree, it allows
 //for faster result intersection sooner in the query execution
 andexp :
  notexp (AND<AndOperand>^ notexp )*;
- 
- 
+
+
 //or expression should always be after AND expressions.  This will give us a smaller result set to union when evaluating trees
 //also a root level expression
-expression :
+orexp :
  andexp (OR<OrOperand>^ andexp )*;
 
 
+directexp :
+ DIRECT<DirectOperand>^ directidlist  ;
+
+
+// can use DIRECT {UUID|ID}+ at the root
+expression :
+  directexp
+  | orexp
+  ;
+
+
 
 //end expressions
 
@@ -300,14 +336,14 @@ direction  : (ASC | DESC);
 //order clause
 order
   : (property direction?){
-		String property = $property.text; 
+		String property = $property.text;
 		String direction = $direction.text;
 		parsedQuery.addSort(new SortPredicate(property, direction));
-    
+
   };
 
 //end order clauses
-  
+
 //Begin select clauses
 
 select_subject
@@ -317,7 +353,7 @@ select_subject
 
 };
 
- 
+
 
 select_assign
   : target=ID ':' source=ID {
@@ -326,9 +362,9 @@ select_assign
 
 };
 
-select_expr 
-  : ('*' | select_subject (',' select_subject) * | '{' select_assign (',' select_assign) * '}');  
-   
+select_expr
+  : ('*' | select_subject (',' select_subject) * | '{' select_assign (',' select_assign) * '}');
+
 //end select clauses
 
 ql returns [ParsedQuery parsedQuery]
@@ -337,7 +373,7 @@ ql returns [ParsedQuery parsedQuery]
   if($expression.tree instanceof Operand){
     parsedQuery.setRootOperand((Operand)$expression.tree);
   }
-  
+
   retval.parsedQuery = parsedQuery;
 
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/CpQueryFilter.tokens
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/CpQueryFilter.tokens b/stack/corepersistence/queryindex/src/main/java/CpQueryFilter.tokens
index 53a0817..a9e7106 100644
--- a/stack/corepersistence/queryindex/src/main/java/CpQueryFilter.tokens
+++ b/stack/corepersistence/queryindex/src/main/java/CpQueryFilter.tokens
@@ -1,4 +1,3 @@
-T__31=31
 T__32=32
 T__33=33
 T__34=34
@@ -8,40 +7,42 @@ T__37=37
 T__38=38
 T__39=39
 T__40=40
+T__41=41
 AND=4
 ASC=5
 BOOLEAN=6
 CONTAINS=7
 DESC=8
-EQ=9
-ESC_SEQ=10
-EXPONENT=11
-FALSE=12
-FLOAT=13
-GT=14
-GTE=15
-HEX_DIGIT=16
-ID=17
-LONG=18
-LT=19
-LTE=20
-NOT=21
-OCTAL_ESC=22
-OF=23
-OR=24
-STRING=25
-TRUE=26
-UNICODE_ESC=27
-UUID=28
-WITHIN=29
-WS=30
-'('=31
-')'=32
-'*'=33
-','=34
-':'=35
-'order by'=36
-'select'=37
-'where'=38
-'{'=39
-'}'=40
+DIRECT=9
+EQ=10
+ESC_SEQ=11
+EXPONENT=12
+FALSE=13
+FLOAT=14
+GT=15
+GTE=16
+HEX_DIGIT=17
+ID=18
+LONG=19
+LT=20
+LTE=21
+NOT=22
+OCTAL_ESC=23
+OF=24
+OR=25
+STRING=26
+TRUE=27
+UNICODE_ESC=28
+UUID=29
+WITHIN=30
+WS=31
+'('=32
+')'=33
+'*'=34
+','=35
+':'=36
+'order by'=37
+'select'=38
+'where'=39
+'{'=40
+'}'=41

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResult.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResult.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResult.java
index 9b1898c..65c20f0 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResult.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResult.java
@@ -30,14 +30,59 @@ import org.apache.usergrid.persistence.model.entity.Id;
  * An instance of a candidate result
  */
 public class CandidateResult implements EntityVersion {
-    private final Id entityId;
+    private Id entityId;
     private final UUID entityVersion;
     private final String docId;
+    private final String directEntityName;
+    private final UUID directEntityUUID;
+    private final String directEntityType;
 
     public CandidateResult( Id entityId, UUID entityVersion, String docId ) {
         this.entityId = entityId;
         this.entityVersion = entityVersion;
         this.docId = docId;
+        this.directEntityName = null;
+        this.directEntityUUID = null;
+        this.directEntityType = null;
+    }
+
+    public CandidateResult( Id entityId, CandidateResult sourceResult ) {
+        this.entityId = entityId;
+        this.entityVersion = sourceResult.entityVersion;
+        this.docId = sourceResult.docId;
+        this.directEntityName = sourceResult.directEntityName;
+        this.directEntityUUID = sourceResult.directEntityUUID;
+        this.directEntityType = sourceResult.directEntityType;
+    }
+
+    // direct query by name before resolution
+    public CandidateResult( String entityType, String directEntityName ) {
+        this.directEntityName = directEntityName;
+        this.directEntityUUID = null;
+        this.directEntityType = entityType;
+        this.entityId = null;
+        this.entityVersion = null;
+        this.docId = null;
+    }
+
+    // direct query by UUID before resolution
+    public CandidateResult( String entityType, UUID directEntityUUID ) {
+        this.directEntityUUID = directEntityUUID;
+        this.directEntityName = null;
+        this.directEntityType = entityType;
+        this.entityId = null;
+        this.entityVersion = null;
+        this.docId = null;
+    }
+
+    public boolean isDirectQueryName() {
+        return directEntityName != null;
+    }
+    public boolean isDirectQueryUUID() {
+        return directEntityUUID != null;
+    }
+    public boolean isDirectQuery() {
+        return isDirectQueryName() || isDirectQueryUUID();
     }
 
     @Override
@@ -54,25 +99,69 @@ public class CandidateResult implements EntityVersion {
         return docId;
     }
 
+    public String getDirectEntityName() {
+        return directEntityName;
+    }
+
+    public UUID getDirectEntityUUID() {
+        return directEntityUUID;
+    }
+
+    public String getDirectEntityType() {
+        return directEntityType;
+    }
+
+    // use to set id for direct query after resolution
+    public void setId(Id entityId) {
+        this.entityId = entityId;
+    }
+
 
     @Override
     public boolean equals( final Object o ) {
         if ( this == o ) {
             return true;
         }
+        if ( o == null ) {
+            return false;
+        }
         if ( !( o instanceof CandidateResult ) ) {
             return false;
         }
 
         final CandidateResult that = ( CandidateResult ) o;
 
-        if ( !entityId.equals( that.entityId ) ) {
+        if ( entityId == null && that.entityId != null) {
+            return false;
+        }
+        if ( entityId != null && !entityId.equals( that.entityId ) ) {
+            return false;
+        }
+        if ( entityVersion == null && that.entityVersion != null) {
+            return false;
+        }
+        if ( entityVersion != null && !entityVersion.equals( that.entityVersion ) ) {
+            return false;
+        }
+        if ( docId == null && that.docId != null) {
+            return false;
+        }
+        if ( docId != null && !docId.equals( that.docId ) ) {
+            return false;
+        }
+        if ( directEntityUUID != that.directEntityUUID ) {
+            return false;
+        }
+        if ( directEntityName == null && that.directEntityName != null) {
+            return false;
+        }
+        if ( directEntityName != null && !directEntityName.equals( that.directEntityName ) ) {
             return false;
         }
-        if ( !entityVersion.equals( that.entityVersion ) ) {
+        if ( directEntityType == null && that.directEntityType != null) {
             return false;
         }
-        if ( !docId.equals( that.docId ) ) {
+        if ( directEntityType != null && !directEntityType.equals( that.directEntityType ) ) {
             return false;
         }
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResults.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResults.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResults.java
index b8f87b6..3a69706 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResults.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/CandidateResults.java
@@ -38,11 +38,19 @@ public class CandidateResults implements Iterable<CandidateResult> {
 
     private final List<CandidateResult> candidates;
     private final Collection<SelectFieldMapping> getFieldMappings;
+    private final boolean isDirectQuery;
 
     public CandidateResults( List<CandidateResult> candidates, final Collection<SelectFieldMapping> getFieldMappings) {
+        this(candidates, getFieldMappings, false);
+    }
+
+    public CandidateResults(List<CandidateResult> candidates,
+                            final Collection<SelectFieldMapping> getFieldMappings,
+                            final boolean isDirectQuery) {
         this.candidates = candidates;
         this.getFieldMappings = getFieldMappings;
         offset = Optional.absent();
+        this.isDirectQuery = isDirectQuery;
     }
 
 
@@ -82,6 +90,10 @@ public class CandidateResults implements Iterable<CandidateResult> {
         return getFieldMappings;
     }
 
+    public boolean isDirectQuery() {
+        return this.isDirectQuery;
+    }
+
 
     /**
      * Get the candidates

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/EntityIndex.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/EntityIndex.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/EntityIndex.java
index afcfdd7..ebe6824 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/EntityIndex.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/EntityIndex.java
@@ -22,6 +22,7 @@ package org.apache.usergrid.persistence.index;
 
 import org.apache.usergrid.persistence.core.CPManager;
 import org.apache.usergrid.persistence.core.util.Health;
+import org.apache.usergrid.persistence.index.query.ParsedQuery;
 import org.apache.usergrid.persistence.model.entity.Id;
 import rx.Observable;
 
@@ -140,6 +141,25 @@ public interface EntityIndex extends CPManager {
                             final int limit, final int offset, final Map<String, Class> fieldsWithType,
                             final boolean analyzeOnly, final boolean returnQuery);
 
+    /**
+     * Search on every document in the specified search edge.  Also search by the types if specified
+     *
+     * @param searchEdge        The edge to search on
+     * @param searchTypes       The search types to search
+     * @param parsedQuery       The parsed query to execute
+     * @param limit             The limit of values to return
+     * @param offset            The offset to query on
+     * @param fieldsWithType    An optional param that allows the caller to provide schema related info which might
+     *                          relate to data in the query, such as sort predicate types
+     * @param analyzeOnly       This optional param will instruct the query processing to only analyze the query and
+     *                          provide info but not actually execute the query.
+     * @param returnQuery       This optional param will cause the index query to be returned instead of run.
+     * @return
+     */
+    CandidateResults search(final SearchEdge searchEdge, final SearchTypes searchTypes, final ParsedQuery parsedQuery,
+                            final int limit, final int offset, final Map<String, Class> fieldsWithType,
+                            final boolean analyzeOnly, final boolean returnQuery);
+
 
     /**
      * Same as search, just iterates all documents that match the index edge exactly.

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/IndexFig.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/IndexFig.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/IndexFig.java
index 9b42da3..493a722 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/IndexFig.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/IndexFig.java
@@ -72,6 +72,8 @@ public interface IndexFig extends GuicyFig {
 
     String USERGRID_QUERYANALYZER_ENFORCE = "usergrid.queryanalyzer.enforce";
 
+    String DIRECT_QUERY_MAX_ITEMS = "direct.query.max.items";
+
 
 
 
@@ -238,4 +240,8 @@ public interface IndexFig extends GuicyFig {
     @Default("false")
     @Key( USERGRID_QUERYANALYZER_ENFORCE )
     boolean enforceQueryBreaker();
+
+    @Default("1000")
+    @Key( DIRECT_QUERY_MAX_ITEMS )
+    int directQueryMaxItems();
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/exceptions/TooManyDirectEntitiesException.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/exceptions/TooManyDirectEntitiesException.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/exceptions/TooManyDirectEntitiesException.java
new file mode 100644
index 0000000..b3f2052
--- /dev/null
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/exceptions/TooManyDirectEntitiesException.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.persistence.index.exceptions;
+
+
+/**
+ * Thrown when the user attempts to perform a "direct" operation with more than the max limit number of entities
+ */
+public class TooManyDirectEntitiesException extends QueryException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+    final private int numberItemsRequested;
+    final private int maxItemsAllowed;
+
+
+    public TooManyDirectEntitiesException(int numberItemsRequested, int maxItemsAllowed) {
+        super( "Exceeded maximum number of direct entities requested: "
+                + Integer.toString(numberItemsRequested) + " requested, limit is " + Integer.toString(maxItemsAllowed));
+        this.numberItemsRequested = numberItemsRequested;
+        this.maxItemsAllowed = maxItemsAllowed;
+    }
+
+
+    public int getNumberItemsRequested() {
+        return numberItemsRequested;
+    }
+
+
+    public int getMaxNumberItems() {
+        return maxItemsAllowed;
+    }
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java
index e3121e1..6dfb2ae 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java
@@ -36,11 +36,9 @@ import org.apache.usergrid.persistence.core.util.Health;
 import org.apache.usergrid.persistence.core.util.StringUtils;
 import org.apache.usergrid.persistence.index.*;
 import org.apache.usergrid.persistence.index.ElasticSearchQueryBuilder.SearchRequestBuilderStrategyV2;
-import org.apache.usergrid.persistence.index.exceptions.IndexException;
-import org.apache.usergrid.persistence.index.exceptions.QueryAnalyzerException;
-import org.apache.usergrid.persistence.index.exceptions.QueryAnalyzerEnforcementException;
-import org.apache.usergrid.persistence.index.exceptions.QueryReturnException;
+import org.apache.usergrid.persistence.index.exceptions.*;
 import org.apache.usergrid.persistence.index.migration.IndexDataVersions;
+import org.apache.usergrid.persistence.index.query.Identifier;
 import org.apache.usergrid.persistence.index.query.ParsedQuery;
 import org.apache.usergrid.persistence.index.query.ParsedQueryBuilder;
 import org.apache.usergrid.persistence.index.query.SortPredicate;
@@ -437,23 +435,38 @@ public class EsEntityIndexImpl implements EntityIndex,VersionedData {
     public CandidateResults search( final SearchEdge searchEdge, final SearchTypes searchTypes, final String query,
                                     final int limit, final int offset, final Map<String, Class> fieldsWithType,
                                     final boolean analyzeOnly, final boolean returnQuery ) {
+        Preconditions.checkNotNull( query, "query cannot be null" );
+        final ParsedQuery parsedQuery = ParsedQueryBuilder.build(query);
+
+        return search(searchEdge, searchTypes, parsedQuery, limit, offset, fieldsWithType, analyzeOnly, returnQuery);
+    }
+
+    public CandidateResults search( final SearchEdge searchEdge, final SearchTypes searchTypes, final ParsedQuery parsedQuery,
+                                    final int limit, final int offset, final Map<String, Class> fieldsWithType,
+                                    final boolean analyzeOnly, final boolean returnQuery ) {
 
         IndexValidationUtils.validateSearchEdge(searchEdge);
         Preconditions.checkNotNull(searchTypes, "searchTypes cannot be null");
-        Preconditions.checkNotNull( query, "query cannot be null" );
         Preconditions.checkArgument( limit > 0, "limit must be > 0" );
 
 
         SearchResponse searchResponse;
 
-        final ParsedQuery parsedQuery = ParsedQueryBuilder.build(query);
-
         if ( parsedQuery == null ){
             throw new IllegalArgumentException("a null query string cannot be parsed");
         }
 
         final QueryVisitor visitor = visitParsedQuery(parsedQuery);
 
+        List<Identifier> directIdentifiers = visitor.getDirectIdentifiers();
+        if (directIdentifiers != null && directIdentifiers.size() > 0) {
+            // this is a direct query
+            if (directIdentifiers.size() > indexFig.directQueryMaxItems()) {
+                throw new TooManyDirectEntitiesException(directIdentifiers.size(), indexFig.directQueryMaxItems());
+            }
+            return buildCandidateResultsForDirectQuery(directIdentifiers, parsedQuery, searchTypes);
+        }
+
         boolean hasGeoSortPredicates = false;
 
         for (SortPredicate sortPredicate : parsedQuery.getSortPredicates() ){
@@ -644,7 +657,7 @@ public class EsEntityIndexImpl implements EntityIndex,VersionedData {
 
 
     /**
-     * Parse the results and return the canddiate results
+     * Parse the results and return the candidate results
      */
     private CandidateResults parseResults( final SearchResponse searchResponse, final ParsedQuery query,
                                            final int limit, final int from, boolean hasGeoSortPredicates ) {
@@ -677,6 +690,33 @@ public class EsEntityIndexImpl implements EntityIndex,VersionedData {
         return candidateResults;
     }
 
+
+    /**
+     * Build CandidateResults from direct query
+     */
+    private CandidateResults buildCandidateResultsForDirectQuery(final List<Identifier> directIdentifiers,
+                                                                 final ParsedQuery query,
+                                                                 final SearchTypes searchTypes) {
+        Preconditions.checkArgument(searchTypes.getTypes().length > 0, "Search type required");
+        String entityType = searchTypes.getTypes()[0];
+
+        List<CandidateResult> candidates = new ArrayList<>(directIdentifiers.size());
+
+        for (Identifier id : directIdentifiers) {
+            CandidateResult candidateResult = null;
+            if (id.isUUID()) {
+                candidateResult = new CandidateResult(entityType, id.getUUID());
+            } else if (id.isName()) {
+                candidateResult = new CandidateResult(entityType, id.getName());
+            }
+            candidates.add(candidateResult);
+        }
+
+        return new CandidateResults(candidates, query.getSelectFieldMappings(), true);
+    }
+
+
+
     private List<CandidateResult> aggregateScrollResults(List<CandidateResult> candidates,
                                                          final SearchResponse searchResponse, final UUID markedVersion){
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsQueryVistor.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsQueryVistor.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsQueryVistor.java
index 4dd0d24..7f08ee3 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsQueryVistor.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsQueryVistor.java
@@ -19,9 +19,10 @@
 package org.apache.usergrid.persistence.index.impl;
 
 
-import java.util.Stack;
-import java.util.UUID;
+import java.util.*;
 
+import org.apache.usergrid.persistence.index.query.Identifier;
+import org.apache.usergrid.persistence.index.query.tree.*;
 import org.elasticsearch.common.geo.GeoDistance;
 import org.elasticsearch.common.unit.DistanceUnit;
 import org.elasticsearch.index.query.BoolFilterBuilder;
@@ -44,17 +45,6 @@ import org.slf4j.LoggerFactory;
 import org.apache.usergrid.persistence.index.exceptions.IndexException;
 import org.apache.usergrid.persistence.index.exceptions.NoFullTextIndexException;
 import org.apache.usergrid.persistence.index.exceptions.NoIndexException;
-import org.apache.usergrid.persistence.index.query.tree.AndOperand;
-import org.apache.usergrid.persistence.index.query.tree.ContainsOperand;
-import org.apache.usergrid.persistence.index.query.tree.Equal;
-import org.apache.usergrid.persistence.index.query.tree.GreaterThan;
-import org.apache.usergrid.persistence.index.query.tree.GreaterThanEqual;
-import org.apache.usergrid.persistence.index.query.tree.LessThan;
-import org.apache.usergrid.persistence.index.query.tree.LessThanEqual;
-import org.apache.usergrid.persistence.index.query.tree.NotOperand;
-import org.apache.usergrid.persistence.index.query.tree.OrOperand;
-import org.apache.usergrid.persistence.index.query.tree.QueryVisitor;
-import org.apache.usergrid.persistence.index.query.tree.WithinOperand;
 
 import com.google.common.base.Optional;
 
@@ -79,6 +69,13 @@ public class EsQueryVistor implements QueryVisitor {
 
     private final GeoSortFields geoSortFields = new GeoSortFields();
 
+    /**
+     * Query direct to C* bypassing ES
+     */
+    private final List<Identifier> directIdList = new ArrayList<>();
+
+
+
 
     @Override
     public void visit( AndOperand op ) throws IndexException {
@@ -323,6 +320,35 @@ public class EsQueryVistor implements QueryVisitor {
 
 
     @Override
+    public void visit( DirectOperand op ) {
+        List<Literal> idList = op.getDirectIds();
+
+        Set<Identifier> idSet = new HashSet<>();
+
+        for (Literal literal : idList) {
+
+            Identifier identifier = null;
+            if (literal instanceof IdLiteral) {
+                String name = ((IdLiteral)literal).getValue();
+                identifier = Identifier.fromName(name);
+            } else if (literal instanceof UUIDLiteral) {
+                UUID uuid = ((UUIDLiteral)literal).getValue();
+                identifier = Identifier.fromUUID(uuid);
+            }
+            // should only allow IdLiteral or UUIDLiteral, ignore if other
+
+            if (identifier != null) {
+                // ignore if already seen
+                if (!idSet.contains(identifier)) {
+                    directIdList.add(identifier);
+                    idSet.add(identifier);
+                }
+            }
+        }
+    }
+
+
+    @Override
     public void visit( LessThan op ) throws NoIndexException {
         final String name = op.getProperty().getValue().toLowerCase();
         final Object value = op.getLiteral().getValue();
@@ -472,6 +498,12 @@ public class EsQueryVistor implements QueryVisitor {
     }
 
 
+    @Override
+    public List<Identifier> getDirectIdentifiers() {
+        return directIdList;
+    }
+
+
     /**
      * Generate the field name term for the field name  for queries
      */

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/ParsedQuery.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/ParsedQuery.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/ParsedQuery.java
index 1cb3ba2..80ba6b1 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/ParsedQuery.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/ParsedQuery.java
@@ -34,6 +34,7 @@ import java.util.Set;
 
 import org.apache.usergrid.persistence.index.SelectFieldMapping;
 import org.apache.usergrid.persistence.index.exceptions.QueryParseException;
+import org.apache.usergrid.persistence.index.query.tree.DirectOperand;
 import org.apache.usergrid.persistence.index.query.tree.Operand;
 
 
@@ -206,4 +207,8 @@ public class ParsedQuery {
     public boolean isGeoQuery(){
         return getOriginalQuery().contains("location") && getOriginalQuery().contains("within");
     }
+
+    public boolean isDirectQuery() {
+        return rootOperand instanceof DirectOperand;
+    }
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/DirectOperand.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/DirectOperand.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/DirectOperand.java
new file mode 100644
index 0000000..796dc5e
--- /dev/null
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/DirectOperand.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.persistence.index.query.tree;
+
+
+import org.antlr.runtime.Token;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+
+public class DirectOperand extends Operand {
+
+    public DirectOperand(Token t ) {
+        super( t );
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.apache.usergrid.persistence.query.tree.Operand#visit(org.apache.usergrid.persistence
+     * .query.tree.QueryVisitor)
+     */
+    @Override
+    public void visit( QueryVisitor visitor ) {
+        visitor.visit( this );
+    }
+
+
+    /**
+     * @param name
+     * @param index
+     */
+    public void setDirectId( String name, int index ) {
+        setChild( index, new IdLiteral( name ) );
+    }
+
+
+    /**
+     * @param uuid
+     * @param index
+     */
+    public void setDirectId(UUID uuid, int index ) {
+        setChild( index, new UUIDLiteral( uuid ) );
+    }
+
+    /**
+     * @param index
+     * @return
+     */
+    public Literal getDirectId( int index ) {
+        return ( Literal ) this.children.get( index );
+    }
+
+    /**
+     * @return
+     */
+    public List<Literal> getDirectIds() {
+        List<Literal> ids = new ArrayList<>();
+        for (Object child : this.children) {
+            ids.add((Literal) child);
+        }
+        return ids;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/IdLiteral.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/IdLiteral.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/IdLiteral.java
new file mode 100644
index 0000000..1defdd3
--- /dev/null
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/IdLiteral.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  The ASF licenses this file to You
+ * under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.  For additional information regarding
+ * copyright in this work, please see the NOTICE file in the top level
+ * directory of this distribution.
+ */
+
+package org.apache.usergrid.persistence.index.query.tree;
+
+
+import org.antlr.runtime.ClassicToken;
+import org.antlr.runtime.Token;
+
+
+public class IdLiteral extends Literal<String> {
+
+    private String value;
+
+
+    public IdLiteral(Token t ) {
+        super( t );
+        value = t.getText();
+    }
+
+
+    public IdLiteral(String value ) {
+        super( new ClassicToken( 0, value ) );
+        this.value = value;
+    }
+
+
+    public String getValue() {
+        return this.value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/57afad20/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/QueryVisitor.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/QueryVisitor.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/QueryVisitor.java
index 273f23f..6c191cf 100644
--- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/QueryVisitor.java
+++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/query/tree/QueryVisitor.java
@@ -19,19 +19,16 @@
 package org.apache.usergrid.persistence.index.query.tree;
 
 
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
+import java.util.List;
 
 import org.apache.usergrid.persistence.index.exceptions.NoFullTextIndexException;
 import org.apache.usergrid.persistence.index.exceptions.NoIndexException;
 import org.apache.usergrid.persistence.index.exceptions.IndexException;
 import org.apache.usergrid.persistence.index.impl.GeoSortFields;
-import org.apache.usergrid.persistence.index.query.SortPredicate;
+import org.apache.usergrid.persistence.index.query.Identifier;
 
 import org.elasticsearch.index.query.FilterBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
 
 import com.google.common.base.Optional;
 
@@ -103,6 +100,12 @@ public interface QueryVisitor {
      */
     void visit( GreaterThanEqual op ) throws NoIndexException;
 
+    /**
+     * @param op
+     * @throws NoIndexException
+     */
+    void visit( DirectOperand op );
+
 
     /**
      * Return any filters created during parsing
@@ -126,4 +129,9 @@ public interface QueryVisitor {
      * @return The GeoSortFields  null safe
      */
     GeoSortFields getGeoSorts();
+
+    /**
+     * Return list of identifiers for direct query
+     */
+    List<Identifier> getDirectIdentifiers();
 }