From kraison at common-lisp.net Tue May 18 19:43:41 2010 From: kraison at common-lisp.net (Kevin Raison) Date: Tue, 18 May 2010 15:43:41 -0400 Subject: [cl-neo4j-cvs] r1 - Message-ID: Author: kraison Date: Tue May 18 15:43:41 2010 New Revision: 1 Log: initial check in Added: LICENSE README cl-neo4j-package.lisp cl-neo4j.asd conditions.lisp globals.lisp neo4j.lisp utilities.lisp Added: LICENSE ============================================================================== --- (empty file) +++ LICENSE Tue May 18 15:43:41 2010 @@ -0,0 +1,25 @@ + cl-neo4j + + Copyright (c) 2010 Kevin Raison + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + Added: README ============================================================================== --- (empty file) +++ README Tue May 18 15:43:41 2010 @@ -0,0 +1 @@ +TODO. Added: cl-neo4j-package.lisp ============================================================================== --- (empty file) +++ cl-neo4j-package.lisp Tue May 18 15:43:41 2010 @@ -0,0 +1,35 @@ +(in-package #:cl-user) + +(defpackage #:cl-neo4j + (:use #:cl + #:json + #:json-rpc + #:drakma) + (:export #:do-neo4j-query + #:get-node + #:extract-node-id + #:create-node + #:set-node-properties + #:get-node-properties + #:del-node-properties + #:set-node-property + #:get-node-property + #:del-node-property + #:delete-node + #:create-relationship + #:set-relationship-properties + #:get-relationship-properties + #:del-relationship-properties + #:set-relationship-property + #:get-relationship-property + #:del-relationship-property + #:delete-relationship + #:get-node-relationships + #:list-indices + #:add-to-index + #:remove-from-index + #:query-index + #:traverse + ;; Vars + #:*neo4j-host* + #:*neo4j-port*)) Added: cl-neo4j.asd ============================================================================== --- (empty file) +++ cl-neo4j.asd Tue May 18 15:43:41 2010 @@ -0,0 +1,22 @@ +;; ASDF package description for cl-neo4j -*- Lisp -*- + +(defpackage :cl-neo4j-system (:use :cl :asdf)) +(in-package :cl-neo4j-system) + +(defsystem cl-neo4j + :name "neo4j RESTful Client Interface ()" + :maintainer "Kevin Raison" + :author "Kevin Raison " + :version "0.1" + :description "neo4j RESTful Client Interface" + :long-description "neo4j RESTful Client Interface." + :depends-on (:drakma + :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 "neo4j" :depends-on ("conditions")))) + + + Added: conditions.lisp ============================================================================== --- (empty file) +++ conditions.lisp Tue May 18 15:43:41 2010 @@ -0,0 +1,44 @@ +(in-package #:cl-neo4j) + +(define-condition unknown-return-type-error (error) + ((uri :accessor uri :initarg :uri) + (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))))) + +(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))))) + +(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)))))) + +(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))))) + +(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)))))) + +(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))))) + Added: globals.lisp ============================================================================== --- (empty file) +++ globals.lisp Tue May 18 15:43:41 2010 @@ -0,0 +1,4 @@ +(in-package #:cl-neo4j) + +(defvar *neo4j-host* "localhost") +(defvar *neo4j-port* 9999) Added: neo4j.lisp ============================================================================== --- (empty file) +++ neo4j.lisp Tue May 18 15:43:41 2010 @@ -0,0 +1,269 @@ +(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 decode? + (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 ,(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/node/~A" host port node-id)) + ((cadr (assoc :encode-relationship? args)) + `(encode-json-to-string + (list (cons "to" + (format nil "http://~A:~A/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))))))))))))) + +(defun extract-node-id (url) + "http://localhost:9999/node/1" + (subseq url (1+ (search "/" url :from-end t)))) + +(def-neo4j-fun get-node (node-id) + :get + (:uri-spec (if node-id + (format nil "http://~A:~A/node/~A" host port node-id) + (format nil "http://~A:~A/" host port))) + (:status-handlers + ((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 "http://~A:~A/node" host port)) + (:status-handlers + ((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 "http://~A:~A/node/~A" host port 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))))) + +(def-neo4j-fun set-node-properties (node-id properties) + :put + (:encode-properties? t) + (:uri-spec (format nil "http://~A:~A/node/~A/properties" host port 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))))) + +(def-neo4j-fun get-node-properties (node-id) + :get + (:uri-spec (format nil "http://~A:~A/node/~A/properties" host port node-id)) + (:status-handlers + ((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 "http://~A:~A/node/~A/properties" host port node-id)) + (:status-handlers + ((202 (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 "http://~A:~A/node/~A/properties/~A" host port node-id + (if (symbolp property) + (string-downcase (symbol-name property)) + property))) + (:status-handlers + ((204 (values t body)) + (400 (error 'invalid-data-sent-error :uri uri :json json))))) + +(def-neo4j-fun get-node-property (node-id property) + :get + (:uri-spec (format nil "http://~A:~A/node/~A/properties/~A" host port node-id + (if (symbolp property) + (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))))) + +(def-neo4j-fun del-node-property (node-id property) + :delete + (:uri-spec (format nil "http://~A:~A/node/~A/properties/~A" host port node-id + (if (symbolp property) + (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))))) + +(def-neo4j-fun create-relationship (node-id to-node-id relationship-type properties) + :post + (:encode-relationship? t) + (:uri-spec (format nil "http://~A:~A/node/~A/relationships" host port node-id)) + (: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))))) + +(def-neo4j-fun set-relationship-properties (relationship-id properties) + :put + (:encode-properties? t) + (:uri-spec (format nil "http://~A:~A/relationship/~A/properties" host port relationship-id)) + (:status-handlers + ((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 "http://~A:~A/relationship/~A/properties" host port relationship-id)) + (:status-handlers + ((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 "http://~A:~A/relationship/~A/properties" host port relationship-id)) + (:status-handlers + ((202 (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 "http://~A:~A/relationship/~A/properties/~A" + host port relationship-id property)) + (:status-handlers + ((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 "http://~A:~A/relationship/~A/properties/~A" + host port 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))))) + +(def-neo4j-fun del-relationship-property (relationship-id property) + :delete + (:uri-spec (format nil "http://~A:~A/relationship/~A/properties/~A" + host port relationship-id property)) + (:status-handlers + ((204 (values t body)) + (404 (error 'relationship-not-found-error :uri uri))))) + +(def-neo4j-fun delete-relationship (relationship-id) + :delete + (:uri-spec (format nil "http://~A:~A/relationship/~A" host port relationship-id)) + (:status-handlers + ((204 (values t body)) + (404 (error 'relationship-not-found-error :uri uri))))) + +(def-neo4j-fun get-node-relationships (node-id direction types) + :get + (:uri-spec (format nil "http://~A:~A/node/~A/relationships/~A/~{~A~^\\&~}" + host port node-id + (cond ((null direction) "all") + ((symbolp direction) + (string-downcase (symbol-name direction))) + (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))))) + +(def-neo4j-fun list-indices () + :get + (:uri-spec (format nil "http://~A:~A/index" host port)) + (:status-handlers + ((200 (decode-json-from-string (map 'string #'code-char body)))))) + +(def-neo4j-fun add-to-index (node-id key value) + :post + (:uri-spec (format nil "http://~A:~A/index/node/~A/~A" host port key value)) + (:encode-node-id? t) + (:status-handlers + ((201 (values t body))))) + +(def-neo4j-fun remove-from-index (node-id key value) + :delete + (:uri-spec (format nil "http://~A:~A/index/node/~A/~A/~A" host port key value node-id)) + (:status-handlers + ((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 "http://~A:~A/index/node/~A/~A" host port key value)) + (:status-handlers + ((200 (decode-json-from-string (map 'string #'code-char body)))))) + +(def-neo4j-fun query-fulltext-index (key value) + :get + (:uri-spec (format nil "http://~A:~A/index/node-fulltext/~A/~A" host port key value)) + (:status-handlers + ((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/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)))))) + Added: utilities.lisp ============================================================================== --- (empty file) +++ utilities.lisp Tue May 18 15:43:41 2010 @@ -0,0 +1 @@ +(in-package #:cl-neo4j)