Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add removeRecursive and listSubTreeBFS methods #88

Merged
merged 3 commits into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ This module has been tested to work with ZooKeeper version 3.4.*.
+ [close](#void-close)
+ [create](#void-createpath-data-acls-mode-callback)
+ [remove](#void-removepath-version-callback)
+ [removeRecursive](#void-removerecursivepath-version-callback)
+ [exists](#void-existspath-watcher-callback)
+ [getChildren](#void-getchildrenpath-watcher-callback)
+ [listSubTreeBFS](#void-listsubtreebfspath-callback)
+ [getData](#void-getdatapath-watcher-callback)
+ [setData](#void-setdatapath-data-version-callback)
+ [getACL](#void-getaclpath-callback)
Expand Down Expand Up @@ -225,7 +227,7 @@ Create a node with given path, data, acls and mode.
* path `String` - Path of the node.
* data `Buffer` - The data buffer, optional, defaults to null.
* acls `Array` - An array of [ACL](#acl) objects, optional, defaults to
`ACL.OPEN_ACL_UNSAFE`
`ACL.OPEN_ACL_UNSAFE`
* mode `CreateMode` - The creation mode, optional, defaults to
`CreateMode.PERSISTENT`
* callback(error, path) `Function` - The callback function.
Expand Down Expand Up @@ -277,6 +279,31 @@ zookeeper.remove('/test/demo', -1, function (error) {

---

#### void removeRecursive(path, [version], callback)

Deletes a node and all its children with the given path and version.

**Arguments**

* path `String` - Path of the node.
* version `Number` - The version of the node, optional, defaults to -1.
* callback(error) `Function` - The callback function.

**Example**

```javascript
zookeeper.removeRecursive('/test/demo', -1, function (error) {
if (error) {
console.log(error.stack);
return;
}

console.log('Nodes removed.');
});
```

---

#### void exists(path, [watcher], callback)

Check the existence of a node. The callback will be invoked with the
Expand Down Expand Up @@ -346,6 +373,33 @@ zookeeper.getChildren('/test/demo', function (error, children, stats) {
});
```

---

#### void listSubTreeBFS(path, callback)

Retrieve a list of all children including itself for the given node path.

**Arguments**

* path `String` - Path of the node.
* callback(error, children) `Function` - The callback function. The
children is an array of strings.

**Example**

```javascript
zookeeper.listSubTreeBFS('/test/demo', function (error, children) {
if (error) {
console.log(error.stack);
return;
}

console.log('Children are: %j.', children);
});
```

---

#### void getData(path, [watcher], callback)

Retrieve the data and the stat of the node of the given path. If the watcher
Expand Down Expand Up @@ -502,7 +556,7 @@ Create given path in a way similar to `mkdir -p`.
* path `String` - Path of the node.
* data `Buffer` - The data buffer, optional, defaults to `null`.
* acls `Array` - An array of [ACL](#acl) objects, optional, defaults to
`ACL.OPEN_ACL_UNSAFE`
`ACL.OPEN_ACL_UNSAFE`
* mode `CreateMode` - The creation mode, optional, defaults to
`CreateMode.PERSISTENT`
* callback(error, path) `Function` - The callback function.
Expand Down Expand Up @@ -590,7 +644,7 @@ var pwd = client.getSessionPassword();
---

#### Number getSessionTimeout()

Returns the *negotiated* session timeout (in milliseconds) for this client
instance. The value returned is not valid until the client connects to a server
and may change after a re-connect.
Expand Down Expand Up @@ -660,7 +714,7 @@ Optionally, you can register watcher functions when calling
[`exists`](#void-existspath-watcher-callback),
[`getChildren`](#void-getchildrenpath-watcher-callback) and
[`getData`](#void-getdatapath-watcher-callback) methods. The watcher function
will be called with an instance of `Event`.
will be called with an instance of `Event`.

**Properties**

Expand Down Expand Up @@ -743,7 +797,7 @@ Add a create operation with given path, data, acls and mode.
* path `String` - Path of the node.
* data `Buffer` - The data buffer, optional, defaults to null.
* acls `Array` - An array of [ACL](#acl) objects, optional, defaults to
`ACL.OPEN_ACL_UNSAFE`
`ACL.OPEN_ACL_UNSAFE`
* mode `CreateMode` - The creation mode, optional, defaults to
`CreateMode.PERSISTENT`

Expand Down Expand Up @@ -773,7 +827,7 @@ Add a check (existence) operation with given path and optional version.
---

#### Transaction remove(path, data, version)

Add a delete operation with the given path and optional version.

**Arguments**
Expand Down Expand Up @@ -841,7 +895,7 @@ zookeeper.create('/test/demo', function (error, path) {

#### Number getCode()

Return the error code of the exception.
Return the error code of the exception.

---

Expand Down
20 changes: 20 additions & 0 deletions examples/listSubTreeBFS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var zookeeper = require('../index.js');

var client = zookeeper.createClient(process.argv[2]);
var path = process.argv[3];

client.once('connected', function () {
console.log('Connected to the server.');

client.listSubTreeBFS(path, function (error, children) {
if (error) {
console.log('Failed to list all child nodes of %s due to:', path, error);
return;
}
console.log('All child nodes of %s are: %j', path, children);

client.close();
});
});

client.connect();
20 changes: 20 additions & 0 deletions examples/removeRecursive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var zookeeper = require('../index.js');

var client = zookeeper.createClient(process.argv[2]);
var path = process.argv[3];

client.once('connected', function () {
console.log('Connected to the server.');

client.removeRecursive(path, function (error) {
if (error) {
console.log('Failed to remove all nodes for %s: %s', path, error);
} else {
console.log('Removed all nodes for: %s', path);
}

client.close();
});
});

client.connect();
78 changes: 78 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,45 @@ Client.prototype.remove = function (path, version, callback) {
);
};

/**
* Deletes a node and all its children.
*
* @param path {String} The node path.
* @param [version=-1] {Number} The version of the node.
* @param callback {Function} The callback function.
*/
Client.prototype.removeRecursive = function(path, version, callback) {
if (!callback) {
callback = version;
version = -1;
}

Path.validate(path);

assert(typeof callback === 'function', 'callback must be a function.');
assert(typeof version === 'number', 'version must be a number.');

var self = this;

self.listSubTreeBFS(path, function (error, children) {
if (error) {
callback(error);
return;
}
async.eachSeries(children.reverse(), function (nodePath, next) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverse is called here to remove leaf nodes first

self.remove(nodePath, version, function(err) {
// Skip NO_NODE exception
if (err && err.getCode() === Exception.NO_NODE) {
next(null);
return;
}

next(err);
});
}, callback);
});
};

/**
* Set the data for the node of the given path if such a node exists and the
* optional given version matches the version of the node (if the given
Expand Down Expand Up @@ -814,6 +853,45 @@ Client.prototype.getChildren = function (path, watcher, callback) {
);
};

/**
* BFS list of the system under path. Note that this is not an atomic snapshot of
* the tree, but the state as it exists across multiple RPCs from clients to the
* ensemble.
*
* @method listSubTreeBFS
* @param path {String} The node path.
* @param callback {Function} The callback function.
*/
Client.prototype.listSubTreeBFS = function(path, callback) {
Path.validate(path);
assert(typeof callback === 'function', 'callback must be a function.');

var self = this;
var tree = [path];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This includes the path that was passed in as part of the results, which is also what ZKUtil.listSubTreeBFS seems to do.


async.reduce(tree, tree, function(memo, item, next) {
self.getChildren(item, function (error, children) {
if (error) {
next(error);
return;
}
if (!children || !Array.isArray(children) || !children.length) {
next(null, tree);
return;
}
children.forEach(function(child) {
var childPath = item + '/' + child;

if (item === '/') {
childPath = item + child;
}
tree.push(childPath);
});
next(null, tree);
});
}, callback);
};

/**
* Create node path in the similar way of `mkdir -p`
*
Expand Down