A common question that gets asked in the Couchbase forums and on Stack Overflow is how to
delete all records from a bucket that have a key that starts with some value. In other words, how to delete all records that share a
particular compound key. The simple solution is to create a Couchbase view and execute a range query.
You can execute range queries with all the Couchbase SDKs, but for this particular example we’re going to see how to do it with Node.js.
The Prerequisites
Before attempting the following, you should have the following installed and configured:
- Node.js and Node Package Manager (NPM) installed
- Couchbase 3.0+
Our Sample Data
For the rest of this example we’re going to use some sample documents in a bucket that I’m going to call testbucket.
The documents in our bucket will have the following key names:
- user::nraboy
- user::mingenthron
- user::blawson
- product::1
- product::2
- product::3
The content of these documents don’t really matter for this example. What does matter is the prefix that I’ve chosen to use for each of the
two document types. I’ve chosen to use user:: to represent user documents and product:: to represent
product documents. The complete keys represent compound keys.
Creating our Couchbase View
We have many documents (not really) in our Couchbase bucket. Chances are we’re not going to remember all the keys to do direct key-value
lookups. For this reason we’re going to need to create a Couchbase View.
You can create Couchbase Views through code, but for this particular example we’re just going to create one via the Couchbase Dashboard. Sign
into the Couchbase Dashboard and click the Views tab and select the testbucket that you created earlier.
Choose to create a new development view and call the design document _design/dev_docs with a view name of
by_id. The default view code should be fine, but just in case it is not, you want to make sure the view looks like the
following:
1 2 3 4 5 |
function (doc, meta) { emit(meta.id, null); } |
This view will emit all documents and the key will be each of the documents ids.
Our Node.js Application
At this point we’re going to create a fresh Node.js application to keep things easy to understand.
Preparing the Project
Using your Terminal (Mac / Linux) or Command Prompt (Windows), execute the following command:
1 2 3 |
npm init |
Answer all the questions and you should be good. You can also just create your NPM package.json file from scratch and
add the following content:
1 2 3 4 5 6 7 8 9 |
{ "name": "mass-delete-nodejs", "version": "1.0.0", "description": "Delete all documents prefixed with a particular value", "author": "Couchbase, Inc.", "license": "MIT" } |
We’re not done. We need to install the project dependencies before we can start planning out this
application. From the Command Prompt or Terminal, run the following command:
1 2 3 |
npm install couchbase express --save |
This will install Express Framework, and the Couchbase Node.js SDK.
The Code That Matters
If you haven’t already, create an app.js file that sits next to your package.json file in your project.
The contect of this file should be as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
var express = require("express"); var couchbase = require("couchbase"); var app = express(); var bucket = (new couchbase.Cluster("http://127.0.0.1:8091")).openBucket("testbucket"); var ViewQuery = couchbase.ViewQuery; var query = ViewQuery.from('docs', 'by_id'); query.range("user::", "user::" + "u02ad", false); bucket.query(query, function(error, results) { if(error) { return console.log(error); } console.log("Found " + results.length + " documents to delete"); for(i in results) { bucket.remove(results[i].id, function(error, result) { console.log("Deleting " + results[i].key); }); } }); var server = app.listen(3000, function () { console.log("Listening on port %s...", server.address().port); }); |
There is a lot going on here so I’m going to break it down.
1 2 3 4 5 |
var express = require("express"); var couchbase = require("couchbase"); var app = express(); |
Not too much to care about above. We’re just including the Couchbase SDK and Express Framework for use in our application. However, below
you’ll see that we are connecting to our cluster and opening a particular bucket:
1 2 3 |
var bucket = (new couchbase.Cluster("http://127.0.0.1:8091")).openBucket("testbucket"); |
We’re connecting to a locally hosted Couchbase instance and opening our testbucket mentioned earlier.
Now we’re going to look at something great!
1 2 3 4 5 6 |
var ViewQuery = couchbase.ViewQuery; var query = ViewQuery.from('docs', 'by_id'); query.range("user::", "user::" + "u02ad", false); |
We are preparing a ViewQuery object from our _design/dev_docs design document and by_id view. To only query
for particular documents we’re going to use what is called a range query. We already know that our view is returning documents with the
document id as the key value. This means with a range query we can determine what keys we want to receive.
In the range query, the start key will be user:: because we want to delete all user documents. The end key will also
contain user::, but have the unicode character u02ad appended to it. This will capture everything
prefixed with what we’re looking for. More information on this type of range query can be seen in the
official Couchbase documentation.
Finally we execute this query and capture the results. The results are looped through and we will delete every record one by one as seen
in the following:
1 2 3 4 5 6 7 |
for(i in results) { bucket.remove(results[i].id, function(error, result) { console.log("Deleting " + results[i].key); }); } |
Every delete issued in Node.js will be non-blocking so the application layer won’t lock up. When a delete request hits Couchbase Server,
the document is then marked for deletion and is then later deleted when compaction happens.
1 2 3 |
node app.js |
Run the above line to see this project in action.
Conclusion
Using range queries you can query for particular documents in a view and then choose to delete them. The particular documents in this example
had compound keys prefixed with user:: in the key.
Can this be done with Couchbase server 2.5 and java client 1.4 as well?
Yes, this concept should work there as well, the view should be the same, but the java code will obviously be different than above.
Another option I always recommend to people depending on how soon they need to delete things and the load they can put on their Couchbase cluster is to instead of deleting the object directly, I put a random TTL on each of the objects between now and X days from now. Then the Couchbase cluster will take care of deleting the object from the bucket when the TTL expires with diminished load on the cluster.