Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit 986387e

Browse files
committed
Can now select URIs via a query
1 parent 9f81d59 commit 986387e

File tree

8 files changed

+220
-33
lines changed

8 files changed

+220
-33
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
group=com.marklogic
22
javadocsDir=../gh-pages-marklogic-java/javadocs
3-
version=3.0-alpha1
3+
version=3.0-alpha2

src/main/java/com/marklogic/client/ext/datamovement/UrisQueryBatcherBuilder.java renamed to src/main/java/com/marklogic/client/ext/datamovement/DocumentUrisQueryBatcherBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
/**
99
* Builds a QueryBatcher based on an array of document URIs.
1010
*/
11-
public class UrisQueryBatcherBuilder implements QueryBatcherBuilder {
11+
public class DocumentUrisQueryBatcherBuilder implements QueryBatcherBuilder {
1212

1313
private String[] documentUris;
1414

15-
public UrisQueryBatcherBuilder(String... documentUris) {
15+
public DocumentUrisQueryBatcherBuilder(String... documentUris) {
1616
this.documentUris = documentUris;
1717
}
1818

src/main/java/com/marklogic/client/ext/datamovement/QueryBatcherTemplate.java

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import com.marklogic.client.query.StringQueryDefinition;
99
import com.marklogic.client.query.StructuredQueryDefinition;
1010

11+
import java.util.ArrayList;
1112
import java.util.Iterator;
13+
import java.util.List;
1214

1315
/**
1416
* Spring-style Template class for simplifying common usages of QueryBatcher. Threadsafe, at least as long as
@@ -25,8 +27,8 @@ public class QueryBatcherTemplate extends LoggingObject {
2527
private boolean stopJob = true;
2628
private String jobName;
2729
private ForestConfiguration forestConfig;
28-
private QueryFailureListener[] queryFailureListeners;
29-
private QueryBatchListener[] urisReadyListeners;
30+
private List<QueryFailureListener> queryFailureListeners;
31+
private List<QueryBatchListener> urisReadyListeners;
3032

3133
public QueryBatcherTemplate(DatabaseClient databaseClient) {
3234
this.databaseClient = databaseClient;
@@ -51,8 +53,8 @@ public QueryBatcherJobTicket applyOnCollections(QueryBatchListener urisReadyList
5153
* @param documentUris
5254
* @return
5355
*/
54-
public QueryBatcherJobTicket applyOnDocuments(QueryBatchListener urisReadyListener, String... documentUris) {
55-
return apply(urisReadyListener, new UrisQueryBatcherBuilder(documentUris));
56+
public QueryBatcherJobTicket applyOnDocumentUris(QueryBatchListener urisReadyListener, String... documentUris) {
57+
return apply(urisReadyListener, new DocumentUrisQueryBatcherBuilder(documentUris));
5658
}
5759

5860
/**
@@ -66,6 +68,17 @@ public QueryBatcherJobTicket applyOnUriPattern(QueryBatchListener urisReadyListe
6668
return apply(urisReadyListener, new UriPatternQueryBatcherBuilder(uriPattern));
6769
}
6870

71+
/**
72+
* Apply the given listener on batches of documents with URIs matching the given XQuery or JavaScript query.
73+
*
74+
* @param urisReadyListener
75+
* @param xqueryOrJavascriptQuery
76+
* @return
77+
*/
78+
public QueryBatcherJobTicket applyOnUrisQuery(QueryBatchListener urisReadyListener, String xqueryOrJavascriptQuery) {
79+
return apply(urisReadyListener, new UrisQueryQueryBatcherBuilder(xqueryOrJavascriptQuery));
80+
}
81+
6982
/**
7083
* Apply the given listener on batches on documents matching the given structured query.
7184
*
@@ -173,10 +186,10 @@ public QueryBatcherJobTicket apply(QueryBatchListener urisReadyListener, QueryBa
173186
queryBatcher.withForestConfig(forestConfig);
174187
}
175188
if (urisReadyListeners != null) {
176-
queryBatcher.setUrisReadyListeners(urisReadyListeners);
189+
queryBatcher.setUrisReadyListeners(urisReadyListeners.toArray(new QueryBatchListener[]{}));
177190
}
178191
if (queryFailureListeners != null) {
179-
queryBatcher.setQueryFailureListeners(queryFailureListeners);
192+
queryBatcher.setQueryFailureListeners(queryFailureListeners.toArray(new QueryFailureListener[]{}));
180193
}
181194

182195
if (urisReadyListener != null) {
@@ -262,11 +275,35 @@ public void setForestConfig(ForestConfiguration forestConfig) {
262275
this.forestConfig = forestConfig;
263276
}
264277

265-
public void setQueryFailureListeners(QueryFailureListener... queryFailureListeners) {
266-
this.queryFailureListeners = queryFailureListeners;
278+
public void addQueryFailureListeners(QueryFailureListener... listeners) {
279+
if (this.queryFailureListeners == null) {
280+
this.queryFailureListeners = new ArrayList<>();
281+
}
282+
for (QueryFailureListener listener : listeners) {
283+
this.queryFailureListeners.add(listener);
284+
}
267285
}
268286

269-
public void setUrisReadyListeners(QueryBatchListener... urisReadyListeners) {
270-
this.urisReadyListeners = urisReadyListeners;
287+
public void setQueryFailureListeners(QueryFailureListener... listeners) {
288+
this.queryFailureListeners = new ArrayList<>();
289+
for (QueryFailureListener listener : listeners) {
290+
this.queryFailureListeners.add(listener);
291+
}
292+
}
293+
294+
public void addUrisReadyListeners(QueryBatchListener... listeners) {
295+
if (this.urisReadyListeners == null) {
296+
this.urisReadyListeners = new ArrayList<>();
297+
}
298+
for (QueryBatchListener listener : listeners) {
299+
this.urisReadyListeners.add(listener);
300+
}
301+
}
302+
303+
public void setUrisReadyListeners(QueryBatchListener... listeners) {
304+
this.urisReadyListeners = new ArrayList<>();
305+
for (QueryBatchListener listener : listeners) {
306+
this.urisReadyListeners.add(listener);
307+
}
271308
}
272309
}

src/main/java/com/marklogic/client/ext/datamovement/UriPatternQueryBatcherBuilder.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import com.marklogic.client.datamovement.DataMovementManager;
55
import com.marklogic.client.datamovement.QueryBatcher;
66
import com.marklogic.client.eval.EvalResult;
7+
import com.marklogic.client.ext.util.EvalResultIterator;
78

89
import java.util.Iterator;
910

1011
/**
11-
* Builds a QueryBatcher based on a URI pattern that is fed into cts:uri-match via an eval call.
12+
* Builds a QueryBatcher based on a URI pattern that is fed into cts:uri-match via an eval call. Note that cts:uri-match
13+
* may not always scale as well as a cts:uris query will.
1214
*/
1315
public class UriPatternQueryBatcherBuilder implements QueryBatcherBuilder {
1416

@@ -21,17 +23,6 @@ public UriPatternQueryBatcherBuilder(String uriPattern) {
2123
@Override
2224
public QueryBatcher buildQueryBatcher(DatabaseClient databaseClient, DataMovementManager dataMovementManager) {
2325
final Iterator<EvalResult> evalResults = databaseClient.newServerEval().xquery(String.format("cts:uri-match('%s')", uriPattern)).eval().iterator();
24-
Iterator<String> stringIterator = new Iterator<String>() {
25-
@Override
26-
public boolean hasNext() {
27-
return evalResults.hasNext();
28-
}
29-
30-
@Override
31-
public String next() {
32-
return evalResults.next().getString();
33-
}
34-
};
35-
return dataMovementManager.newQueryBatcher(stringIterator);
26+
return dataMovementManager.newQueryBatcher(new EvalResultIterator(evalResults));
3627
}
3728
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.marklogic.client.ext.datamovement;
2+
3+
import com.marklogic.client.DatabaseClient;
4+
import com.marklogic.client.datamovement.DataMovementManager;
5+
import com.marklogic.client.datamovement.QueryBatcher;
6+
import com.marklogic.client.eval.ServerEvaluationCall;
7+
import com.marklogic.client.ext.helper.LoggingObject;
8+
import com.marklogic.client.ext.util.EvalResultIterator;
9+
10+
/**
11+
* Builds a QueryBatcher based on either an XQuery or Javascript query.
12+
* <p>
13+
* Because this will likely use either cts:uris or cts.uris, this class checks to see if your query starts with
14+
* "cts:" or "cts." but not "cts:uris" or "cts.uris". If it does, it assumes that your query is the 3rd argument for a
15+
* cts:uris or cts.uris call and wraps it appropriately. You can disable this behavior by calling
16+
* setWrapQueryIfAppropriate(false).
17+
*/
18+
public class UrisQueryQueryBatcherBuilder extends LoggingObject implements QueryBatcherBuilder {
19+
20+
private String xquery;
21+
private String javascript;
22+
private boolean wrapQueryIfAppropriate = true;
23+
24+
/**
25+
* Looks for "cts:" in the query - if it's found, then assumes this is XQuery; otherwise, Javascript. If this isn't
26+
* reliable for your query, use one of the static methods on this class to explicitly declare the type of your query.
27+
*
28+
* @param query
29+
*/
30+
public UrisQueryQueryBatcherBuilder(String query) {
31+
if (query.contains("cts:")) {
32+
xquery = query;
33+
} else {
34+
javascript = query;
35+
}
36+
}
37+
38+
/**
39+
* Empty constructor used by the static methods on this class.
40+
*/
41+
protected UrisQueryQueryBatcherBuilder() {
42+
}
43+
44+
public static UrisQueryQueryBatcherBuilder withXquery(String xquery) {
45+
UrisQueryQueryBatcherBuilder b = new UrisQueryQueryBatcherBuilder();
46+
b.xquery = xquery;
47+
return b;
48+
}
49+
50+
public static UrisQueryQueryBatcherBuilder withJavascript(String javascript) {
51+
UrisQueryQueryBatcherBuilder b = new UrisQueryQueryBatcherBuilder();
52+
b.javascript = javascript;
53+
return b;
54+
}
55+
56+
@Override
57+
public QueryBatcher buildQueryBatcher(DatabaseClient databaseClient, DataMovementManager dataMovementManager) {
58+
ServerEvaluationCall call = databaseClient.newServerEval();
59+
if (javascript != null) {
60+
if (wrapQueryIfAppropriate) {
61+
javascript = wrapJavascriptIfAppropriate(javascript);
62+
}
63+
if (logger.isInfoEnabled()) {
64+
logger.info("Calling JavaScript: " + javascript);
65+
}
66+
call = call.javascript(javascript);
67+
} else if (xquery != null) {
68+
if (wrapQueryIfAppropriate) {
69+
xquery = wrapXqueryIfAppropriate(xquery);
70+
}
71+
if (logger.isInfoEnabled()) {
72+
logger.info("Calling XQuery: " + xquery);
73+
}
74+
call = call.xquery(xquery);
75+
} else {
76+
throw new IllegalStateException("Either xquery or javascript must be defined");
77+
}
78+
79+
return dataMovementManager.newQueryBatcher(new EvalResultIterator(call.eval().iterator()));
80+
}
81+
82+
protected String wrapXqueryIfAppropriate(String query) {
83+
if (query.startsWith("cts:") && !query.startsWith("cts:uris")) {
84+
return String.format("cts:uris((), (), %s)", query);
85+
}
86+
return query;
87+
}
88+
89+
protected String wrapJavascriptIfAppropriate(String query) {
90+
if (query.startsWith("cts.") && !query.startsWith("cts.uris")) {
91+
return String.format("cts.uris(\"\", null, %s)", query);
92+
}
93+
return query;
94+
}
95+
96+
public void setWrapQueryIfAppropriate(boolean wrapQueryIfAppropriate) {
97+
this.wrapQueryIfAppropriate = wrapQueryIfAppropriate;
98+
}
99+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.marklogic.client.ext.util;
2+
3+
import com.marklogic.client.eval.EvalResult;
4+
5+
import java.util.Iterator;
6+
7+
/**
8+
* Adapts an Iterator<EvalResult> to an Iterator<String> so that it can be used easily with a DMSDK QueryBatcher.
9+
*/
10+
public class EvalResultIterator implements Iterator<String> {
11+
12+
private Iterator<EvalResult> evalResults;
13+
14+
public EvalResultIterator(Iterator<EvalResult> evalResults) {
15+
this.evalResults = evalResults;
16+
}
17+
18+
@Override
19+
public boolean hasNext() {
20+
return evalResults.hasNext();
21+
}
22+
23+
@Override
24+
public String next() {
25+
return evalResults.next().getString();
26+
}
27+
}

src/test/java/com/marklogic/client/ext/datamovement/listener/ManageCollectionsTest.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.marklogic.client.ext.datamovement.listener;
22

3+
import com.marklogic.client.datamovement.DeleteListener;
34
import com.marklogic.client.datamovement.QueryBatch;
45
import com.marklogic.client.datamovement.QueryBatchListener;
56
import com.marklogic.client.ext.AbstractIntegrationTest;
67
import com.marklogic.client.ext.batch.RestBatchWriter;
78
import com.marklogic.client.ext.batch.SimpleDocumentWriteOperation;
8-
import com.marklogic.client.datamovement.DeleteListener;
99
import com.marklogic.client.ext.datamovement.QueryBatcherTemplate;
10+
import com.marklogic.client.ext.datamovement.UrisQueryQueryBatcherBuilder;
1011
import com.marklogic.client.ext.helper.ClientHelper;
1112
import org.junit.Test;
1213

@@ -27,15 +28,15 @@ public void setThenAddThenRemove() {
2728
qbt.setBatchSize(1);
2829
qbt.setThreadCount(2);
2930

30-
qbt.setUrisReadyListeners(new QueryBatchListener() {
31+
qbt.addUrisReadyListeners(new QueryBatchListener() {
3132
@Override
3233
public void processEvent(QueryBatch batch) {
3334
System.out.println("Testing, job batch number: " + batch.getJobBatchNumber() + "; " + batch.getJobTicket().getJobId());
3435
}
3536
});
3637

3738
// Clear out the test documents
38-
qbt.applyOnDocuments(new DeleteListener(), firstUri, secondUri);
39+
qbt.applyOnDocumentUris(new DeleteListener(), firstUri, secondUri);
3940

4041
// Insert documents
4142
RestBatchWriter writer = new RestBatchWriter(client, false);
@@ -68,6 +69,38 @@ public void processEvent(QueryBatch batch) {
6869
qbt.applyOnUriPattern(new RemoveCollectionsListener("red"), "dmsdk-test-2*");
6970
assertUriInCollections(firstUri, COLLECTION, "red");
7071
assertUriNotInCollections(secondUri, "red");
72+
73+
// Set via XQuery URIs query
74+
String xquery = String.format("cts:document-query(('%s', '%s'))", firstUri, secondUri);
75+
qbt.applyOnUrisQuery(new SetCollectionsListener(COLLECTION, "green"), xquery);
76+
assertUriInCollections(firstUri, COLLECTION, "green");
77+
assertUriInCollections(secondUri, COLLECTION, "green");
78+
79+
// Set via Javascript URIs query
80+
String javascript = String.format("cts.documentQuery(['%s', '%s'])", firstUri, secondUri);
81+
qbt.applyOnUrisQuery(new SetCollectionsListener(COLLECTION, "blue"), javascript);
82+
assertUriInCollections(firstUri, COLLECTION, "blue");
83+
assertUriInCollections(secondUri, COLLECTION, "blue");
84+
85+
// Set via full XQuery query
86+
xquery = String.format("cts:uris((), (), cts:document-query(('%s', '%s')))", firstUri, secondUri);
87+
qbt.applyOnUrisQuery(new SetCollectionsListener(COLLECTION, "green"), xquery);
88+
assertUriInCollections(firstUri, COLLECTION, "green");
89+
assertUriInCollections(secondUri, COLLECTION, "green");
90+
91+
// Set via full Javascript query
92+
javascript = String.format("cts.uris('', null, cts.documentQuery(['%s', '%s']))", firstUri, secondUri);
93+
qbt.applyOnUrisQuery(new SetCollectionsListener(COLLECTION, "blue"), javascript);
94+
assertUriInCollections(firstUri, COLLECTION, "blue");
95+
assertUriInCollections(secondUri, COLLECTION, "blue");
96+
97+
// Test out a failure listener
98+
xquery = String.format("cts:document-query(('%s', '%s'))", firstUri, secondUri);
99+
UrisQueryQueryBatcherBuilder builder = new UrisQueryQueryBatcherBuilder(xquery);
100+
builder.setWrapQueryIfAppropriate(false); // this will result in a bad query
101+
qbt.apply(new SetCollectionsListener(COLLECTION, "green"), builder);
102+
assertUriInCollections(firstUri, COLLECTION, "blue");
103+
assertUriInCollections(secondUri, COLLECTION, "blue");
71104
}
72105

73106
private void assertUriInCollections(String uri, String... collections) {

src/test/java/com/marklogic/client/ext/datamovement/listener/ManagePermissionsTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void test() {
2424
QueryBatcherTemplate qbt = new QueryBatcherTemplate(newClient("Documents"));
2525

2626
// Clear out the test documents
27-
qbt.applyOnDocuments(new DeleteListener(), uri);
27+
qbt.applyOnDocumentUris(new DeleteListener(), uri);
2828

2929
// Insert documents
3030
RestBatchWriter writer = new RestBatchWriter(client, false);
@@ -42,15 +42,15 @@ public void test() {
4242
assertTrue(perms.get("app-user").contains(DocumentMetadataHandle.Capability.UPDATE));
4343

4444
// Set permissions
45-
qbt.applyOnDocuments(new SetPermissionsListener("alert-user", "read", "alert-user", "update"), uri);
45+
qbt.applyOnDocumentUris(new SetPermissionsListener("alert-user", "read", "alert-user", "update"), uri);
4646
perms = helper.getMetadata(uri).getPermissions();
4747
assertEquals(1, perms.size());
4848
assertEquals(2, perms.get("alert-user").size());
4949
assertTrue(perms.get("alert-user").contains(DocumentMetadataHandle.Capability.READ));
5050
assertTrue(perms.get("alert-user").contains(DocumentMetadataHandle.Capability.UPDATE));
5151

5252
// Add permissions
53-
qbt.applyOnDocuments(new AddPermissionsListener("app-user", "read", "app-user", "update"), uri);
53+
qbt.applyOnDocumentUris(new AddPermissionsListener("app-user", "read", "app-user", "update"), uri);
5454
perms = helper.getMetadata(uri).getPermissions();
5555
assertEquals(2, perms.size());
5656
assertEquals(2, perms.get("alert-user").size());
@@ -61,7 +61,7 @@ public void test() {
6161
assertTrue(perms.get("app-user").contains(DocumentMetadataHandle.Capability.UPDATE));
6262

6363
// Remove permissions
64-
qbt.applyOnDocuments(new RemovePermissionsListener("app-user", "read", "app-user", "update"), uri);
64+
qbt.applyOnDocumentUris(new RemovePermissionsListener("app-user", "read", "app-user", "update"), uri);
6565
perms = helper.getMetadata(uri).getPermissions();
6666
assertEquals(1, perms.size());
6767
assertEquals(2, perms.get("alert-user").size());

0 commit comments

Comments
 (0)