From 8e812635d7f70e748ea829e3714650a279f70f88 Mon Sep 17 00:00:00 2001 From: Paul Carey Date: Wed, 10 Sep 2008 11:31:25 +0100 Subject: [PATCH] Improved view API a little. Added merge method - used for merging different document types returned by a join view into a single ViewObject. --- lib/relaxdb/all_delegator.rb | 4 +-- lib/relaxdb/relaxdb.rb | 38 ++++++++++++++++++++-------- spec/denormalisation_spec.rb | 2 +- spec/relaxdb_spec.rb | 48 ++++++++++++++++++++++++++++++++++-- 4 files changed, 77 insertions(+), 15 deletions(-) diff --git a/lib/relaxdb/all_delegator.rb b/lib/relaxdb/all_delegator.rb index 115896a..87ff75f 100644 --- a/lib/relaxdb/all_delegator.rb +++ b/lib/relaxdb/all_delegator.rb @@ -18,7 +18,7 @@ def __getobj__ view_path = "_view/#{@klass}/all" map_function = ViewCreator.all(@klass) - @all = RelaxDB::retrieve(view_path, @klass, "all", map_function) + @all = RelaxDB.retrieve(view_path, @klass, "all", map_function) end def sorted_by(*atts) @@ -27,7 +27,7 @@ def sorted_by(*atts) q = Query.new(@klass.name, v.view_name) yield q if block_given? - RelaxDB::retrieve(q.view_path, @klass, v.view_name, v.map_function) + RelaxDB.retrieve(q.view_path, @klass, v.view_name, v.map_function) end # Note that this method leaves the corresponding DesignDoc for the associated class intact diff --git a/lib/relaxdb/relaxdb.rb b/lib/relaxdb/relaxdb.rb index bc0e891..cfbf6b3 100644 --- a/lib/relaxdb/relaxdb.rb +++ b/lib/relaxdb/relaxdb.rb @@ -49,6 +49,7 @@ def load(id) create_object(data) end + # Used internally by RelaxDB def retrieve(view_path, design_doc, view_name, map_function) begin resp = db.get(view_path) @@ -61,21 +62,38 @@ def retrieve(view_path, design_doc, view_name, map_function) create_from_hash(data) end - def view(design_doc, view_name, default_ret_val=[]) + # Requests the given view from CouchDB and returns a hash. + # This method should typically be wrapped in one of merge, instantiate, or reduce_result. + def view(design_doc, view_name) q = Query.new(design_doc, view_name) yield q if block_given? resp = db.get(q.view_path) - data = JSON.parse(resp.body) - - # presence of total_rows tells us a map function was invoked - # if it's absent a map reduce invocation occured - if data["total_rows"] - create_from_hash(data) - else - obj = data["rows"][0] && data["rows"][0]["value"] - obj ? ViewObject.create(obj) : default_ret_val + JSON.parse(resp.body) + end + + # Should be invoked on the result of a join view + # Merges all rows based on merge_key and returns an array of ViewOject + def merge(data, merge_key) + merged = {} + data["rows"].each do |row| + value = row["value"] + merged[value[merge_key]] ||= {} + merged[value[merge_key]].merge!(value) end + + merged.values.map { |v| ViewObject.create(v) } + end + + # Creates RelaxDB::Document objects from the result + def instantiate(data) + create_from_hash(data) + end + + # Returns a scalar, an object, or an Array of objects + def reduce_result(data) + obj = data["rows"][0] && data["rows"][0]["value"] + ViewObject.create(obj) end def create_from_hash(data) diff --git a/spec/denormalisation_spec.rb b/spec/denormalisation_spec.rb index bd30f03..ae65c96 100644 --- a/spec/denormalisation_spec.rb +++ b/spec/denormalisation_spec.rb @@ -35,7 +35,7 @@ class Leaf < RelaxDB::Document it "should not interfere with normal belongs_to behaviour" do tree = Tree.new(:name => "sapling", :climate => "tropical").save leaf = Leaf.new(:tree => tree).save - leaf = RelaxDB.load(leaf._id) + leaf = RelaxDB.load(leaf._id) leaf.tree.name.should == "sapling" leaf.tree.climate.should == "tropical" end diff --git a/spec/relaxdb_spec.rb b/spec/relaxdb_spec.rb index 4f84ebb..8a4fa28 100644 --- a/spec/relaxdb_spec.rb +++ b/spec/relaxdb_spec.rb @@ -58,8 +58,52 @@ end - it "should offer an example where behaviour is different with caching enabled and caching disabled" do - # if caching is added + describe ".view" do + + map_func = %Q< + function (doc) { + emit(doc._id, doc); + } + > + + it "should request a view and return a hash" do + RelaxDB::DesignDocument.get("viewtest").add_view("simple", "map", map_func).save + data = RelaxDB.view("viewtest", "simple") + data.should be_instance_of(Hash) + end + + it "may accept a block" do + RelaxDB::DesignDocument.get("viewtest").add_view("simple", "map", map_func).save + RelaxDB.db.put("x", {}.to_json) + RelaxDB.db.put("y", {}.to_json) + data = RelaxDB.view("viewtest", "simple") { |q| q.key("x") } + data["rows"].size.should == 1 + end + end + + describe ".merge" do + + it "should merge rows sharing a common merge key into a single ViewObject" do + rows = [ + {"value" => {"sculptor_id" => 1, "sculpture_name" => "strandbeesten"} }, + {"value" => {"sculptor_id" => 1, "sculptor_name" => "hans"} }, + {"value" => {"sculptor_id" => 2, "sculpture_name" => "parading dogs"} }, + {"value" => {"sculptor_id" => 2, "sculptor_name" => "holmes"} } + ] + data = {"rows" => rows} + result = RelaxDB.merge(data, "sculptor_id") + result = result.sort { |a, b| a.sculptor_name <=> b.sculptor_name } + + result[0].sculptor_name.should == "hans" + result[0].sculpture_name.should == "strandbeesten" + result[1].sculptor_name.should == "holmes" + result[1].sculpture_name.should == "parading dogs" + end + + end + + # if caching is added + # it "should offer an example where behaviour is different with caching enabled and caching disabled" end