Skip to content

Commit 706e476

Browse files
authored
Merge pull request #5 from colinfindlay/feature/sort-limit
#4 - Adding support for Sort, Limit and Skip
2 parents bccff07 + 1dabf72 commit 706e476

File tree

10 files changed

+398
-12
lines changed

10 files changed

+398
-12
lines changed

src/main/java/com/arangodb/graphql/context/ArangoGraphQLContext.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public class ArangoGraphQLContext {
4949

5050
private int max;
5151

52+
private Map sort;
53+
54+
private Integer limit;
55+
56+
private Integer skip;
57+
5258
private ArangoFilterGroup rootFilters;
5359
private List<ArangoFilterGroup> traversalFilters;
5460

@@ -66,15 +72,45 @@ public ArangoGraphQLContext(DataFetchingEnvironment dataFetchingEnvironment, Sel
6672
edgeDirectives = new HashMap<>();
6773
rootTraversalDirection = new ArangoEdgeTraversalDirection();
6874

75+
setupLimit(dataFetchingEnvironment);
76+
setupSort(dataFetchingEnvironment);
77+
6978
setupRootCollection(dataFetchingEnvironment);
70-
rootFilters = new ArangoRootFilterGroup(dataFetchingEnvironment.getArguments(), dataFetchingEnvironment.getFieldType());
79+
Map<String, Object> filterArguments = filterArguments(dataFetchingEnvironment);
80+
rootFilters = new ArangoRootFilterGroup(filterArguments, dataFetchingEnvironment.getFieldType());
7181
traversalFilters = new ArrayList<>();
7282

7383
selectedFields = selectedFieldCollector.getFields();
7484
selectedFields.forEach(x -> processField(x));
7585

7686
}
7787

88+
private Map<String, Object> filterArguments(DataFetchingEnvironment dataFetchingEnvironment){
89+
Map<String, Object> arguments = dataFetchingEnvironment.getArguments();
90+
arguments.remove("sort");
91+
arguments.remove("limit");
92+
arguments.remove("skip");
93+
return arguments;
94+
}
95+
96+
private void setupSort(DataFetchingEnvironment dataFetchingEnvironment){
97+
Object sort = dataFetchingEnvironment.getArgument("sort");
98+
if(sort instanceof Map){
99+
this.sort = (Map) sort;
100+
}
101+
}
102+
103+
private void setupLimit(DataFetchingEnvironment dataFetchingEnvironment){
104+
Object limit = dataFetchingEnvironment.getArgument("limit");
105+
if(limit instanceof Integer){
106+
this.limit = (Integer) limit;
107+
Object skip = dataFetchingEnvironment.getArgument("skip");
108+
if(skip instanceof Integer){
109+
this.skip = (Integer) skip;
110+
}
111+
}
112+
}
113+
78114
private void setupRootCollection(DataFetchingEnvironment dataFetchingEnvironment) {
79115
GraphQLType rootType = GraphQLTypeUtil.unwrapAll(dataFetchingEnvironment.getFieldType());
80116
ArangoVertexDirective arangoVertexDirective = new ArangoVertexDirective((GraphQLDirectiveContainer) rootType);
@@ -265,4 +301,28 @@ public List<EdgeCollection> getEdgeCollections() {
265301
public ArangoEdgeDirective edgeDirectiveFor(String qualifiedName) {
266302
return edgeDirectives.get(qualifiedName);
267303
}
304+
305+
/**
306+
*
307+
* @return A sorted Map of top level attributes and their sort directions
308+
*/
309+
public Map getSort() {
310+
return sort;
311+
}
312+
313+
/**
314+
*
315+
* @return The maximum number of top level results
316+
*/
317+
public Integer getLimit() {
318+
return limit;
319+
}
320+
321+
/**
322+
*
323+
* @return The number of top level records to skip (used for pagination)
324+
*/
325+
public Integer getSkip() {
326+
return skip;
327+
}
268328
}

src/main/java/com/arangodb/graphql/generator/DefaultArangoQueryGeneratorChain.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public class DefaultArangoQueryGeneratorChain extends ArangoQueryGeneratorChain
4747
public DefaultArangoQueryGeneratorChain() {
4848
with(new WithGenerator());
4949
with(new RootForGenerator());
50+
with(new SortGenerator());
51+
with(new LimitGenerator());
5052
with(new RootFilterGenerator());
5153
with(new TraversalForGenerator());
5254
with(new EdgeCollectionGenerator());
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* DISCLAIMER
3+
* Copyright 2019 ArangoDB GmbH, Cologne, Germany
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
* Copyright holder is ArangoDB GmbH, Cologne, Germany
18+
*
19+
*/
20+
21+
package com.arangodb.graphql.generator;
22+
23+
import com.arangodb.graphql.context.ArangoFilterGroup;
24+
import com.arangodb.graphql.context.ArangoGraphQLContext;
25+
26+
import java.util.Arrays;
27+
import java.util.List;
28+
29+
/**
30+
* AQL Generator that will generate a LIMIT statement for the root query
31+
*
32+
* @author Colin Findlay
33+
*/
34+
public class LimitGenerator implements ArangoQueryGenerator {
35+
36+
@Override
37+
public String generateAql(ArangoGraphQLContext ctx) {
38+
String aql = "LIMIT ";
39+
if(ctx.getSkip() != null){
40+
aql = aql + ctx.getSkip() + ", ";
41+
}
42+
return aql + ctx.getLimit();
43+
}
44+
45+
@Override
46+
public boolean shouldGenerateFor(ArangoGraphQLContext ctx) {
47+
return ctx.getLimit() != null;
48+
}
49+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* DISCLAIMER
3+
* Copyright 2019 ArangoDB GmbH, Cologne, Germany
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
* Copyright holder is ArangoDB GmbH, Cologne, Germany
18+
*
19+
*/
20+
21+
package com.arangodb.graphql.generator;
22+
23+
import com.arangodb.graphql.context.ArangoGraphQLContext;
24+
25+
import java.util.Map;
26+
import java.util.stream.Collectors;
27+
28+
/**
29+
* AQL Generator that will generate a SORT statement for the root query
30+
*
31+
* @author Colin Findlay
32+
*/
33+
public class SortGenerator implements ArangoQueryGenerator {
34+
35+
@Override
36+
public String generateAql(ArangoGraphQLContext ctx) {
37+
Map<String, String> sort = ctx.getSort();
38+
39+
String sortEntries = sort.entrySet()
40+
.stream()
41+
.map(e -> sortEntry(e.getKey(), e.getValue()))
42+
.collect(Collectors.joining(", "));
43+
44+
String aql = "SORT " + sortEntries;
45+
return aql;
46+
}
47+
48+
private String sortEntry(String key, String value){
49+
if("DESC".equals(value)) {
50+
return "rootVertex." + key + " DESC";
51+
}
52+
else{
53+
return "rootVertex." + key;
54+
}
55+
}
56+
57+
@Override
58+
public boolean shouldGenerateFor(ArangoGraphQLContext ctx) {
59+
return ctx.getSort() != null && ctx.getSort().size() > 0;
60+
}
61+
}

src/main/java/com/arangodb/graphql/query/result/ResultVertices.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
import com.arangodb.entity.BaseDocument;
2424
import com.arangodb.graphql.query.BaseDocumentPathEntity;
2525

26-
import java.util.List;
27-
import java.util.Map;
28-
import java.util.Set;
26+
import java.util.*;
2927
import java.util.stream.Collectors;
3028

3129
import static java.util.stream.Collectors.toMap;
@@ -54,13 +52,16 @@ public ResultVertices(String rootCollection, List<BaseDocumentPathEntity> paths)
5452
vertices = paths.stream()
5553
.flatMap(x -> x.getVertices().stream())
5654
.filter(x -> x != null)
57-
.collect(toMap(BaseDocument::getId, x -> x, (x, y) -> x));
55+
.collect(toMap(BaseDocument::getId,
56+
x -> x,
57+
(x, y) -> x,
58+
LinkedHashMap::new));
5859

59-
rootVertices = paths.stream()
60+
rootVertices = new LinkedHashSet<>(paths.stream()
6061
.map(x -> x.getVertices())
6162
.map(x -> x.size() > 0 ? x.get(0) : null)
6263
.filter(x -> x != null)
63-
.collect(Collectors.toSet());
64+
.collect(Collectors.toList()));
6465
}
6566

6667
/**
@@ -72,6 +73,14 @@ public BaseDocument get(String id) {
7273
return vertices.get(id);
7374
}
7475

76+
/**
77+
* Get all vertices
78+
* @return Get all vertices
79+
*/
80+
public Map<String, BaseDocument> vertices() {
81+
return vertices;
82+
}
83+
7584
/**
7685
* Get all the root vertices in the result
7786
* @return All the root vertices in the result

src/test/java/com/arangodb/graphql/context/ArangoGraphQLContextTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ public void setUp() throws Exception {
115115
when(selectedField.getQualifiedName()).thenReturn("id");
116116
when(selectedField.getArguments()).thenReturn(args);
117117

118+
when(dataFetchingEnvironment.getArgument("limit")).thenReturn(100);
119+
when(dataFetchingEnvironment.getArgument("skip")).thenReturn(200);
120+
121+
Map sort = new HashMap();
122+
sort.put("property", "ASC");
123+
when(dataFetchingEnvironment.getArgument("sort")).thenReturn(sort);
124+
125+
118126
context = new ArangoGraphQLContext(dataFetchingEnvironment, selectedFieldCollector);
119127
}
120128

@@ -195,4 +203,15 @@ public void edgeDirectiveFor() {
195203
assertThat(arangoEdgeDirective.getCollection(), equalTo("myEdgeCollection"));
196204
assertThat(arangoEdgeDirective.getDirection(), equalTo(TraversalOptions.Direction.outbound));
197205
}
206+
207+
@Test
208+
public void limitAndSkip(){
209+
assertThat(context.getLimit(), equalTo(100));
210+
assertThat(context.getSkip(), equalTo(200));
211+
}
212+
213+
@Test
214+
public void sort(){
215+
assertThat(context.getSort().get("property"), equalTo("ASC"));
216+
}
198217
}

src/test/java/com/arangodb/graphql/generator/DefaultArangoQueryGeneratorChainTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ public void testDefaultChainOrder(){
3636

3737
assertThat(chainItems.get(0), instanceOf(WithGenerator.class));
3838
assertThat(chainItems.get(1), instanceOf(RootForGenerator.class));
39-
assertThat(chainItems.get(2), instanceOf(RootFilterGenerator.class));
40-
assertThat(chainItems.get(3), instanceOf(TraversalForGenerator.class));
41-
assertThat(chainItems.get(4), instanceOf(EdgeCollectionGenerator.class));
42-
assertThat(chainItems.get(5), instanceOf(TraversalFilterGenerator.class));
43-
assertThat(chainItems.get(6), instanceOf(ReturnStatementGenerator.class));
39+
assertThat(chainItems.get(2), instanceOf(SortGenerator.class));
40+
assertThat(chainItems.get(3), instanceOf(LimitGenerator.class));
41+
assertThat(chainItems.get(4), instanceOf(RootFilterGenerator.class));
42+
assertThat(chainItems.get(5), instanceOf(TraversalForGenerator.class));
43+
assertThat(chainItems.get(6), instanceOf(EdgeCollectionGenerator.class));
44+
assertThat(chainItems.get(7), instanceOf(TraversalFilterGenerator.class));
45+
assertThat(chainItems.get(8), instanceOf(ReturnStatementGenerator.class));
4446

4547
}
4648

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* DISCLAIMER
3+
* Copyright 2019 ArangoDB GmbH, Cologne, Germany
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
* Copyright holder is ArangoDB GmbH, Cologne, Germany
18+
*
19+
*/
20+
21+
package com.arangodb.graphql.generator;
22+
23+
import com.arangodb.graphql.context.ArangoEdgeTraversalDirection;
24+
import com.arangodb.graphql.context.ArangoGraphQLContext;
25+
import com.arangodb.graphql.model.EdgeCollection;
26+
import com.arangodb.model.TraversalOptions;
27+
import org.junit.Before;
28+
import org.junit.Test;
29+
import org.junit.runner.RunWith;
30+
import org.mockito.Mock;
31+
import org.mockito.junit.MockitoJUnitRunner;
32+
33+
import java.util.Arrays;
34+
import java.util.Collections;
35+
36+
import static org.hamcrest.CoreMatchers.equalTo;
37+
import static org.junit.Assert.*;
38+
import static org.mockito.Mockito.when;
39+
40+
@RunWith(MockitoJUnitRunner.class)
41+
public class LimitGeneratorTest {
42+
43+
44+
@Mock
45+
private ArangoGraphQLContext context;
46+
47+
private LimitGenerator generator;
48+
49+
@Before
50+
public void setUp(){
51+
generator = new LimitGenerator();
52+
}
53+
54+
@Test
55+
public void generateLimitNoSkip() {
56+
when(context.getLimit()).thenReturn(100);
57+
when(context.getSkip()).thenReturn(null);
58+
assertThat(generator.generateAql(context), equalTo("LIMIT 100"));
59+
}
60+
61+
@Test
62+
public void generateLimitSkip() {
63+
when(context.getLimit()).thenReturn(100);
64+
when(context.getSkip()).thenReturn(200);
65+
assertThat(generator.generateAql(context), equalTo("LIMIT 200, 100"));
66+
}
67+
68+
69+
@Test
70+
public void shouldGenerateForLimit() {
71+
when(context.getLimit()).thenReturn(100);
72+
assertTrue(generator.shouldGenerateFor(context));
73+
}
74+
75+
76+
@Test
77+
public void shouldNotGenerateForSkipOnly() {
78+
when(context.getLimit()).thenReturn(null);
79+
assertFalse(generator.shouldGenerateFor(context));
80+
}
81+
}

0 commit comments

Comments
 (0)