diff --git a/services/src/main/java/org/jfrog/artifactory/client/aql/AqlItem.java b/services/src/main/java/org/jfrog/artifactory/client/aql/AqlItem.java index ca6522e2..135283b4 100644 --- a/services/src/main/java/org/jfrog/artifactory/client/aql/AqlItem.java +++ b/services/src/main/java/org/jfrog/artifactory/client/aql/AqlItem.java @@ -11,6 +11,8 @@ public class AqlItem { private static final String DESC = "$desc"; private static final String OR = "$or"; private static final String AND = "$and"; + private static final String MATCH = "$match"; + private static final String NOT_MATCH = "$nmatch"; private Map item; @@ -39,6 +41,14 @@ public static AqlItem desc(String... items) { return new AqlItem(DESC, items); } + public static AqlItem match(String key, String pattern) { + return new AqlItem(key, new AqlItem(MATCH, pattern)); + } + + public static AqlItem notMatch(String key, String pattern) { + return new AqlItem(key, new AqlItem(NOT_MATCH, pattern)); + } + @JsonIgnore public boolean isNotEmpty() { return item != null && !item.isEmpty(); diff --git a/services/src/main/java/org/jfrog/artifactory/client/aql/AqlQueryBuilder.java b/services/src/main/java/org/jfrog/artifactory/client/aql/AqlQueryBuilder.java index b07a2bfa..f549b97a 100644 --- a/services/src/main/java/org/jfrog/artifactory/client/aql/AqlQueryBuilder.java +++ b/services/src/main/java/org/jfrog/artifactory/client/aql/AqlQueryBuilder.java @@ -17,6 +17,8 @@ public class AqlQueryBuilder { private AqlRootElement root = new AqlRootElement(); private AqlItem sort; private AqlInclude include; + private Integer limit; + private Integer offset; public AqlQueryBuilder item(AqlItem item) { root.putAll(item.value()); @@ -26,9 +28,9 @@ public AqlQueryBuilder item(AqlItem item) { public AqlQueryBuilder elements(AqlItem... items) { if (isNotEmpty(items)) { root.putAll(Arrays.stream(items) - .map(item -> item.value().entrySet()) - .flatMap(Collection::stream) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue))); + .map(item -> item.value().entrySet()) + .flatMap(Collection::stream) + .collect(toMap(Map.Entry::getKey, Map.Entry::getValue))); } return this; } @@ -58,6 +60,20 @@ public AqlQueryBuilder or(AqlItem... items) { return this; } + public AqlQueryBuilder match(String key, String pattern) { + if (key != null) { + root.putAll(AqlItem.match(key, pattern).value()); + } + return this; + } + + public AqlQueryBuilder notMatch(String key, String pattern) { + if (key != null) { + root.putAll(AqlItem.notMatch(key, pattern).value()); + } + return this; + } + public AqlQueryBuilder or(Collection items) { return or(setToArray(items)); } @@ -83,10 +99,21 @@ public AqlQueryBuilder desc(String... by) { return this; } + public AqlQueryBuilder limit(int limit) { + this.limit = limit; + return this; + } + + public AqlQueryBuilder offset(int offset) { + this.offset = offset; + return this; + } + public String build() { try { ObjectMapper mapper = new ObjectMapper(); - return "items.find(" + getRootAsString(mapper) + ")" + getIncludeAsString() + getSortAsString(mapper); + return "items.find(" + getRootAsString(mapper) + ")" + getIncludeAsString() + getSortAsString( + mapper) + getOffsetAsString(mapper) + getLimitAsString(mapper); } catch (JsonProcessingException e) { throw new AqlBuilderException("Error serializing object to json: ", e); } @@ -100,16 +127,32 @@ private String getIncludeAsString() { return hasInclude() ? include.toString() : ""; } + private String getOffsetAsString(ObjectMapper mapper) throws JsonProcessingException { + return hasOffset() ? ".offset(" + mapper.writeValueAsString(offset) + ")" : ""; + } + + private String getLimitAsString(ObjectMapper mapper) throws JsonProcessingException { + return hasLimit() ? ".limit(" + mapper.writeValueAsString(limit) + ")" : ""; + } + private String getRootAsString(ObjectMapper mapper) throws JsonProcessingException { return hasRoot() ? mapper.writeValueAsString(root) : ""; } + private boolean hasInclude() { + return include != null && include.isNotEmpty(); + } + private boolean hasSort() { return sort != null && sort.isNotEmpty(); } - private boolean hasInclude() { - return include != null && include.isNotEmpty(); + private boolean hasLimit() { + return limit != null; + } + + private boolean hasOffset() { + return offset != null; } private boolean hasRoot() { diff --git a/services/src/test/java/org/jfrog/artifactory/client/aql/AqlQueryBuilderTest.java b/services/src/test/java/org/jfrog/artifactory/client/aql/AqlQueryBuilderTest.java index cd69b590..0d484301 100644 --- a/services/src/test/java/org/jfrog/artifactory/client/aql/AqlQueryBuilderTest.java +++ b/services/src/test/java/org/jfrog/artifactory/client/aql/AqlQueryBuilderTest.java @@ -9,6 +9,7 @@ import static org.hamcrest.core.IsNull.notNullValue; import static org.jfrog.artifactory.client.aql.AqlItem.aqlItem; import static org.jfrog.artifactory.client.aql.AqlItem.or; +import static org.jfrog.artifactory.client.aql.AqlItem.match; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -143,6 +144,53 @@ public void orTest() { + "]})")); } + @Test + public void matchTest() { + String result = new AqlQueryBuilder() + .match("repo", "myrepo*") + .build(); + + assertThat(result, notNullValue()); + assertThat(result, is("items.find(" + + "{\"repo\":" + + "{\"$match\":\"myrepo*\"}" + + "})")); + } + + @Test + public void notMatchTest() { + String result = new AqlQueryBuilder() + .notMatch("repo", "myrepo*") + .build(); + + assertThat(result, notNullValue()); + assertThat(result, is("items.find(" + + "{\"repo\":" + + "{\"$nmatch\":\"myrepo*\"}" + + "})")); + } + + + @Test + public void limitTest() { + String result = new AqlQueryBuilder() + .limit(123) + .build(); + + assertThat(result, notNullValue()); + assertThat(result, is("items.find().limit(123)")); + } + + @Test + public void offsetTest() { + String result = new AqlQueryBuilder() + .offset(123) + .build(); + + assertThat(result, notNullValue()); + assertThat(result, is("items.find().offset(123)")); + } + @Test public void addNestedFilters() { final String result = new AqlQueryBuilder() @@ -150,7 +198,8 @@ public void addNestedFilters() { or( aqlItem("repo", "myrepo1"), aqlItem("repo", "myrepo2"), - aqlItem("repo", "myrepo3") + aqlItem("repo", "myrepo3"), + match("repo", "myotherrepo*") ), or( aqlItem("name", "maven-metadata1.xml"), @@ -165,8 +214,10 @@ public void addNestedFilters() { + "{\"$or\":[" + "{\"repo\":\"myrepo1\"}," + "{\"repo\":\"myrepo2\"}," - + "{\"repo\":\"myrepo3\"}" - + "]}," + + "{\"repo\":\"myrepo3\"}," + + "{\"repo\":" + + "{\"$match\":\"myotherrepo*\"}" + + "}]}," + "{\"$or\":[" + "{\"name\":\"maven-metadata1.xml\"}," + "{\"name\":\"maven-metadata2.xml\"}" @@ -190,6 +241,8 @@ public void variousElements() { .item(aqlItem("property", "value")) .include("name", "repo") .asc("name", "repo") + .offset(1) + .limit(2) .build(); assertThat(result, notNullValue()); @@ -206,7 +259,9 @@ public void variousElements() { + "\"property\":\"value\"" + "})" + ".include(\"name\",\"repo\")" - + ".sort({\"$asc\":[\"name\",\"repo\"]})")); + + ".sort({\"$asc\":[\"name\",\"repo\"]})" + + ".offset(1)" + + ".limit(2)")); } @Test