Skip to content

Commit

Permalink
Merge branch 'issue20'
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Klishin committed Feb 6, 2013
2 parents 1cfd5d6 + c19b054 commit 46426c1
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 68 deletions.
12 changes: 11 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
## Changes between Neocons 1.1.0-beta2 and 1.1.0-beta3

No changes yet.
### Correct URI Path Encoding

Neocons now correctly encodes all parts of URIs, which means
index keys and values can contain whitespace and Unicode
characters, for example.

GH issue: #20

### Support upgraded to 0.10.0

Neocons now uses ClojureWerkz Support 0.10.0.



Expand Down
10 changes: 7 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.4.0"]
[cheshire "4.0.3"]
[clj-http "0.6.3"]
[clojurewerkz/support "0.11.0"]]
[clj-http "0.6.4"]
[clojurewerkz/support "0.12.0"]
[clojurewerkz/urly "2.0.0-alpha4"]]
:test-selectors {:default (fn [m] (and (not (:time-consuming m))
(not (:http-auth m))
(not (:edge-features m))
Expand All @@ -22,10 +23,13 @@
;; assorted examples (extra integration tests)
:examples :examples
:batching :batching
:traversal :traversal
:uri-encoding (fn [m] (or (:examples m)
(:indexing m)))
:all (constantly true)}
:source-paths ["src/clojure"]
:profiles {:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]}
:1.5 {:dependencies [[org.clojure/clojure "1.5.0-RC4"]]}
:1.5 {:dependencies [[org.clojure/clojure "1.5.0-RC6"]]}
:dev {:plugins [[codox "0.6.1"]]
:codox {:sources ["src/clojure"]
:output-dir "doc/api"}}}
Expand Down
12 changes: 11 additions & 1 deletion src/clojure/clojurewerkz/neocons/rest.clj
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@


(defrecord Neo4JEndpoint
[version node-uri relationships-uri node-index-uri relationship-index-uri relationship-types-uri batch-uri extensions-info-uri extensions reference-node-uri uri])
[version
node-uri
relationships-uri
node-index-uri
relationship-index-uri
relationship-types-uri
batch-uri
extensions-info-uri
extensions
reference-node-uri
uri])

(def ^{:dynamic true} *endpoint*)

Expand Down
6 changes: 5 additions & 1 deletion src/clojure/clojurewerkz/neocons/rest/conversion.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@

Long
(to-id [^Long id]
id))
id)

nil
(to-id [id]
nil))
7 changes: 1 addition & 6 deletions src/clojure/clojurewerkz/neocons/rest/helpers.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(ns clojurewerkz.neocons.rest.helpers
(:import [java.net URI URL URLEncoder]))
(:import [java.net URI URL]))


