2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * * Modifications Copyright © 2024 DEUTSCHE TELEKOM AG.
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
23 package org.onap.aai.query.builder;
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.Multimap;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
33 import java.util.NoSuchElementException;
34 import java.util.Optional;
37 import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
38 import org.apache.tinkerpop.gremlin.process.traversal.Order;
39 import org.apache.tinkerpop.gremlin.process.traversal.P;
40 import org.apache.tinkerpop.gremlin.process.traversal.Path;
41 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
42 import org.apache.tinkerpop.gremlin.process.traversal.Scope;
43 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
44 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
45 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
46 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
47 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
48 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
49 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
50 import org.apache.tinkerpop.gremlin.structure.Direction;
51 import org.apache.tinkerpop.gremlin.structure.Edge;
52 import org.apache.tinkerpop.gremlin.structure.Vertex;
53 import org.onap.aai.db.props.AAIProperties;
54 import org.onap.aai.edges.EdgeRule;
55 import org.onap.aai.edges.EdgeRuleQuery;
56 import org.onap.aai.edges.enums.EdgeType;
57 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
58 import org.onap.aai.exceptions.AAIException;
59 import org.onap.aai.introspection.Introspector;
60 import org.onap.aai.introspection.Loader;
61 import org.onap.aai.query.entities.PaginationResult;
62 import org.onap.aai.schema.enums.ObjectMetadata;
63 import org.onap.aai.schema.enums.PropertyMetadata;
64 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
69 * The Class GraphTraversalBuilder.
71 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
73 private static final Logger LOGGER = LoggerFactory.getLogger(GraphTraversalBuilder.class);
75 private final GroovyTranslator groovyTranslator = GroovyTranslator.of("source");
77 protected GraphTraversal<Vertex, E> traversal = null;
78 protected Admin<Vertex, E> completeTraversal = null;
80 protected QueryBuilder<E> containerQuery;
81 protected QueryBuilder<E> parentQuery;
84 * Instantiates a new graph traversal builder.
86 * @param loader the loader
88 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
89 super(loader, source);
90 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
94 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, GraphTraversal<Vertex, E> traversal) {
95 super(loader, source);
96 this.traversal = traversal;
101 * Instantiates a new graph traversal builder.
103 * @param loader the loader
104 * @param start the start
106 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
107 super(loader, source, start);
109 traversal = (GraphTraversal<Vertex, E>) __.__(start);
117 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
119 // correct value call because the index is registered as an Integer
120 this.vertexHas(key, this.correctObjectType(value));
122 return (QueryBuilder<Vertex>) this;
126 protected void vertexHas(String key, Object value) {
127 traversal.has(key, value);
131 protected void vertexHasNot(String key) {
132 traversal.hasNot(key);
136 protected void vertexHas(String key) {
140 // TODO: Remove this once we test this - at this point i dont thib this is required
141 // because predicare is an object
144 * protected void vertexHas(final String key, final P<?> predicate) {
145 * traversal.has(key, predicate);
153 public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
155 // this is because the index is registered as an Integer
156 List<Object> correctedValues = new ArrayList<>();
157 for (Object item : values) {
158 correctedValues.add(this.correctObjectType(item));
161 this.vertexHas(key, P.within(correctedValues));
163 return (QueryBuilder<Vertex>) this;
169 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
170 ArrayList<String> values = new ArrayList<>(Arrays.asList(value.split(",")));
171 int size = values.size();
172 for (int i = 0; i < size; i++) {
173 values.set(i, values.get(i).trim());
175 this.vertexHas(key, P.within(values));
178 return (QueryBuilder<Vertex>) this;
185 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
187 // correct value call because the index is registered as an Integer
188 // TODO Check if this needs to be in QB and add these as internal
189 this.vertexHas(key, org.janusgraph.core.attribute.Text.textPrefix(value));
192 return (QueryBuilder<Vertex>) this;
199 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
202 return (QueryBuilder<Vertex>) this;
209 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
210 this.vertexHasNot(key);
212 return (QueryBuilder<Vertex>) this;
219 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
221 // correct value call because the index is registered as an Integer
222 this.vertexHas(key, P.neq(this.correctObjectType(value)));
224 return (QueryBuilder<Vertex>) this;
231 public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
233 // this is because the index is registered as an Integer
234 List<Object> correctedValues = new ArrayList<>();
235 for (Object item : values) {
236 correctedValues.add(this.correctObjectType(item));
239 this.vertexHas(key, P.without(correctedValues));
241 return (QueryBuilder<Vertex>) this;
245 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) {
246 this.vertexHas(key, P.gte(this.correctObjectType(value)));
249 return (QueryBuilder<Vertex>) this;
253 public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) {
254 this.vertexHas(key, P.lte(this.correctObjectType(value)));
257 return (QueryBuilder<Vertex>) this;
264 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
265 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
267 return (QueryBuilder<Vertex>) this;
274 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
276 for (Map.Entry<String, String> es : map.entrySet()) {
277 this.vertexHas(es.getKey(), es.getValue());
280 traversal.has(AAIProperties.NODE_TYPE, type);
282 return (QueryBuilder<Vertex>) this;
286 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
288 if (value != null && !"".equals(value)) {
289 boolean bValue = false;
291 if (value instanceof String) {// "true"
292 bValue = Boolean.valueOf(value.toString());
293 } else if (value instanceof Boolean boolean1) {// true
297 this.vertexHas(key, bValue);
300 return (QueryBuilder<Vertex>) this;
307 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
308 Set<String> keys = obj.getKeys();
310 for (String key : keys) {
311 val = obj.getValue(key);
312 Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
313 if (metadata.isPresent()) {
314 // use the db name for the field rather than the object model
315 key = metadata.get();
318 // this is because the index is registered as an Integer
319 if (val.getClass().equals(Long.class)) {
320 this.vertexHas(key, Integer.valueOf(val.toString()));
322 this.vertexHas(key, val);
327 return (QueryBuilder<Vertex>) this;
331 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
332 this.createKeyQuery(obj);
333 allPropertiesQuery(obj);
334 this.createContainerQuery(obj);
335 return (QueryBuilder<Vertex>) this;
338 private void allPropertiesQuery(Introspector obj) {
339 Set<String> props = obj.getProperties();
340 Set<String> keys = obj.getKeys();
342 for (String prop : props) {
343 if (obj.isSimpleType(prop) && !keys.contains(prop)) {
344 val = obj.getValue(prop);
346 Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
347 if (metadata.isPresent()) {
348 // use the db name for the field rather than the object model
349 prop = metadata.get();
351 // this is because the index is registered as an Integer
352 if (val.getClass().equals(Long.class)) {
353 this.vertexHas(prop, Integer.valueOf(val.toString()));
355 this.vertexHas(prop, val);
364 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
365 String type = obj.getChildDBName();
366 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
367 if (abstractType != null) {
368 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
369 traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
371 traversal.has(AAIProperties.NODE_TYPE, type);
375 return (QueryBuilder<Vertex>) this;
379 * @throws NoEdgeRuleFoundException
380 * @throws AAIException
384 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
385 throws AAIException {
386 createTraversal(type, parent, child, false);
387 return (QueryBuilder<Vertex>) this;
392 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
393 throws AAIException {
394 this.createTraversal(type, parent, child, true);
395 return (QueryBuilder<Vertex>) this;
398 private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge)
399 throws AAIException {
400 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
401 if ("true".equals(isAbstractType)) {
402 markParentBoundary();
403 traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
406 this.edgeQueryToVertex(type, parent, child, null);
414 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
415 List<String> labels) throws AAIException {
416 this.edgeQueryToVertex(type, out, in, labels);
417 return (QueryBuilder<Vertex>) this;
420 private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent,
421 Introspector child, boolean isPrivateEdge) throws AAIException {
422 String childName = child.getDbName();
423 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
424 String[] inheritors = inheritorMetadata.split(",");
425 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
427 for (int i = 0; i < inheritors.length; i++) {
428 String inheritor = inheritors[i];
429 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
430 if (edgeRules.hasRule(qB.build())) {
431 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
433 rules = edgeRules.getRules(qB.edgeType(type).build());
434 } catch (EdgeRuleNotFoundException e) {
435 throw new NoEdgeRuleFoundException(e);
438 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
440 final List<String> inLabels = new ArrayList<>();
441 final List<String> outLabels = new ArrayList<>();
443 rules.values().forEach(rule -> {
444 if (rule.getDirection().equals(Direction.IN)) {
445 inLabels.add(rule.getLabel());
447 outLabels.add(rule.getLabel());
451 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
452 innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
453 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
454 innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
456 innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
457 __.in(inLabels.toArray(new String[inLabels.size()])));
460 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
461 unionTraversals.add(innerTraversal);
465 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
468 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
469 List<String> labels) throws AAIException {
470 Introspector outObj = loader.introspectorFromName(outNodeType);
471 Introspector inObj = loader.introspectorFromName(inNodeType);
472 this.edgeQuery(type, outObj, inObj, labels);
474 return (QueryBuilder<Edge>) this;
481 public QueryBuilder<E> union(QueryBuilder... builder) {
482 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
483 for (int i = 0; i < builder.length; i++) {
484 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
486 this.traversal.union(traversals);
496 public QueryBuilder<E> where(QueryBuilder... builder) {
497 for (int i = 0; i < builder.length; i++) {
498 this.traversal.where((GraphTraversal<Vertex, Vertex>) builder[i].getQuery());
509 public QueryBuilder<E> or(QueryBuilder... builder) {
510 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
511 for (int i = 0; i < builder.length; i++) {
512 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
514 this.traversal.or(traversals);
521 public QueryBuilder<E> store(String name) {
523 this.traversal.aggregate(Scope.local , name);
530 public QueryBuilder<E> cap(String name) {
531 this.traversal.cap(name);
538 public QueryBuilder<E> unfold() {
539 this.traversal.unfold();
546 public QueryBuilder<E> dedup() {
548 this.traversal.dedup();
555 public QueryBuilder<E> emit() {
557 this.traversal.emit();
565 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
567 this.traversal.repeat((GraphTraversal<Vertex, E>) builder.getQuery());
574 public QueryBuilder<E> until(QueryBuilder<E> builder) {
575 this.traversal.until((GraphTraversal<Vertex, E>) builder.getQuery());
582 public QueryBuilder<E> groupCount() {
583 this.traversal.groupCount();
590 public QueryBuilder<E> both() {
591 this.traversal.both();
598 public QueryBuilder<Tree> tree() {
600 this.traversal.tree();
603 return (QueryBuilder<Tree>) this;
607 public QueryBuilder<E> by(String name) {
608 this.traversal.by(name);
615 public QueryBuilder<E> valueMap() {
616 this.traversal.valueMap();
623 public QueryBuilder<E> valueMap(String... names) {
624 this.traversal.valueMap(names);
634 public QueryBuilder<E> simplePath() {
635 this.traversal.simplePath();
644 public QueryBuilder<Path> path() {
645 this.traversal.path();
647 return (QueryBuilder<Path>) this;
651 public QueryBuilder<Edge> outE() {
652 this.traversal.outE();
654 return (QueryBuilder<Edge>) this;
658 public QueryBuilder<Edge> inE() {
659 this.traversal.inE();
661 return (QueryBuilder<Edge>) this;
665 public QueryBuilder<Vertex> outV() {
666 this.traversal.outV();
668 return (QueryBuilder<Vertex>) this;
672 public QueryBuilder<Vertex> inV() {
673 this.traversal.inV();
675 return (QueryBuilder<Vertex>) this;
679 public QueryBuilder<E> as(String name) {
680 this.traversal.as(name);
687 public QueryBuilder<E> not(QueryBuilder<E> builder) {
688 this.traversal.not(builder.getQuery());
695 public QueryBuilder<E> select(String name) {
696 this.traversal.select(name);
704 public QueryBuilder<E> select(Pop pop, String name) {
705 this.traversal.select(pop, name);
713 public QueryBuilder<E> select(String... names) {
714 if (names.length == 1) {
715 this.traversal.select(names[0]);
716 } else if (names.length == 2) {
717 this.traversal.select(names[0], names[1]);
718 } else if (names.length > 2) {
719 String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
720 this.traversal.select(names[0], names[1], otherNames);
731 * @param outObj the out type
732 * @param inObj the in type
733 * @throws NoEdgeRuleFoundException
734 * @throws AAIException
736 private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
737 throws AAIException {
738 String outType = outObj.getDbName();
739 String inType = inObj.getDbName();
741 if (outObj.isContainer()) {
742 outType = outObj.getChildDBName();
744 if (inObj.isContainer()) {
745 inType = inObj.getChildDBName();
747 markParentBoundary();
748 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
749 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
751 if (labels == null) {
753 rules.putAll(edgeRules.getRules(qB.build()));
754 } catch (EdgeRuleNotFoundException e) {
755 // is ok per original functioning of this section
756 // TODO add "best try" sort of flag to the EdgeRuleQuery
757 // to indicate if the exception should be thrown or not
760 for (String label : labels) {
762 rules.putAll(edgeRules.getRules(qB.label(label).build()));
763 } catch (EdgeRuleNotFoundException e) {
764 throw new NoEdgeRuleFoundException(e);
767 if (rules.isEmpty()) {
768 throw new NoEdgeRuleFoundException(
769 "No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
773 final List<String> inLabels = new ArrayList<>();
774 final List<String> outLabels = new ArrayList<>();
776 for (EdgeRule rule : rules.values()) {
777 if (labels != null && !labels.contains(rule.getLabel())) {
780 if (Direction.IN.equals(rule.getDirection())) {
781 inLabels.add(rule.getLabel());
783 outLabels.add(rule.getLabel());
788 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
789 traversal.out(outLabels.toArray(new String[outLabels.size()]));
790 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
791 traversal.in(inLabels.toArray(new String[inLabels.size()]));
793 traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
794 __.in(inLabels.toArray(new String[inLabels.size()])));
799 this.createContainerQuery(inObj);
806 * @param outObj the out type
807 * @param inObj the in type
808 * @throws NoEdgeRuleFoundException
809 * @throws AAIException
811 private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
812 throws AAIException {
813 String outType = outObj.getDbName();
814 String inType = inObj.getDbName();
816 if (outObj.isContainer()) {
817 outType = outObj.getChildDBName();
819 if (inObj.isContainer()) {
820 inType = inObj.getChildDBName();
823 markParentBoundary();
824 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
825 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
828 if (labels == null) {
829 rules.putAll(edgeRules.getRules(qB.build()));
831 for (String label : labels) {
832 rules.putAll(edgeRules.getRules(qB.label(label).build()));
835 } catch (EdgeRuleNotFoundException e) {
836 throw new NoEdgeRuleFoundException(e);
839 final List<String> inLabels = new ArrayList<>();
840 final List<String> outLabels = new ArrayList<>();
842 for (EdgeRule rule : rules.values()) {
843 if (labels != null && !labels.contains(rule.getLabel())) {
846 if (Direction.IN.equals(rule.getDirection())) {
847 inLabels.add(rule.getLabel());
849 outLabels.add(rule.getLabel());
854 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
855 traversal.outE(outLabels.toArray(new String[outLabels.size()]));
856 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
857 traversal.inE(inLabels.toArray(new String[inLabels.size()]));
859 traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])),
860 __.inE(inLabels.toArray(new String[inLabels.size()])));
865 public QueryBuilder<E> limit(long amount) {
866 traversal.limit(amount);
874 public <E2> E2 getQuery() {
875 return (E2) this.traversal;
882 public QueryBuilder<E> getParentQuery() {
883 return this.parentQuery != null
885 : cloneQueryAtStep(parentStepIndex);
889 public QueryBuilder<E> getContainerQuery() {
891 if (this.parentStepIndex == 0) {
892 return removeQueryStepsBetween(0, containerStepIndex);
894 return this.containerQuery;
902 public void markParentBoundary() {
903 this.parentQuery = cloneQueryAtStep(stepIndex);
904 parentStepIndex = stepIndex;
908 public void markContainer() {
909 this.containerQuery = cloneQueryAtStep(stepIndex);
910 containerStepIndex = stepIndex;
917 public Vertex getStart() {
921 protected int getParentStepIndex() {
922 return parentStepIndex;
925 protected int getContainerStepIndex() {
926 return containerStepIndex;
929 protected int getStepIndex() {
940 protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
942 protected void executeQuery() {
944 Admin<Vertex, Vertex> admin;
946 this.completeTraversal = traversal.asAdmin();
948 boolean queryLoggingEnabled = false;
949 if(queryLoggingEnabled) {
950 String query = groovyTranslator.translate(traversal.asAdmin().getBytecode()).getScript();
951 LOGGER.info("Query: {}", query);
954 admin = source.V().asAdmin();
955 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
957 this.completeTraversal = (Admin<Vertex, E>) admin;
964 public boolean hasNext() {
965 if (this.completeTraversal == null) {
969 return this.completeTraversal.hasNext();
974 if (this.completeTraversal == null) {
978 return this.completeTraversal.next();
982 public List<E> toList() {
983 if (this.completeTraversal == null) {
986 return this.completeTraversal.toList();
990 public QueryBuilder<E> sort(Sort sort) {
991 Order order = sort.getDirection() == Sort.Direction.ASC ? Order.asc : Order.desc;
992 traversal.order().by(sort.getProperty(), order);
997 public PaginationResult<E> toPaginationResult(Pageable pageable) {
998 int page = pageable.getPage();
999 int pageSize = pageable.getPageSize();
1000 if(pageable.isIncludeTotalCount()) {
1001 return paginateWithTotalCount(page, pageSize);
1003 return paginateWithoutTotalCount(page, pageSize);
1007 private PaginationResult<E> paginateWithoutTotalCount(int page, int pageSize) {
1008 int startIndex = page * pageSize;
1009 traversal.range(startIndex, startIndex + pageSize);
1011 if (this.completeTraversal == null) {
1014 return completeTraversal.hasNext()
1015 ? new PaginationResult<E>(completeTraversal.toList())
1016 : new PaginationResult<E>(Collections.emptyList());
1019 private PaginationResult<E> paginateWithTotalCount(int page, int pageSize) {
1020 int startIndex = page * pageSize;
1021 traversal.fold().as("results","count")
1022 .select("results","count").
1023 by(__.range(Scope.local, startIndex, startIndex + pageSize)).
1024 by(__.count(Scope.local));
1026 if (this.completeTraversal == null) {
1030 return mapPaginationResult((Map<String,Object>) completeTraversal.next());
1031 // .next() will throw an IllegalArguementException if there are no vertices of the given type
1032 } catch (NoSuchElementException | IllegalArgumentException e) {
1033 return new PaginationResult<>(Collections.emptyList(), 0L);
1037 private PaginationResult<E> mapPaginationResult(Map<String,Object> result) {
1038 Object objCount = result.get("count");
1039 Object vertices = result.get("results");
1040 if(vertices == null) {
1041 return new PaginationResult<E>(Collections.emptyList() ,0L);
1043 List<E> results = null;
1044 if(vertices instanceof List) {
1045 results = (List<E>) vertices;
1046 } else if (vertices instanceof Vertex) {
1047 results = Collections.singletonList((E) vertices);
1049 String msg = "Results must be a list or a vertex, but was %s".formatted(vertices.getClass().getName());
1051 throw new IllegalArgumentException(msg);
1053 long totalCount = parseCount(objCount);
1054 return new PaginationResult<E>(results, totalCount);
1057 private long parseCount(Object count) {
1058 if(count instanceof String string) {
1059 return Long.parseLong(string);
1060 } else if(count instanceof Integer integer) {
1061 return Long.valueOf(integer);
1062 } else if (count instanceof Long long1) {
1065 throw new IllegalArgumentException("Count must be a string, integer, or long");
1069 protected QueryBuilder<Edge> has(String key, String value) {
1070 traversal.has(key, value);
1072 return (QueryBuilder<Edge>) this;