Skip to content

Commit

Permalink
Support links on stored values
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelklishin committed May 12, 2012
1 parent 68111ca commit d5fdead
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 16 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 Welle 1.0.0-alpha5 and 1.0.0-alpha6

No changes yet.
### Links support

`clojurewerkz.welle.kv/store` now takes the new `:links` option that lets you store
Riak links with the value. `clojurewerkz.welle.kv/fetch` then transparently deserializes when the value
is fetched back:

``` clojure
(kv/store bucket-name k v :content-type Constants/CTYPE_TEXT_UTF8 :links [{:bucket "pages" :key "clojurewerkz.org" :tag "links"}])
(let [fetched (kv/fetch-one bucket-name k)]
(println (:links fetched)))
```


## Changes between Welle 1.0.0-alpha4 and 1.0.0-alpha5
Expand Down
41 changes: 31 additions & 10 deletions src/clojure/clojurewerkz/welle/conversion.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
(:require [clojure.data.json :as json]
[clojure.set :as cs]
[clojure.java.io :as io])
(:use [clojure.walk :only [stringify-keys]])
(:import [com.basho.riak.client.cap Quora Quorum VClock BasicVClock]
[com.basho.riak.client.raw StoreMeta FetchMeta DeleteMeta]
com.basho.riak.client.IRiakObject
[com.basho.riak.client IRiakObject RiakLink]
[com.basho.riak.client.builders RiakObjectBuilder BucketPropertiesBuilder]
[com.basho.riak.client.bucket BucketProperties TunableCAPProps]
com.basho.riak.client.http.util.Constants
Expand Down Expand Up @@ -123,15 +124,27 @@

;; Clojure <=> IRiakObject

(defn ^com.basho.riak.client.RiakLink
to-riak-link
"Converts a Clojure map to a RiakLink instance"
[m]
(let [m (stringify-keys m)]
(RiakLink. (get m "bucket") (get m "key") (get m "tag"))))

(defn from-riak-link
"Converts a RiakLink instance to a Clojure map"
[^RiakLink rl]
{:bucket (.getBucket rl) :key (.getKey rl) :tag (.getTag rl)})

(declare deserialize)
(defn to-riak-object
"Builds a Riak object from a Clojure map of well-known attributes:
:value, :content-type, :metadata, :indexes, :vclock, :vtag, :last-modified"
(^com.basho.riak.client.IRiakObject
[{:keys [^String bucket ^String key value content-type metadata indexes vclock vtag last-modified]
[{:keys [^String bucket ^String key value content-type metadata indexes
vclock vtag last-modified links]
:or {content-type Constants/CTYPE_OCTET_STREAM
metadata {}
indexes []}
metadata {}}
:as options}]
(let [^RiakObjectBuilder bldr (doto (RiakObjectBuilder/newBuilder (name bucket) (name key))
(.withValue value)
Expand All @@ -140,10 +153,13 @@
(when vclock (.withVClock bldr ^VClock (to-vclock vclock)))
(when vtag (.withVtag bldr vtag))
(when last-modified (.withLastModified bldr last-modified))
;; TODO: this code breaks when indexed values are not collections
(doseq [[idx-key idx-vals] indexes
idx-val (if (coll? idx-vals) idx-vals [idx-vals])]
(.addIndex bldr ^String (name idx-key) idx-val))
(when-let [indexes (seq indexes)]
;; TODO: this code breaks when indexed values are not collections
(doseq [[idx-key idx-vals] indexes
idx-val (if (coll? idx-vals) idx-vals [idx-vals])]
(.addIndex bldr ^String (name idx-key) idx-val)))
(when-let [xs (seq links)]
(.withLinks bldr (map to-riak-link xs)))
(.build bldr))))

(defn indexes-from
Expand All @@ -158,17 +174,22 @@
(merge-with cs/union acc-m {idx-name idx-fields})))]
(reduce step {} indexes)))

(defn links-from
"Returns links on the given IRiakObject as a lazy sequence of Clojure maps"
[^IRiakObject ro]
(map from-riak-link (.getLinks ro)))

(defn from-riak-object
"Converts IRiakObjects to a Clojure map"
[^IRiakObject ro]

{:vclock (.getVClock ro)
:content-type (.getContentType ro)
:vtag (.getVtag ro)
:last-modified (.getLastModified ro)
:metadata (into {} (.getMeta ro))
:value (.getValue ro)
:indexes (indexes-from ro)})
:indexes (indexes-from ro)
:links (links-from ro)})


