Skip to content

Commit b72e46e

Browse files
committed
Merge branch 'release/0.2.21'
2 parents 2aac1e5 + ca53940 commit b72e46e

10 files changed

Lines changed: 558 additions & 52 deletions

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
## Version 0.2.21 — October 2, 2013
2+
3+
- New `GraphDatabase` methods to create, read, and delete node and
4+
relationship indexes, e.g. `createNodeIndex()`, `getNodeIndexes()`, and
5+
`deleteNodeIndex()` respectively for nodes.
6+
7+
- New `Node` and `Relationship` `unindex()` methods to remove the current
8+
node or relationship from an index, optionally under the given key or
9+
key-value pair.
10+
11+
- Both of the above were contributed by [@flipside][] — many thanks!
12+
(Pull [#55][])
13+
14+
- The `Node` class's `createRelationshipTo()` and `createRelationshipFrom()`
15+
methods now support omitting the `data` parameter.
16+
(Issue [#87][]; thanks [@rpsirois][]!)
17+
18+
- The Cypher `query()` method now guards against malformed responses, which
19+
may happen if Neo4j runs out of memory serving inefficient queries.
20+
(Issue [#71][])
21+
22+
[#55]: https://github.com/thingdom/node-neo4j/pull/55
23+
[#71]: https://github.com/thingdom/node-neo4j/issue/71
24+
[#87]: https://github.com/thingdom/node-neo4j/issue/87
25+
26+
[@rpsirois]: https://github.com/rpsirois
27+
128
## Version 0.2.20 — April 17, 2013
229

330
- Improved error handling: this library now catches and parses a couple of

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This is a client library for accessing [Neo4j][], a graph database, from
44
[Node.js][]. It uses Neo4j's [REST API][neo4j-rest-api].
55

6-
This library supports and has been tested against Neo4j 1.4 through Neo4j 1.8.
6+
This library supports and has been tested against Neo4j 1.4 through Neo4j 1.9.
77

88

99
## Installation
@@ -67,10 +67,11 @@ dependency in your package.json, ***please* specify something like `0.2.x` or
6767

6868
You'll also need a local Neo4j database instance for the tests:
6969

70-
curl http://dist.neo4j.org/neo4j-community-1.8.2-unix.tar.gz --O neo4j-community-1.8.2-unix.tar.gz
71-
tar -zxvf neo4j-community-1.8.2-unix.tar.gz
72-
rm neo4j-community-1.8.2-unix.tar.gz
73-
ln -s neo4j-community-1.8.2/bin/neo4j neo4j
70+
NEO4J_VERSION=neo4j-community-1.9.4
71+
curl http://dist.neo4j.org/$NEO4J_VERSION-unix.tar.gz --O $NEO4J_VERSION-unix.tar.gz
72+
tar -zxvf $NEO4J_VERSION-unix.tar.gz
73+
rm $NEO4J_VERSION-unix.tar.gz
74+
ln -s $NEO4J_VERSION/bin/neo4j neo4j
7475

7576
If you're new to Neo4j, read the [Getting Started][neo4j-getting-started] page.
7677
Start the server:

lib/GraphDatabase._coffee

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,174 @@ module.exports = class GraphDatabase
406406
catch error
407407
throw adjustError error
408408

409+
### Indexes: ###
410+
411+
#
412+
# Get the current existing node indexes.
413+
# "Returns" (via callback) an array of string index names, but the array
414+
# also serves as a dictionary of index name to its config properties.
415+
#
416+
# @param callback {Function}
417+
# @return {Array<String>}
418+
#
419+
# db.getNodeIndexes(function (err, indexes) {
420+
# if (err) throw err;
421+
# indexes.forEach(function (name) {
422+
# console.log('Index', name, 'has config:', indexes[name]);
423+
# });
424+
# });
425+
#
426+
getNodeIndexes: (_) ->
427+
try
428+
services = @getServices _
429+
response = @_request.get services.node_index, _
430+
431+
if response.statusCode not in [status.OK, status.NO_CONTENT]
432+
# Database error
433+
throw response
434+
435+
# Success: transform the map into an array-map hybrid.
436+
map = response.body or {}
437+
arr = []
438+
for name, props of map
439+
arr.push name
440+
arr[name] = props
441+
return arr
442+
443+
catch error
444+
throw adjustError error
445+
446+
#
447+
# Create node index.
448+
#
449+
# @param name {String}
450+
# @param callback {Function}
451+
#
452+
createNodeIndex: (name, _) ->
453+
try
454+
services = @getServices _
455+
456+
response = @_request.post
457+
url: services.node_index
458+
json: {name}
459+
, _
460+
461+
if response.statusCode isnt status.CREATED
462+
# Database error
463+
throw response
464+
465+
# Success
466+
return
467+
468+
catch error
469+
throw adjustError error
470+
471+
#
472+
# Delete a node index.
473+
#
474+
# @param name {String}
475+
# @param callback {Function}
476+
#
477+
deleteNodeIndex: (name, _) ->
478+
try
479+
services = @getServices _
480+
response = @_request.del
481+
url: "#{services.node_index}/#{encodeURIComponent name}"
482+
, _
483+
484+
if response.statusCode isnt status.NO_CONTENT
485+
# Database error
486+
throw response
487+
488+
# Success
489+
return
490+
491+
catch error
492+
throw adjustError error
493+
494+
#
495+
# Get the current existing relationship indexes.
496+
# "Returns" (via callback) an array of string index names, but the array
497+
# also serves as a dictionary of index name to its config properties.
498+
#
499+
# @param callback {Function}
500+
# @return {Array<String>}
501+
#
502+
# db.getRelationshipIndexes(function (err, indexes) {
503+
# if (err) throw err;
504+
# indexes.forEach(function (name) {
505+
# console.log('Index', name, 'has config:', indexes[name]);
506+
# });
507+
# });
508+
#
509+
getRelationshipIndexes: (_) ->
510+
try
511+
services = @getServices _
512+
response = @_request.get services.relationship_index, _
513+
514+
if response.statusCode not in [status.OK, status.NO_CONTENT]
515+
# Database error
516+
throw response
517+
518+
# Success: transform the map into an array-map hybrid.
519+
map = response.body or {}
520+
arr = []
521+
for name, props of map
522+
arr.push name
523+
arr[name] = props
524+
return arr
525+
526+
catch error
527+
throw adjustError error
528+
529+
#
530+
# Create relationship index.
531+
#
532+
# @param name {String}
533+
# @param callback {Function}
534+
#
535+
createRelationshipIndex: (name, _) ->
536+
try
537+
services = @getServices _
538+
539+
response = @_request.post
540+
url: "#{services.relationship_index}/"
541+
json: {name}
542+
, _
543+
544+
if response.statusCode isnt status.CREATED
545+
# Database error
546+
throw response
547+
548+
# Success
549+
return
550+
551+
catch error
552+
throw adjustError error
553+
554+
#
555+
# Delete a relationship index.
556+
#
557+
# @param name {String}
558+
# @param callback {Function}
559+
#
560+
deleteRelationshipIndex: (name, _) ->
561+
try
562+
services = @getServices _
563+
response = @_request.del
564+
url: "#{services.relationship_index}/#{encodeURIComponent name}"
565+
, _
566+
567+
if response.statusCode isnt status.NO_CONTENT
568+
# Database error
569+
throw response
570+
571+
# Success
572+
return
573+
574+
catch error
575+
throw adjustError error
576+
409577
### Misc/Other: ###
410578

411579
#
@@ -471,6 +639,23 @@ module.exports = class GraphDatabase
471639

472640
# Success: build result maps, and transform nodes/relationships
473641
body = response.body
642+
643+
# Update: guard against streaming errors where the response code
644+
# is still 200, but the body is malformed JSON, and node-request
645+
# swallows the error parsing the JSON:
646+
# https://github.com/thingdom/node-neo4j/issues/71
647+
# This is a manual fix for only this operation, since we know
648+
# here that the response should always be a JSON object.
649+
if typeof body isnt 'object'
650+
throw new Error """
651+
Malformed Cypher response for query:
652+
653+
#{query}
654+
655+
Neo4j may have run out of memory processing this query.
656+
Maybe try a more efficient query?
657+
"""
658+
474659
columns = body.columns
475660
results = for row in body.data
476661
map = {}

lib/Node._coffee

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,13 @@ module.exports = class Node extends PropertyContainer
118118
#
119119
# @param index {String} The name of the index, e.g. `'users'`.
120120
# @param key {String} The key to index under, e.g. `'username'`.
121-
# @param value {Object} The value to index under, e.g. `'aseemk'`.
121+
# @param value {String} The value to index under, e.g. `'aseemk'`.
122122
# @param callback {Function}
123123
#
124124
index: (index, key, value, _) ->
125125
try
126-
# TODO
127126
if not @exists
128-
throw new Error 'Node must exists before indexing properties'
127+
throw new Error 'Node must exist before indexing.'
129128

130129
services = @db.getServices _
131130
version = @db.getVersion _
@@ -162,30 +161,94 @@ module.exports = class Node extends PropertyContainer
162161
throw adjustError error
163162

164163
#
165-
# Create and "return" (via callback) a relationship of the given type and
166-
# with the given properties from this node to another node.
164+
# Delete this node from the given index, optionally under the given key
165+
# or key-value pair. (A key is required if a value is given.)
166+
#
167+
# @param index {String} The name of the index, e.g. `'users'`.
168+
# @param key {String} (Optional) The key to unindex from, e.g. `'username'`.
169+
# @param value {String} (Optional) The value to unindex from, e.g. `'aseemk'`.
170+
# @param callback {Function}
171+
#
172+
unindex: (index, key, value, _) ->
173+
# see below for the code that normalizes the args;
174+
# this function assumes all args are present (but may be null/etc.).
175+
try
176+
if not @exists
177+
throw new Error 'Node must exist before unindexing.'
178+
179+
services = @db.getServices _
180+
181+
key = encodeURIComponent key if key
182+
value = encodeURIComponent value if value
183+
base = "#{services.node_index}/#{encodeURIComponent index}"
184+
url =
185+
if key and value
186+
"#{base}/#{key}/#{value}/#{@id}"
187+
else if key
188+
"#{base}/#{key}/#{@id}"
189+
else
190+
"#{base}/#{@id}"
191+
192+
response = @_request.del url, _
193+
194+
if response.statusCode isnt status.NO_CONTENT
195+
# database error
196+
throw response
197+
198+
# success
199+
return
200+
201+
catch error
202+
throw adjustError error
203+
204+
# helper for overloaded unindex() method:
205+
do (actual = @::unindex) =>
206+
@::unindex = (index, key, value, callback) ->
207+
if typeof key is 'function'
208+
callback = key
209+
key = null
210+
value = null
211+
else if typeof value is 'function'
212+
callback = value
213+
value = null
214+
215+
actual.call @, index, key, value, callback
216+
217+
#
218+
# Create and "return" (via callback) a relationship of the given type, and
219+
# optionally with the given properties, from this node to another node.
167220
#
168221
# @param otherNode {Node}
169222
# @param type {String}
170-
# @param data {Object} The properties this relationship should have.
223+
# @param data {Object} (Optional) The properties this relationship should have.
171224
# @param callback {Function}
172225
# @return {Relationship}
173226
#
174-
createRelationshipTo: (otherNode, type, data, _) ->
175-
@_createRelationship this, otherNode, type, data, _
227+
createRelationshipTo: (otherNode, type, data, cb) ->
228+
# support omitting data:
229+
if typeof data is 'function'
230+
cb = data
231+
data = null
232+
233+
@_createRelationship this, otherNode, type, data, cb
176234

177235
#
178-
# Create and "return" (via callback) a relationship of the given type and
179-
# with the given properties from another node to this node.
236+
# Create and "return" (via callback) a relationship of the given type, and
237+
# optionally with the given properties, from another node to this node.
180238
#
181239
# @param otherNode {Node}
182240
# @param type {String}
183-
# @param data {Object} The properties this relationship should have.
241+
# @param data {Object} (Optional) The properties this relationship should have.
184242
# @param callback {Function}
185243
# @return {Relationship}
186244
#
187-
createRelationshipFrom: (otherNode, type, data, _) ->
188-
@_createRelationship otherNode, this, type, data, _
245+
createRelationshipFrom: (otherNode, type, data, cb) ->
246+
# support omitting data:
247+
if typeof data is 'function'
248+
cb = data
249+
data = null
250+
251+
@_createRelationship otherNode, this, type, data, cb
189252

190253
#
191254
# Create and "return" (via callback) a relationship of the given type and
@@ -199,7 +262,7 @@ module.exports = class Node extends PropertyContainer
199262
# @param callback {Function}
200263
# @return {Relationship}
201264
#
202-
_createRelationship: (from, to, type, data, _) ->
265+
_createRelationship: (from, to, type, data={}, _) ->
203266
try
204267
# ensure this node exists
205268
# ensure otherNode exists

0 commit comments

Comments
 (0)