;;
Expand All @@ -11,11 +11,6 @@
(let [url (URL. location)]
(Long/valueOf ^String (first (re-seq #"\d+$" (.getPath url))))))


(defn encode
[s]
(URLEncoder/encode (name s) "UTF-8"))

(defn maybe-append
[^String s ^String prefix]
(.toLowerCase (if (.endsWith (.toLowerCase s) (.toLowerCase prefix))
Expand Down
26 changes: 13 additions & 13 deletions src/clojure/clojurewerkz/neocons/rest/nodes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
For more information, see http://docs.neo4j.org/chunked/milestone/rest-api-unique-indexes.html section (19.8.1)"
[idx k v data]
(let [req-body (json/encode {:key k :value v :properties data})
uri (str (:node-index-uri rest/*endpoint*) "/" (encode idx) "?unique")
uri (str (url-with-path (:node-index-uri rest/*endpoint*) idx) "?unique")
{:keys [status headers body]} (rest/POST uri :body req-body)
payload (json/decode body true)
location (:self payload)]
Expand All @@ -65,9 +65,9 @@
This function returns a lazy sequence of results, so you may need to force it using clojure.core/doall"
[xs]
(let [batched (doall (reduce (fn [acc x]
(conj acc {:body x
:to "/node"
:method "POST"})) [] xs))
(conj acc {:body x
:to "/node"
:method "POST"})) [] xs))
{:keys [status headers body]} (rest/POST (:batch-uri rest/*endpoint*) :body (json/encode batched))
payload (map :body (json/decode body true))]
(map instantiate-node-from payload)))
Expand Down Expand Up @@ -189,16 +189,16 @@

(defn add-to-index
"Adds the given node to the index"
([node idx key value]
([node idx ^String key value]
(add-to-index node idx key value false))
([node idx key value unique?]
([node idx ^String key value unique?]
(let [id (to-id node)
req-body (json/encode {:key key :value value :uri (node-location-for rest/*endpoint* (to-id node))})
req-body (json/encode {:key (name key) :value value :uri (node-location-for rest/*endpoint* (to-id node))})
{:keys [status body]} (rest/POST (node-index-location-for rest/*endpoint* idx) :body req-body :query-string (if unique?
{"unique" "true"}
{}))
payload (json/decode body true)]
(instantiate-node-from payload id))))
{"unique" "true"}
{}))
payload (json/decode body true)]
(instantiate-node-from payload id))))


(defn delete-from-index
Expand Down Expand Up @@ -232,14 +232,14 @@
(let [{:keys [status body]} (rest/GET (auto-node-index-lookup-location-for rest/*endpoint* key value))
xs (json/decode body true)]
(map (fn [doc] (fetch-from (:indexed doc))) xs)))
([^String idx key value]
([^String idx ^String key value]
(let [{:keys [status body]} (rest/GET (node-index-lookup-location-for rest/*endpoint* idx key value))
xs (json/decode body true)]
(map (fn [doc] (fetch-from (:indexed doc))) xs))))

(defn find-one
"Finds a single node using the index"
[^String idx key value]
[^String idx ^String key value]
(let [{:keys [status body]} (rest/GET (node-index-lookup-location-for rest/*endpoint* idx key value))
[node] (json/decode body true)]
(when node
Expand Down
79 changes: 52 additions & 27 deletions src/clojure/clojurewerkz/neocons/rest/records.clj
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
(ns clojurewerkz.neocons.rest.records
(:require clojurewerkz.neocons.rest)
(:require clojurewerkz.neocons.rest
[clojurewerkz.urly.core :as u]
[clojure.string :as s])
(:use clojurewerkz.neocons.rest.helpers
[clojurewerkz.neocons.rest.conversion :only [to-id]])
(:import clojurewerkz.neocons.rest.Neo4JEndpoint))
(:import clojurewerkz.neocons.rest.Neo4JEndpoint
java.net.URLEncoder))

(defrecord Node
[id location-uri data relationships-uri create-relationship-uri])
Expand All @@ -19,99 +22,121 @@
(defrecord CypherQueryResponse
[data columns])



(def ^{:const true} slash "/")

(defn ^String encode-slashes
[^String s]
(.replaceAll s "/" "%2F"))

(defn ^String encode-segment
[^String s]
(encode-slashes (u/encode-path s)))



(defn ^String url-with-path
[^String root & segments]
(str root slash (s/join slash segments)))

(defn ^String root-with-path
[^Neo4JEndpoint endpoint & segments]
(str (:uri endpoint) slash (s/join slash segments)))

(defn node-location-for
[^Neo4JEndpoint endpoint ^long id]
(str (:node-uri endpoint) "/" id))
(url-with-path (:node-uri endpoint) id))

(defn rel-location-for
[^Neo4JEndpoint endpoint ^long id]
(str (:relationships-uri endpoint) "/" id))
(url-with-path (:relationships-uri endpoint) id))

(defn node-properties-location-for
[^Neo4JEndpoint endpoint ^long id]
(str (:node-uri endpoint) "/" id "/properties"))
(url-with-path (:node-uri endpoint) id "properties"))

(defn node-property-location-for
[^Neo4JEndpoint endpoint ^long id prop]
(str (node-properties-location-for endpoint id) "/" (encode prop)))
(url-with-path (:node-uri endpoint) id "properties" (encode-segment (name prop))))

(defn node-index-location-for
[^Neo4JEndpoint endpoint idx]
(str (:node-index-uri endpoint) "/" (encode idx)))
(url-with-path (:node-index-uri endpoint) (encode-segment idx)))

(defn rel-index-location-for
[^Neo4JEndpoint endpoint idx]
(str (:relationship-index-uri endpoint) "/" (encode idx)))
(url-with-path (:relationship-index-uri endpoint) (encode-segment idx)))

(defn node-in-index-location-for
([^Neo4JEndpoint endpoint ^long id idx]
(str (:node-index-uri endpoint) "/" (encode idx) "/" id))
(url-with-path (:node-index-uri endpoint) (encode-segment idx) id))
([^Neo4JEndpoint endpoint ^long id idx key]
(str (:node-index-uri endpoint) "/" (encode idx) "/" (encode key) "/" id))
(url-with-path (:node-index-uri endpoint) (encode-segment idx) (encode-segment key) id))
([^Neo4JEndpoint endpoint id idx key value]
(str (:node-index-uri endpoint) "/" (encode idx) "/" (encode key) "/" (encode (str value)) "/" id)))
(url-with-path (:node-index-uri endpoint) (encode-segment idx) (encode-segment key) (encode-segment (str value)) id)))

(defn rel-in-index-location-for
([^Neo4JEndpoint endpoint ^long id idx]
(str (:relationship-index-uri endpoint) "/" (encode idx) "/" id))
(url-with-path (:relationship-index-uri endpoint) (encode-segment idx) id))
([^Neo4JEndpoint endpoint ^long id idx key]
(str (:relationship-index-uri endpoint) "/" (encode idx) "/" (encode key) "/" id))
(url-with-path (:relationship-index-uri endpoint) (encode-segment idx) (encode-segment key) id))
([^Neo4JEndpoint endpoint id idx key value]
(str (:relationship-index-uri endpoint) "/" (encode idx) "/" (encode key) "/" (encode (str value)) "/" id)))
(url-with-path (:relationship-index-uri endpoint) (encode-segment idx) (encode-segment key) (encode-segment (str value)) id)))

(defn node-index-lookup-location-for
[^Neo4JEndpoint endpoint ^String idx key value]
(str (:node-index-uri endpoint) "/" (encode idx) "/" (encode key) "/" (encode (str value))))
(url-with-path (:node-index-uri endpoint) (encode-segment idx) (encode-segment key) (encode-segment (str value))))

(defn auto-node-index-location-for
[^Neo4JEndpoint endpoint]
(str (:uri endpoint) "index/auto/node/"))
(root-with-path endpoint "index" "auto" "node"))

(defn auto-node-index-lookup-location-for
[^Neo4JEndpoint endpoint key value]
(str (auto-node-index-location-for endpoint) (encode key) "/" (encode (str value))))
(url-with-path (auto-node-index-location-for endpoint) (encode-segment key) (encode-segment (str value))))


(defn rel-index-lookup-location-for
[^Neo4JEndpoint endpoint ^String idx key value]
(str (:relationship-index-uri endpoint) "/" (encode idx) "/" (encode key) "/" (encode (str value))))
(url-with-path (:relationship-index-uri endpoint) (encode-segment idx) (encode-segment key) (encode-segment (str value))))

(defn auto-rel-index-location-for
[^Neo4JEndpoint endpoint]
(str (:uri endpoint) "index/auto/relationship/"))
(str (root-with-path endpoint) "index" "auto" "relationship"))

(defn auto-rel-index-lookup-location-for
[^Neo4JEndpoint endpoint key value]
(str (auto-rel-index-location-for endpoint) (encode key) "/" (encode (str value))))
(root-with-path (auto-rel-index-location-for endpoint) (encode-segment key) (encode-segment (str value))))


(defn node-traverse-location-for
[^Neo4JEndpoint endpoint rel]
(str (:node-uri endpoint) "/" (to-id rel) "/traverse/node"))
(url-with-path (:node-uri endpoint) (to-id rel) "traverse" "node"))

(defn path-traverse-location-for
[^Neo4JEndpoint endpoint rel]
(str (:node-uri endpoint) "/" (to-id rel) "/traverse/path"))
(url-with-path (:node-uri endpoint) (to-id rel) "traverse" "path"))

(defn paths-location-for
[^Neo4JEndpoint endpoint rel]
(str (:node-uri endpoint) "/" (to-id rel) "/paths"))
(url-with-path (:node-uri endpoint) (to-id rel) "paths"))

(defn path-location-for
[^Neo4JEndpoint endpoint rel]
(str (:node-uri endpoint) "/" (to-id rel) "/path"))
(url-with-path (:node-uri endpoint) (to-id rel) "path"))

(defn rel-properties-location-for
[^Neo4JEndpoint endpoint rel]
(str (:relationships-uri endpoint) "/" (to-id rel) "/properties"))
(url-with-path (:relationships-uri endpoint) (to-id rel) "properties"))

(defn rel-property-location-for
[^Neo4JEndpoint endpoint rel prop]
(str (rel-properties-location-for endpoint (to-id rel)) "/" (name prop)))
(url-with-path (rel-properties-location-for endpoint (to-id rel)) (encode-segment (name prop))))

(defn rel-traverse-location-for
[^Neo4JEndpoint endpoint rel]
(str (:node-uri endpoint) "/" (to-id rel) "/traverse/relationship"))
(url-with-path (:node-uri endpoint) (to-id rel) "traverse" "relationship"))

(defn instantiate-node-from
([payload]
Expand Down
2 changes: 1 addition & 1 deletion src/clojure/clojurewerkz/neocons/rest/relationships.clj
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
([^Node from ^Node to rel-type idx k v]
(create-unique-in-index from to rel-type idx k v {}))
([^Node from ^Node to rel-type idx k v data]
(let [uri (str (:relationship-index-uri rest/*endpoint*) "/" (encode idx) "/?unique")
(let [uri (str (url-with-path (:relationship-index-uri rest/*endpoint*) idx) "/?unique")
body {:key k
:value v
:start (or (:location-uri from) (node-location-for rest/*endpoint* (to-id from)))
Expand Down
19 changes: 14 additions & 5 deletions test/clojurewerkz/neocons/rest/test/indexing_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
(nodes/create-index name conf)
(nodes/delete-index name)))

(deftest ^{:indexing true :focus true} test-create-a-new-rel-index-with-explicit-configuration
(deftest ^{:indexing true} test-create-a-new-rel-index-with-explicit-configuration
(let [name "rel-index-2"
conf {:type "fulltext" :provider "lucene"}]
(rels/create-index name conf)))
Expand Down Expand Up @@ -73,6 +73,15 @@
home (nodes/create {:uri uri})]
(nodes/add-to-index (:id home) (:name idx) "uri" uri)))

(deftest ^{:indexing true} test-adding-a-node-to-index-with-value-with-spaces
(let [idx (nodes/create-index "things")
s "a value with spaces"
k "a key with spaces"
n (nodes/create {:value s})
_ (nodes/add-to-index (:id n) (:name idx) k s)
n' (nodes/find-one (:name idx) k s)]
(is (= "a value with spaces" (-> n' :data :value)))))

(deftest ^{:indexing true} test-adding-a-node-to-index-as-unique
(let [idx (nodes/create-index "uris")
uri "http://arstechnica.com"
Expand Down Expand Up @@ -148,7 +157,7 @@
(nodes/delete-from-index (:id node2) (:name idx) "url")
(nodes/add-to-index (:id node1) (:name idx) "url" url1)
(nodes/add-to-index (:id node2) (:name idx) "url" url2)
(let [ids (set (map :id (nodes/find (:name idx) :url url1)))]
(let [ids (set (map :id (nodes/find (:name idx) "url" url1)))]
(is (ids (:id node1)))
(is (not (ids (:id node2)))))))

Expand All @@ -160,15 +169,15 @@
rel (rels/create node1 node2 :links {:url url})]
(rels/delete-from-index (:id rel) (:name idx) "target-url")
(rels/add-to-index (:id rel) (:name idx) "target-url" url)
(let [ids (set (map :id (rels/find (:name idx) :target-url url)))]
(let [ids (set (map :id (rels/find (:name idx) "target-url" url)))]
(is (ids (:id rel))))))

(deftest ^{:indexing true} test-finding-a-node-with-url-unsafe-key-to-index
(let [idx (nodes/create-index "uris")
uri "http://arstechnica.com/search/?query=Diablo+III"
home (nodes/create {:uri uri})]
(nodes/add-to-index (:id home) (:name idx) "uri" uri)
(nodes/find-one (:name idx) :uri uri)
(nodes/find-one (:name idx) "uri" uri)
(nodes/delete-from-index (:id home) (:name idx))))

(deftest ^{:indexing true} test-removing-a-node-removes-it-from-indexes
Expand All @@ -182,7 +191,7 @@
(nodes/delete-from-index (:id node1) (:name idx) "url")
(nodes/add-to-index (:id node1) (:name idx) "url" url1)
(nodes/delete (:id node1))
(let [ids (set (map :id (nodes/find (:name idx) :url url1)))]
(let [ids (set (map :id (nodes/find (:name idx) "url" url1)))]
(is (not (ids (:id node1)))))))

(deftest ^{:indexing true} test-finding-nodes-using-full-text-search-queries-over-index
Expand Down
Loading

0 comments on commit 46426c1

Please sign in to comment.