6464import org .apache .hugegraph .iterator .BatchMapperIterator ;
6565import org .apache .hugegraph .iterator .ExtendableIterator ;
6666import org .apache .hugegraph .iterator .FilterIterator ;
67+ import org .apache .hugegraph .iterator .FlatMapperIterator ;
6768import org .apache .hugegraph .iterator .LimitIterator ;
6869import org .apache .hugegraph .iterator .MapperIterator ;
6970import org .apache .hugegraph .job .system .DeleteExpiredJob ;
@@ -548,24 +549,27 @@ public Number queryNumber(Query query) {
548549 boolean hasUpdate = this .hasUpdate ();
549550 Aggregate aggregate = query .aggregateNotNull ();
550551
551- // TODO: we can concat index-query results and tx uncommitted records.
552- if (hasUpdate ) {
553- E .checkArgument (!isConditionQuery ,
554- "It's not allowed to query by index when " +
555- "there are uncommitted records." );
552+ if (hasUpdate && (query .resultType () == HugeType .VERTEX ||
553+ query .resultType () == HugeType .EDGE )) {
554+ E .checkArgument (aggregate .func () == AggregateFunc .COUNT ,
555+ "The %s operator with uncommitted records " +
556+ "is not supported" ,
557+ aggregate .func ().string ());
558+ Query queryWithoutAggregate = query .copy ();
559+ queryWithoutAggregate .aggregate (null );
560+ Iterator <?> results = queryWithoutAggregate .resultType ().isVertex () ?
561+ this .queryVertices (queryWithoutAggregate ) :
562+ this .queryEdges (queryWithoutAggregate );
563+ return countAndClose (results );
556564 }
557565
558566 QueryList <Number > queries = this .optimizeQueries (query , q -> {
559567 boolean isIndexQuery = q instanceof IdQuery ;
560568 assert isIndexQuery || isConditionQuery || q == query ;
561- // Need to fall back if there are uncommitted records
562- boolean fallback = hasUpdate ;
569+ boolean fallback = false ;
563570 Number result ;
564571
565- if (fallback ) {
566- // Here just ignore it, and do fall back later
567- result = null ;
568- } else if (!isIndexQuery || !isConditionQuery ) {
572+ if (!isIndexQuery || !isConditionQuery ) {
569573 // It's a sysprop-query, let parent tx do it
570574 assert !fallback ;
571575 result = super .queryNumber (q );
@@ -591,9 +595,9 @@ public Number queryNumber(Query query) {
591595 assert q .resultType ().isVertex () || q .resultType ().isEdge ();
592596 // Reset aggregate to fallback and scan
593597 q .aggregate (null );
594- result = IteratorUtils . count (q .resultType ().isVertex () ?
595- this .queryVertices (q ) :
596- this .queryEdges (q ));
598+ result = countAndClose (q .resultType ().isVertex () ?
599+ this .queryVertices (q ) :
600+ this .queryEdges (q ));
597601 }
598602
599603 return new QueryResults <>(IteratorUtils .of (result ), q );
@@ -605,6 +609,19 @@ public Number queryNumber(Query query) {
605609 return aggregate .reduce (results .iterator ());
606610 }
607611
612+ private static long countAndClose (Iterator <?> results ) {
613+ try {
614+ long count = 0L ;
615+ while (results .hasNext ()) {
616+ results .next ();
617+ count ++;
618+ }
619+ return count ;
620+ } finally {
621+ CloseableIterator .closeIterator (results );
622+ }
623+ }
624+
608625 @ Watched (prefix = "graph" )
609626 public HugeVertex addVertex (Object ... keyValues ) {
610627 return this .addVertex (this .constructVertex (true , keyValues ));
@@ -819,7 +836,7 @@ public Iterator<Vertex> queryVertices() {
819836 public Iterator <Vertex > queryVertices (Query query ) {
820837 if (this .hasUpdate ()) {
821838 E .checkArgument (query .noLimitAndOffset (),
822- "It's not allowed to query with offser /limit " +
839+ "It's not allowed to query with offset /limit " +
823840 "when there are uncommitted records." );
824841 // TODO: also add check: no SCAN, no OLAP
825842 E .checkArgument (!query .paging (),
@@ -1008,7 +1025,7 @@ public Iterator<Edge> queryEdges() {
10081025 public Iterator <Edge > queryEdges (Query query ) {
10091026 if (this .hasUpdate ()) {
10101027 E .checkArgument (query .noLimitAndOffset (),
1011- "It's not allowed to query with offser /limit " +
1028+ "It's not allowed to query with offset /limit " +
10121029 "when there are uncommitted records." );
10131030 // TODO: also add check: no SCAN, no OLAP
10141031 E .checkArgument (!query .paging (),
@@ -2037,6 +2054,22 @@ private Iterator<?> joinTxEdges(Query query, Iterator<HugeEdge> edges,
20372054 edges = this .joinTxRecords (query , edges , matchTxFunc ,
20382055 this .addedEdges , this .removedEdges ,
20392056 this .updatedEdges );
2057+ if (query instanceof ConditionQuery &&
2058+ ((ConditionQuery ) query ).containsCondition (HugeKeys .OWNER_VERTEX ) &&
2059+ !(this .addedEdges .isEmpty () && this .updatedEdges .isEmpty ())) {
2060+ edges = new FlatMapperIterator <>(edges , edge -> {
2061+ // Rehydrated self-loops may hold distinct vertex objects.
2062+ if (edge .sourceVertex ().id ().equals (edge .targetVertex ().id ()) &&
2063+ (this .addedEdges .containsKey (edge .id ()) ||
2064+ this .updatedEdges .containsKey (edge .id ()))) {
2065+ HugeEdge opposite = edge .switchOwner ();
2066+ if (query .test (opposite )) {
2067+ return ImmutableList .of (edge , opposite ).iterator ();
2068+ }
2069+ }
2070+ return ImmutableList .of (edge ).iterator ();
2071+ });
2072+ }
20402073 if (removingVertices .isEmpty ()) {
20412074 return edges ;
20422075 }
@@ -2073,6 +2106,9 @@ private <V extends HugeElement> Iterator<V> joinTxRecords(
20732106 * Records in memory have higher priority than a query from backend store
20742107 */
20752108 for (V elem : addedTxRecords .values ()) {
2109+ if (removedTxRecords .containsKey (elem .id ())) {
2110+ continue ;
2111+ }
20762112 if (query .reachLimit (txResults .size ())) {
20772113 break ;
20782114 }
@@ -2081,6 +2117,9 @@ private <V extends HugeElement> Iterator<V> joinTxRecords(
20812117 }
20822118 }
20832119 for (V elem : updatedTxRecords .values ()) {
2120+ if (removedTxRecords .containsKey (elem .id ()) || addedTxRecords .containsKey (elem .id ())) {
2121+ continue ;
2122+ }
20842123 if (query .reachLimit (txResults .size ())) {
20852124 break ;
20862125 }
0 commit comments