;; Index queries
Expand Down
3 changes: 2 additions & 1 deletion src/clojure/clojurewerkz/welle/kv.clj
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
(defn store
"Stores an object in Riak"
[^String bucket-name ^String key value &{ :keys [w dw pw
indexes vclock ^String vtag ^Long last-modified
indexes links vclock ^String vtag ^Long last-modified
^Boolean return-body ^Boolean if-none-match ^Boolean if-not-modified
content-type metadata]
:or {content-type Constants/CTYPE_OCTET_STREAM
Expand All @@ -37,6 +37,7 @@
:content-type content-type
:metadata (stringify-keys metadata)
:indexes indexes
:links links
:vclock vclock
:vtag vtag
:last-modified last-modified})
Expand Down
25 changes: 22 additions & 3 deletions test/clojurewerkz/welle/test/conversion_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
com.basho.riak.client.bucket.TunableCAPProps
com.basho.riak.client.util.CharsetUtils
com.basho.riak.client.http.util.Constants
com.basho.riak.client.IRiakObject
[com.basho.riak.client IRiakObject RiakLink]
[java.util Date UUID]))

(set! *warn-on-reflection* true)
Expand Down Expand Up @@ -110,12 +110,16 @@
metadata {"metakey" "metavalue"}
indexes {"handle" #{"johnnyriak"}}
vclock (vclock-for "vclock for a riak object")
ro (to-riak-object {:bucket bucket :key key :value value :content-type content-type :metadata metadata :indexes indexes :vclock vclock})]
links [{:bucket "pages" :key "http://clojurewerkz.org" :tag "links"}
{:bucket "pages" :key "http://clojureriak.info" :tag "links"}]
ro (to-riak-object {:bucket bucket :key key :value value :content-type content-type :metadata metadata :indexes indexes :vclock vclock :links links})]
(is (= bucket (.getBucket ro)))
(is (= key (.getKey ro)))
(is (= "A value" (.getValueAsString ro)))
(is (= content-type (.getContentType ro)))
(is (= vclock (.getVClock ro))))))
(is (= vclock (.getVClock ro)))
(is (.hasLinks ro))
(is (= 2 (.numLinks ro))))))

(deftest test-to-bucket-properties
(testing "case 1"
Expand Down Expand Up @@ -234,3 +238,18 @@
query (to-index-query value bucket-name index-name)]
(is (= "email_bin" (.getIndex query)))
(is (= bucket-name (.getBucket query))))))


(deftest test-to-riak-link
(testing "positive scenario"
(let [m {:bucket "pages" :key "http://clojureriak.info" :tag "links"}
rl (to-riak-link m)]
(is (= "pages" (.getBucket rl)))
(is (= "http://clojureriak.info" (.getKey rl)))
(is (= "links" (.getTag rl))))))

(deftest test-from-riak-link
(testing "positive scenario"
(let [m {:bucket "pages" :key "http://clojureriak.info" :tag "links"}
rl (RiakLink. "pages" "http://clojureriak.info" "links")]
(is (= m (from-riak-link rl))))))
21 changes: 20 additions & 1 deletion test/clojurewerkz/welle/test/kv_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
v (merge {:city "New York City" :state "NY" :year 2011 :participants #{"johndoe" "timsmith" "michaelblack"}
:venue {:name "Sheraton New York Hotel & Towers" :address "811 Seventh Avenue" :street "Seventh Avenue"}}
;; on Clojure 1.4+, we can add date to this map, too. Clojure 1.4's extensible reader has extensions for
;; Date/instant serialization but 1.3 will fail. MK.
;; Date/instant serialization but 1.3 will fail. MK.
(when clojure14?
{:date (java.util.Date.)}))
ct "application/clojure"
Expand Down Expand Up @@ -148,6 +148,25 @@
(drain bucket-name)))


;;
;; Links
;;

(deftest test-basic-store-with-links
(let [bucket-name "clojurewerkz.welle.kv/store-with-given-links"
bucket (wb/update bucket-name)
k (str (UUID/randomUUID))
v "value"
links [{:bucket "pages" :key "clojurewerkz.org" :tag "links"}]
stored (kv/store bucket-name k v :content-type Constants/CTYPE_TEXT_UTF8 :links links)
[fetched] (kv/fetch bucket-name k)]
(is (= Constants/CTYPE_TEXT_UTF8 (:content-type fetched)))
(is (= links (:links fetched)))
(is (= v (:value fetched)))
(is-riak-object fetched)
(drain bucket-name)))



;;
;; kv/fetch, kv/fetch-one
Expand Down

0 comments on commit d5fdead

Please sign in to comment.