Skip to content

Commit

Permalink
Improved view API a little. Added merge method - used for merging dif…
Browse files Browse the repository at this point in the history
…ferent document types returned by a join view into a single ViewObject.
  • Loading branch information
paulcarey committed Sep 10, 2008
1 parent da5470e commit 8e81263
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 15 deletions.
4 changes: 2 additions & 2 deletions lib/relaxdb/all_delegator.rb
Expand Up @@ -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)
Expand All @@ -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
Expand Down
38 changes: 28 additions & 10 deletions lib/relaxdb/relaxdb.rb
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion spec/denormalisation_spec.rb
Expand Up @@ -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
Expand Down
48 changes: 46 additions & 2 deletions spec/relaxdb_spec.rb
Expand Up @@ -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

0 comments on commit 8e81263

Please sign in to comment.