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>.