From 4c102a88f51ccb1555e6a985fa56a8833d269e34 Mon Sep 17 00:00:00 2001 From: Mikhail Novikov Date: Thu, 30 Jun 2011 13:36:47 +0300 Subject: [PATCH] Massive refactoring to make code a bit more organized. Indexes are disabled for now. --- cl-neo4j-package.lisp | 2 + cl-neo4j.asd | 23 +++-- conditions.lisp | 42 +++++---- neo4j.lisp | 215 +++++++++++++++++------------------------- utilities.lisp | 58 ++++++++++++ 5 files changed, 184 insertions(+), 156 deletions(-) diff --git a/cl-neo4j-package.lisp b/cl-neo4j-package.lisp index c40f917..8727704 100644 --- a/cl-neo4j-package.lisp +++ b/cl-neo4j-package.lisp @@ -2,6 +2,8 @@ (defpackage #:cl-neo4j (:use #:cl + #:alexandria + #:anaphora #:json #:json-rpc #:drakma) diff --git a/cl-neo4j.asd b/cl-neo4j.asd index dc08f3f..22a7ebd 100644 --- a/cl-neo4j.asd +++ b/cl-neo4j.asd @@ -10,16 +10,15 @@ :version "0.2" :description "neo4j RESTful Client Interface" :long-description "neo4j RESTful Client Interface." - :depends-on (:drakma - :cl-ppcre - :cl-json) + :depends-on (:alexandria + :anaphora + :drakma + :cl-ppcre + :cl-json) :components ((:file "cl-neo4j-package") - (:file "globals" :depends-on ("cl-neo4j-package")) - (:file "utilities" :depends-on ("globals")) - (:file "conditions" :depends-on ("utilities")) - (:file "struct" :depends-on ("conditions")) - (:file "neo4j" :depends-on ("conditions" "struct")) - (:file "struct-methods" :depends-on ("neo4j")))) - - - + (:file "globals" :depends-on ("cl-neo4j-package")) + (:file "utilities" :depends-on ("globals")) + (:file "conditions" :depends-on ("utilities")) + (:file "struct" :depends-on ("conditions")) + (:file "neo4j" :depends-on ("conditions" "struct")) + (:file "struct-methods" :depends-on ("neo4j")))) diff --git a/conditions.lisp b/conditions.lisp index c10ce5d..1e92603 100644 --- a/conditions.lisp +++ b/conditions.lisp @@ -5,40 +5,48 @@ (property :accessor property :initarg :property) (status :accessor status :initarg :status)) (:report (lambda (condition stream) - (format stream "Unknown status ~A returned for ~A (~A)" - (status condition) (uri condition) (property condition))))) - + (format stream "Unknown status ~A returned for ~A (~A)" + (status condition) (uri condition) (property condition))))) + (define-condition invalid-data-sent-error (error) ((json :accessor json :initarg :json) (uri :accessor uri :initarg :uri)) (:report (lambda (condition stream) - (format stream "Invalid data sent to ~A: ~A" (uri condition) (json condition))))) + (format stream "Invalid data sent to ~A: ~A" (uri condition) (json condition))))) (define-condition node-not-found-error (error) ((uri :accessor uri :initarg :uri) (property :accessor property :initarg :property)) (:report (lambda (condition stream) - (if (slot-boundp condition 'property) - (format stream "Property ~A not found for node ~A" - (property condition) (uri condition)) - (format stream "Node not found ~A" (uri condition)))))) - + (if (slot-boundp condition 'property) + (format stream "Property ~A not found for node ~A" + (property condition) (uri condition)) + (format stream "Node not found ~A" (uri condition)))))) + (define-condition unable-to-delete-node-error (error) ((uri :accessor uri :initarg :uri)) (:report (lambda (condition stream) - (format stream "Unable to delete node ~A. Still has relationships?" (uri condition))))) + (format stream "Unable to delete node ~A. Still has relationships?" (uri condition))))) (define-condition relationship-not-found-error (error) ((uri :accessor uri :initarg :uri) (property :accessor property :initarg :property)) (:report (lambda (condition stream) - (if (slot-boundp condition 'property) - (format stream "Property ~A not found for relationship ~A" - (property condition) (uri condition)) - (format stream "Relationship not found ~A" (uri condition)))))) - + (if (slot-boundp condition 'property) + (format stream "Property ~A not found for relationship ~A" + (property condition) (uri condition)) + (format stream "Relationship not found ~A" (uri condition)))))) + (define-condition index-entry-not-found-error (error) ((uri :accessor uri :initarg :uri)) (:report (lambda (condition stream) - (format stream "Index entry not found ~A" (uri condition))))) - + (format stream "Index entry not found ~A" (uri condition))))) + +(define-condition property-not-found-error (error) + ((uri :accessor uri :initarg :uri) + (property :accessor property :initarg :property)) + (:report (lambda (condition stream) + (format stream "Property ~A not found for ~A ~A" + (first (property condition)) + (second (property condition)) + (third (property condition)))))) \ No newline at end of file diff --git a/neo4j.lisp b/neo4j.lisp index a6e54a0..aeda7d9 100644 --- a/neo4j.lisp +++ b/neo4j.lisp @@ -1,111 +1,64 @@ (in-package #:cl-neo4j) -(defun do-neo4j-query (uri method &key json decode?) - (multiple-value-bind (body status headers uri stream must-close reason) - (http-request uri - :method method - :content json - :content-type (if json "application/json") - :accept "application/json") - (declare (ignore headers uri stream must-close reason)) - (values status (if (and decode? body) - (decode-json-from-string (map 'string #'code-char body)) - body)))) - -(defmacro def-neo4j-fun (name lambda-list method &rest args) - `(progn - (defun ,name ,(append '(&key (host *neo4j-host*) (port *neo4j-port*)) lambda-list) - (let ((uri (format nil "http://~A:~A/db/data/~A" host port ,(cadr (assoc :uri-spec args)))) - (json ,(cond ((cadr (assoc :encode-properties? args)) - `(encode-json-to-string properties)) - ((cadr (assoc :encode-value? args)) - `(encode-json-to-string value)) - ((cadr (assoc :encode-node-id? args)) - `(format nil "http://~A:~A/db/data/node/~A" host port node-id)) - ((cadr (assoc :encode-relationship? args)) - `(encode-json-to-string - (list (cons "to" - (format nil "http://~A:~A/db/data/node/~A" host port to-node-id)) - (cons "type" relationship-type) - (cons "data" properties)))) - (t nil)))) - (multiple-value-bind (body status headers drakma-uri stream must-close reason) - (http-request uri - :method ,method - :content json - :content-type (if json "application/json" nil) - :accept "application/json") - (declare (ignore headers drakma-uri stream must-close reason)) - (case status - ,@(mapcar - #'(lambda (s) s) - (append (cadr (assoc :status-handlers args)) - `((othwerwise - (error 'unknown-return-type-error :uri uri :status status - :property (cond ((boundp 'properties) (symbol-value 'properties)) - ((boundp 'value) (symbol-value 'value)) - ((boundp 'node-id) (symbol-value 'node-id)) - (t nil))))))))))))) - (def-neo4j-fun get-node (node-id) :get (:uri-spec (if node-id (format nil "node/~A" node-id) "")) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body))) - (404 (error 'node-not-found-error :uri uri :property node-id))))) + (200 (decode-json-from-string (map 'string #'code-char body))) + (404 (error 'node-not-found-error :uri uri :property node-id)))) (def-neo4j-fun create-node (properties) :post - (:encode-properties? t) (:uri-spec (format nil "node")) + (:encode properties :string) (:status-handlers - ((201 (decode-json-from-string (map 'string #'code-char body))) - (400 (error 'invalid-data-sent-error :uri uri :json json))))) + (201 (decode-json-from-string (map 'string #'code-char body))) + (400 (error 'invalid-data-sent-error :uri uri :json json)))) (def-neo4j-fun delete-node (node-id) :delete (:uri-spec (format nil "node/~A" node-id)) (:status-handlers - ((204 (values t body)) - (404 (error 'node-not-found-error :uri uri)) - (409 (error 'unable-to-delete-node-error :uri uri))))) + (204 (values t body)) + (404 (error 'node-not-found-error :uri uri)) + (409 (error 'unable-to-delete-node-error :uri uri)))) (def-neo4j-fun set-node-properties (node-id properties) :put - (:encode-properties? t) + (:encode properties :string) (:uri-spec (format nil "node/~A/properties" node-id)) (:status-handlers - ((204 (values t body)) - (400 (error 'invalid-data-sent-error :uri uri :json json)) - (404 (error 'node-not-found-error :uri uri))))) + (204 (values t body)) + (400 (error 'invalid-data-sent-error :uri uri :json json)) + (404 (error 'node-not-found-error :uri uri)))) (def-neo4j-fun get-node-properties (node-id) :get (:uri-spec (format nil "node/~A/properties" node-id)) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body))) - (204 nil) - (404 (error 'node-not-found-error :uri uri))))) + (200 (decode-json-from-string (map 'string #'code-char body))) + (204 nil) + (404 (error 'node-not-found-error :uri uri)))) (def-neo4j-fun del-node-properties (node-id) :delete (:uri-spec (format nil "node/~A/properties" node-id)) (:status-handlers - ((202 (values t body)) - (404 (error 'node-not-found-error :uri uri))))) + (204 (values t body)) + (404 (error 'node-not-found-error :uri uri)))) (def-neo4j-fun set-node-property (node-id property value) :put - (:encode-value? t) (:uri-spec (format nil "node/~A/properties/~A" node-id (if (symbolp property) (string-downcase (symbol-name property)) property))) + (:encode value :string) (:status-handlers - ((204 (values t body)) - (400 (error 'invalid-data-sent-error :uri uri :json json))))) + (204 (values t body)) + (400 (error 'invalid-data-sent-error :uri uri :json json)))) (def-neo4j-fun get-node-property (node-id property) :get @@ -114,8 +67,9 @@ (string-downcase (symbol-name property)) property))) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body))) - (400 (error 'invalid-data-sent-error :uri uri :json json))))) + (200 (decode-json-from-string (map 'string #'code-char body))) + (400 (error 'invalid-data-sent-error :uri uri :json json)) + (404 (error 'property-not-found-error :uri uri :property (list property "node" node-id))))) (def-neo4j-fun del-node-property (node-id property) :delete @@ -124,75 +78,83 @@ (string-downcase (symbol-name property)) property))) (:status-handlers - ((204 (values t body)) - (404 (error 'node-not-found-error :uri uri)) - (409 (error 'unable-to-delete-node-error :uri uri))))) + (204 (values t body)) + (404 (error 'node-not-found-error :uri uri)) + (409 (error 'unable-to-delete-node-error :uri uri)))) + +(def-neo4j-fun get-relationship (relationship-id) + :get + (:uri-spec (format nil "relationship/~A" relationship-id)) + (:status-handlers + (200 (decode-json-from-string (map 'string #'code-char body))) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun create-relationship (node-id to-node-id relationship-type properties) :post - (:encode-relationship? t) (:uri-spec (format nil "node/~A/relationships" node-id)) + (:encode (list to-node-id relationship-type properties) :relationship) (:status-handlers - ((201 (decode-json-from-string (map 'string #'code-char body))) - (400 (error 'invalid-data-sent-error :uri uri :json json)) - (404 (error 'node-not-found-error :uri to-node-id))))) + (201 (decode-json-from-string (map 'string #'code-char body))) + (400 (error 'invalid-data-sent-error :uri uri :json json)) + (404 (error 'node-not-found-error :uri to-node-id)))) (def-neo4j-fun set-relationship-properties (relationship-id properties) :put - (:encode-properties? t) (:uri-spec (format nil "relationship/~A/properties" relationship-id)) + (:encode properties :string) (:status-handlers - ((204 (values t body)) - (400 (error 'invalid-data-sent-error :uri uri :json json)) - (404 (error 'relationship-not-found-error :uri uri))))) + (204 (values t body)) + (400 (error 'invalid-data-sent-error :uri uri :json json)) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun get-relationship-properties (relationship-id) :get (:uri-spec (format nil "relationship/~A/properties" relationship-id)) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body))) - (204 nil) - (404 (error 'relationship-not-found-error :uri uri))))) + (200 (decode-json-from-string (map 'string #'code-char body))) + (204 nil) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun del-relationship-properties (relationship-id) :delete (:uri-spec (format nil "relationship/~A/properties" relationship-id)) (:status-handlers - ((202 (values t body)) - (404 (error 'relationship-not-found-error :uri uri))))) + (204 (values t body)) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun set-relationship-property (relationship-id property value) :put - (:encode-value? t) (:uri-spec (format nil "relationship/~A/properties/~A" relationship-id property)) + (:encode value :string) (:status-handlers - ((204 (values t body)) - (400 (error 'invalid-data-sent-error :uri uri :json json)) - (404 (error 'relationship-not-found-error :uri uri))))) + (204 (values t body)) + (400 (error 'invalid-data-sent-error :uri uri :json json)) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun get-relationship-property (relationship-id property) :get (:uri-spec (format nil "relationship/~A/properties/~A" relationship-id property)) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body))) - (400 (error 'invalid-data-sent-error :uri uri :json json))))) + (200 (decode-json-from-string (map 'string #'code-char body))) + (400 (error 'invalid-data-sent-error :uri uri :json json)) + (404 (error 'property-not-found-error :uri uri :property (list property "relationship" relationship-id))))) (def-neo4j-fun del-relationship-property (relationship-id property) :delete (:uri-spec (format nil "relationship/~A/properties/~A" relationship-id property)) (:status-handlers - ((204 (values t body)) - (404 (error 'relationship-not-found-error :uri uri))))) + (204 (values t body)) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun delete-relationship (relationship-id) :delete (:uri-spec (format nil "relationship/~A" relationship-id)) (:status-handlers - ((204 (values t body)) - (404 (error 'relationship-not-found-error :uri uri))))) + (204 (values t body)) + (404 (error 'relationship-not-found-error :uri uri)))) (def-neo4j-fun get-node-relationships (node-id direction types) :get @@ -204,61 +166,60 @@ (t direction)) types)) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body))) - (404 (error 'node-not-found-error :uri uri :property node-id))))) + (200 (decode-json-from-string (map 'string #'code-char body))) + (404 (error 'node-not-found-error :uri uri)))) +#+nil ( (def-neo4j-fun list-indices () :get (:uri-spec (format nil "index" )) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body)))))) + (200 (decode-json-from-string (map 'string #'code-char body))))) (def-neo4j-fun add-to-index (node-id key value) :post + (:encode node-id :node-url) (:uri-spec (format nil "index/node/~A/~A" key value)) - (:encode-node-id? t) (:status-handlers - ((201 (values t body))))) + (201 (values t body)))) (def-neo4j-fun remove-from-index (node-id key value) :delete (:uri-spec (format nil "index/node/~A/~A/~A" key value node-id)) (:status-handlers - ((204 (values t body)) - (404 (error 'index-entry-not-found-error :uri uri))))) + (204 (values t body)) + (404 (error 'index-entry-not-found-error :uri uri)))) (def-neo4j-fun query-index (key value) :get (:uri-spec (format nil "index/node/~A/~A" key value)) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body)))))) + (200 (decode-json-from-string (map 'string #'code-char body))))) (def-neo4j-fun query-fulltext-index (key value) :get (:uri-spec (format nil "index/node-fulltext/~A/~A" key value)) (:status-handlers - ((200 (decode-json-from-string (map 'string #'code-char body)))))) + (200 (decode-json-from-string (map 'string #'code-char body))))) +) -(defun traverse (&key (host *neo4j-host*) (port *neo4j-port*) node-id (return-type :node) - (max-depth 1) (order :depth-first) uniqueness relationships prune-evaluator - return-filter) - (let ((uri (format nil "http://~A:~A/db/data/node/~A/traverse/~A" - host port node-id (string-downcase (symbol-name return-type)))) - (json (with-output-to-string (s) - (format s "{\"order\":\"~A\"," (case order - (:depth-first "depth first") - (:breadth-first "breadth first"))) - (if uniqueness (format s "\"uniqueness\":\"~A\"," uniqueness)) - (if relationships (format s "\"relationships\":~A," - (encode-json-to-string relationships))) - (if prune-evaluator (format s "\"prune evaluator\":~A," - (encode-json-to-string prune-evaluator))) - (if return-filter (format s "\"return filter\":~A," - (encode-json-to-string return-filter))) - (format s "\"max depth\":~A}" max-depth)))) - (multiple-value-bind (status body) (do-neo4j-query uri :post :json json) - (case status - (200 (decode-json-from-string (map 'string #'code-char body))) - (404 (error 'node-not-found-error :uri uri)) - (otherwise - (error 'unknown-return-type-error :status status :uri uri :property nil)))))) + +(def-neo4j-fun traverse (node-id (return-type :node) (max-depth 1) (order :depth-first) + uniqueness relationships prune-evaluator return-filter) + :post + (:uri-spec (format nil "node/~A/traverse/~A" node-id (string-downcase (symbol-name return-type)))) + (:encode (list + (cons "order" + (case order + (:depth-first "depth_first") + (:breadth-first "breadth_first"))) + + (cons "uniqueness" uniqueness) + (cons "relationships" relationships) + (cons "prune_evaluator" prune-evaluator) + (cons "return_filter" return-filter) + (cons "max_depth" max-depth)) + :string) + (:status-handlers + (200 (decode-json-from-string (map 'string #'code-char body))) + (404 (error 'node-not-found-error :uri uri)))) diff --git a/utilities.lisp b/utilities.lisp index 50696d6..2b95dea 100644 --- a/utilities.lisp +++ b/utilities.lisp @@ -1 +1,59 @@ (in-package #:cl-neo4j) + +(defmacro def-neo4j-fun (name lambda-list method &rest args) + `(progn + (defun ,name ,(append '(&key (host *neo4j-host*) (port *neo4j-port*)) lambda-list) + (let ((uri (format-neo4j-query host port ,(cadr (assoc :uri-spec args)))) + (json (encode-neo4j-json-payload ,@(aif (assoc :encode args) + (cdr it) + (list '() :string))))) + (multiple-value-bind (status body) + (do-neo4j-query uri ,method :json json) + (case status + ,@(append (cdr (assoc :status-handlers args)) + `((otherwise + (error 'unknown-return-type-error :uri uri :status status + :property (list ,@(mapcar (lambda (arg) (if (consp arg) (car arg) arg)) + lambda-list)))))))))))) + +(defun do-neo4j-query (uri method &key json decode?) + (multiple-value-bind (body status headers uri stream must-close reason) + (http-request uri + :method method + :content json + :content-type (if json "application/json") + :accept "application/json") + (declare (ignore headers uri stream must-close reason)) + (values status (if (and decode? body) + (decode-json-from-string (map 'string #'code-char body)) + body)))) + +(defun format-neo4j-query (host port resource &key (db-postfix "db/data/")) + (format nil "http://~A:~A/~A~A" host port db-postfix resource)) + +(defgeneric encode-neo4j-json-payload (object encode-type &key) + (:method (object encode-type &key) + (declare (ignore encode-type)) + (encode-json-to-string object))) + +(defmethod encode-neo4j-json-payload (object (encode-type (eql :node-url)) &key (host *neo4j-host*) (port *neo4j-port*)) + (declare (ignore encode-type)) + (format-neo4j-query host port (format nil "node/~A" object))) + +(defmethod encode-neo4j-json-payload (object (encode-type (eql :object)) &key) + (declare (ignore encode-type)) + (encode-neo4j-json-payload (mapcar (lambda (el) + (cons (caar el) (if (cadr el) + (encode-neo4j-json-payload (cdar el) + (cadr el)) + (cdar el)))) + object) + :string)) + +(defmethod encode-neo4j-json-payload (object (encode-type (eql :relationship)) &key) + (declare (ignore encode-type)) + (destructuring-bind (to type data) object + (encode-neo4j-json-payload (list (list (cons "to" to) :node-url) + (list (cons "type" type)) + (list (cons "data" data))) + :object))) \ No newline at end of file