You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:26:27 UTC
[sling-org-apache-sling-nosql-couchbase-resourceprovider] 21/42:
SLING-4381 switch to Couchbase 4.0 with N1QL to get rid of couchbase views
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-nosql-couchbase-resourceprovider.git
commit 27c0b6581865b51cc5748b5bb48f741b900d6c30
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Mon Sep 14 23:09:39 2015 +0000
SLING-4381 switch to Couchbase 4.0 with N1QL to get rid of couchbase views
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1703076 13f79535-47bb-0310-9956-ffa450edef68
---
README.md | 18 +----
pom.xml | 9 ++-
src/main/couchbase-views/ancestorPath.js | 40 -----------
src/main/couchbase-views/ancestorPathTester.html | 84 ----------------------
src/main/couchbase-views/parentPath.js | 37 ----------
src/main/couchbase-views/parentPathTester.html | 82 ---------------------
.../impl/CouchbaseNoSqlAdapter.java | 64 ++++++++++++-----
7 files changed, 58 insertions(+), 276 deletions(-)
diff --git a/README.md b/README.md
index 4fd582b..6b88a77 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@ Sling ResourceProvider implementation that uses [Couchbase](http://www.couchbase
Based on the "Apache Sling NoSQL Generic Resource Provider" and "Apache Sling NoSQL Couchbase Client".
+Couchbase Server 4.0 with N1QL support is required for this implementation.
+
Configuration on deployment
---------------------------
@@ -13,21 +15,6 @@ Configuration on deployment
* Additionally a factory configuration for "Apache Sling NoSQL Couchbase Resource Provider Factory" defines the root of the resource tree that should be stored in Couchbase
-Couchbase Views for path-based access
--------------------------------------
-
-For list and delete operations two couchbase views have to be defined and published in the bucket that is used by the resource provider.
-
-Steps to create those views:
-* Log into Couchbase Console
-* Go to "Views" and select the correct bucket
-* Add a new design document via "Create Development View" and name it "\_design/dev\_resourceIndex" (the prefix "\_design/dev\_" is added automatically)
-* Use the name "ancestorPath" for the first view that is created together with the design document
-* Paste the view code from [ancestorPath.js](src/main/couchbase-views/ancestorPath.js) into the editor and save it
-* Create another view named "parentPath", paste the view code from [parentPath.js](src/main/couchbase-views/parentPath.js) and save it
-* Publish the design document so the views are production views
-
-
Run integration tests
---------------------
@@ -36,4 +23,3 @@ To run the integration tests you have to set up a real couchbase server and run
```
mvn -Pcouchbase-integration-test -DcouchbaseHosts=localhost:8091 -DbucketName=test integration-test
```
-
diff --git a/pom.xml b/pom.xml
index 07c509d..49fc370 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,14 @@
<version>2.2.2</version>
<scope>provided</scope>
</dependency>
-
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.3.2</version>
+ <scope>provided</scope>
+ </dependency>
+
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.testing.sling-mock</artifactId>
diff --git a/src/main/couchbase-views/ancestorPath.js b/src/main/couchbase-views/ancestorPath.js
deleted file mode 100644
index f8cd94b..0000000
--- a/src/main/couchbase-views/ancestorPath.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/*
- * Emits for each document the all parent paths - allowing to fetch children and their decendants by path.
- * Includes the path of the item itself.
- */
-function(doc, meta) {
-
- // handle only sling resource documents with a valid path
- if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) {
- return;
- }
- var pathParts = doc.path.split("/");
- if (pathParts.length < 3) {
- return;
- }
-
- while (pathParts.length >= 2) {
- // remove last element to get parent path
- var parentPath = pathParts.join("/");
- emit(parentPath, null);
- pathParts.pop();
- }
-}
diff --git a/src/main/couchbase-views/ancestorPathTester.html b/src/main/couchbase-views/ancestorPathTester.html
deleted file mode 100644
index 9933e6f..0000000
--- a/src/main/couchbase-views/ancestorPathTester.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!DOCTYPE html>
-<!--
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
--->
-<html>
- <head>
- <title>Couchbase View Tester</title>
- <style>body { font-family: Courier }</style>
- </head>
- <body>
-
-<script>
-
-var emit = function(key, value) {
- document.write("[" + key + "]" + "<br/>");
-}
-
-var testFunction = function(doc, meta) {
-
- // handle only sling resource documents with a valid path
- if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) {
- return;
- }
- var pathParts = doc.path.split("/");
- if (pathParts.length < 3) {
- return;
- }
-
- while (pathParts.length >= 2) {
- // remove last element to get parent path
- var parentPath = pathParts.join("/");
- emit(parentPath, null);
- pathParts.pop();
- }
-};
-
-var testInput = [
- null,
- "",
- "abc",
- "/",
- "/content",
- "/content/node1",
- "/content/node1/node2",
- "/content/node1/node2/node3",
- "/content/node1/node2/node3/node4"
-];
-
-</script>
-
- <table border="1">
- <tr>
- <th>Input</th>
- <th>Output</th>
- </tr>
-<script>
-for (var i=0; i < testInput.length; i++) {
- document.write("<tr>")
- document.write("<td>" + testInput[i] + "</td>")
- document.write("<td>")
- testFunction({path: testInput[i], data: {}}, {id: "sling-resource:doc" + i});
- document.write("</td>")
- document.write("</tr>")
-}
-</script>
- </table>
-
- </body>
-</html>
diff --git a/src/main/couchbase-views/parentPath.js b/src/main/couchbase-views/parentPath.js
deleted file mode 100644
index 54c4c1f..0000000
--- a/src/main/couchbase-views/parentPath.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/*
- * Emits for each document the direct parent path - allowing to fetch direct children by path.
- */
-function(doc, meta) {
-
- // handle only sling resource documents with a valid path
- if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) {
- return;
- }
- var pathParts = doc.path.split("/");
- if (pathParts.length < 3) {
- return;
- }
-
- // remove last element to get parent path
- pathParts.pop();
- var parentPath = pathParts.join("/");
- emit(parentPath, null);
-}
diff --git a/src/main/couchbase-views/parentPathTester.html b/src/main/couchbase-views/parentPathTester.html
deleted file mode 100644
index efcc2d1..0000000
--- a/src/main/couchbase-views/parentPathTester.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<!DOCTYPE html>
-<!--
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
--->
-<html>
- <head>
- <title>Couchbase View Tester</title>
- <style>body { font-family: Courier }</style>
- </head>
- <body>
-
-<script>
-
-var emit = function(key, value) {
- document.write("[" + key + "]" + "<br/>");
-}
-
-var testFunction = function(doc, meta) {
-
- // handle only sling resource documents with a valid path
- if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) {
- return;
- }
- var pathParts = doc.path.split("/");
- if (pathParts.length < 3) {
- return;
- }
-
- // remove last element to get parent path
- pathParts.pop();
- var parentPath = pathParts.join("/");
- emit(parentPath, null);
-};
-
-var testInput = [
- null,
- "",
- "abc",
- "/",
- "/content",
- "/content/node1",
- "/content/node1/node2",
- "/content/node1/node2/node3",
- "/content/node1/node2/node3/node4"
-];
-
-</script>
-
- <table border="1">
- <tr>
- <th>Input</th>
- <th>Output</th>
- </tr>
-<script>
-for (var i=0; i < testInput.length; i++) {
- document.write("<tr>")
- document.write("<td>" + testInput[i] + "</td>")
- document.write("<td>")
- testFunction({path: testInput[i], data: {}}, {id: "sling-resource:doc" + i});
- document.write("</td>")
- document.write("</tr>")
-}
-</script>
- </table>
-
- </body>
-</html>
diff --git a/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.java b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.java
index 5ebcc6f..fd4a798 100644
--- a/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.java
+++ b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.java
@@ -18,8 +18,12 @@
*/
package org.apache.sling.nosql.couchbase.resourceprovider.impl;
+import static com.couchbase.client.java.query.Select.select;
+
import java.util.Iterator;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringUtils;
import org.apache.sling.nosql.couchbase.client.CouchbaseClient;
import org.apache.sling.nosql.couchbase.client.CouchbaseKey;
import org.apache.sling.nosql.generic.adapter.AbstractNoSqlAdapter;
@@ -30,9 +34,11 @@ import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.document.JsonDocument;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.error.DocumentAlreadyExistsException;
-import com.couchbase.client.java.view.Stale;
-import com.couchbase.client.java.view.ViewQuery;
-import com.couchbase.client.java.view.ViewRow;
+import com.couchbase.client.java.query.N1qlParams;
+import com.couchbase.client.java.query.N1qlQuery;
+import com.couchbase.client.java.query.N1qlQueryResult;
+import com.couchbase.client.java.query.N1qlQueryRow;
+import com.couchbase.client.java.query.consistency.ScanConsistency;
/**
* {@link org.apache.sling.nosql.generic.adapter.NoSqlAdapter} implementation for Couchbase.
@@ -49,16 +55,18 @@ public final class CouchbaseNoSqlAdapter extends AbstractNoSqlAdapter {
*/
public static final String PN_DATA = "data";
- private static final String VIEW_DESIGN_DOCUMENT = "resourceIndex";
- private static final String VIEW_PARENT_PATH = "parentPath";
- private static final String VIEW_ANCESTOR_PATH = "ancestorPath";
-
private final CouchbaseClient couchbaseClient;
private final String cacheKeyPrefix;
+
+ private static final N1qlParams N1QL_PARAMS = N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS);
public CouchbaseNoSqlAdapter(CouchbaseClient couchbaseClient, String cacheKeyPrefix) {
this.couchbaseClient = couchbaseClient;
this.cacheKeyPrefix = cacheKeyPrefix;
+
+ // make sure primary index is present - ignore error if it is already present
+ Bucket bucket = couchbaseClient.getBucket();
+ bucket.query(N1qlQuery.simple("CREATE PRIMARY INDEX ON " + couchbaseClient.getBucketName()));
}
@Override
@@ -89,8 +97,14 @@ public final class CouchbaseNoSqlAdapter extends AbstractNoSqlAdapter {
public Iterator<NoSqlData> getChildren(String parentPath) {
Bucket bucket = couchbaseClient.getBucket();
// fetch all direct children of this path
- final Iterator<ViewRow> results = bucket.query(
- ViewQuery.from(VIEW_DESIGN_DOCUMENT, VIEW_PARENT_PATH).key(parentPath).stale(Stale.FALSE)).rows();
+ Pattern directChildren = Pattern.compile("^" + parentPath + "/[^/]+$");
+ N1qlQuery query = N1qlQuery.simple(select("*")
+ .from(couchbaseClient.getBucketName())
+ .where("REGEXP_LIKE(`" + PN_PATH + "`, '" + directChildren.pattern() + "')"),
+ N1QL_PARAMS);
+ N1qlQueryResult queryResult = bucket.query(query);
+ handleQueryError(queryResult);
+ final Iterator<N1qlQueryRow> results = queryResult.iterator();
return new Iterator<NoSqlData>() {
@Override
public boolean hasNext() {
@@ -99,8 +113,8 @@ public final class CouchbaseNoSqlAdapter extends AbstractNoSqlAdapter {
@Override
public NoSqlData next() {
- JsonDocument doc = results.next().document();
- JsonObject envelope = doc.content();
+ JsonObject item = results.next().value();
+ JsonObject envelope = item.getObject(couchbaseClient.getBucketName());
String path = envelope.getString(PN_PATH);
JsonObject data = envelope.getObject(PN_DATA);
return new NoSqlData(path, data.toMap(), MultiValueMode.LISTS);
@@ -136,16 +150,34 @@ public final class CouchbaseNoSqlAdapter extends AbstractNoSqlAdapter {
@Override
public boolean deleteRecursive(String path) {
Bucket bucket = couchbaseClient.getBucket();
- // fetch referenced item and all descendants
- Iterator<ViewRow> results = bucket.query(
- ViewQuery.from(VIEW_DESIGN_DOCUMENT, VIEW_ANCESTOR_PATH).key(path).stale(Stale.FALSE)).rows();
+ // fetch all descendants and self for deletion
+ Pattern descendantsAndSelf = Pattern.compile("^" + path + "(/.+)?$");
+ N1qlQuery query = N1qlQuery.simple(select("*")
+ .from(couchbaseClient.getBucketName())
+ .where("REGEXP_LIKE(`" + PN_PATH + "`, '" + descendantsAndSelf.pattern() + "')"),
+ N1QL_PARAMS);
+ N1qlQueryResult queryResult = bucket.query(query);
+ handleQueryError(queryResult);
+ final Iterator<N1qlQueryRow> results = queryResult.iterator();
boolean deletedAny = false;
while (results.hasNext()) {
- ViewRow result = results.next();
- bucket.remove(result.document());
+ JsonObject item = results.next().value();
+ JsonObject envelope = item.getObject(couchbaseClient.getBucketName());
+ String itemPath = envelope.getString(PN_PATH);
+ String itemCacheKey = CouchbaseKey.build(itemPath, cacheKeyPrefix);
+ bucket.remove(itemCacheKey);
deletedAny = true;
}
return deletedAny;
}
+
+ private void handleQueryError(N1qlQueryResult queryResult) {
+ if (!queryResult.parseSuccess()) {
+ throw new RuntimeException("Couchbase query parsing error: " + StringUtils.join(queryResult.errors(), "\n"));
+ }
+ if (!queryResult.finalSuccess()) {
+ throw new RuntimeException("Couchbase query error: " + StringUtils.join(queryResult.errors(), "\n"));
+ }
+ }
}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.