CouchDB membership approaches Revision d70e66fc1177 (Tue Dec 16 2008 at 17:04) - Diff Link to this snippet: https://friendpaste.com/2jTD1QBPqQvTi3KW8uMtnJ Embed: manni perldoc borland colorful default murphy trac fruity autumn bw emacs pastie friendly Show line numbers Wrap lines 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277## CouchDB membership views, two approaches. Sho Fukamachi# You will need to install edge DataMapper to run this#require 'rubygems'require 'dm-core'# Specify the db to be used. Create it in CouchDB yourself locally.DataMapper.setup(:default, Addressable::URI.parse("couchdb://localhost:5984/phototest2"))# create classesclass Tag include DataMapper::Resource property :id, String, :key => true, :field => :_id property :rev, String, :field => :_rev property :name, String property :photos_cache, JsonObject, :default => {} view :all, { "map" => "function(doc) { if (doc.type == 'tag') { emit(null, doc); } }" } view :by_name, { "map" => "function(doc) { if (doc.type == 'tag') { emit(doc.name, doc); } }" } view :by_photo_id, { "map" => " function(doc) { if(doc.type == 'tag' && doc.photos_cache) { for (i in doc.photos_cache) { emit(i, doc); } } }"} view :by_photo_name, { "map" => " function(doc) { if(doc.type == 'tag' && doc.photos_cache) { for (i in doc.photos_cache) { emit(doc.photos_cache[i]['photo_name'], doc); } } }"} def photo_array Membership.photos_for_tag(self.id).first.value end def hash_for_cache {:tag_name => self.name, :tag_id => self.id, :tag_rev => self.rev} endendclass Photo include DataMapper::Resource property :id, String, :key => true, :field => :_id property :rev, String, :field => :_rev property :name, String property :tags_cache, JsonObject, :default => {} property :test, JsonObject view :all, { "map" => "function(doc) { if (doc.type == 'photo') { emit(null, doc); } }" } view :by_name, { "map" => "function(doc) { if (doc.type == 'photo') { emit(doc.name, doc); } }" } view :by_tag_id, { "map" => " function(doc) { if(doc.type == 'photo' && doc.tags_cache) { for (i in doc.tags_cache) { emit(i, doc); } } }"} view :by_tag_name, { "map" => " function(doc) { if(doc.type == 'photo' && doc.tags_cache) { for (i in doc.tags_cache) { emit(doc.tags_cache[i]['tag_name'], doc); } } }"} def hash_for_cache {:photo_name => self.name, :photo_id => self.id, :photo_rev => self.rev} end def tags_array Membership.tags_for_photo(self.id).first.value end def apply_tag(tag) Membership.tag_photo(self, tag) end endclass Membership include DataMapper::Resource property :id, String, :key => true, :field => :_id property :rev, String, :field => :_rev property :tag_id, String property :tag_name, String property :tag_rev, String property :photo_id, String property :photo_name, String property :photo_rev, String after :save, :populate_caches view :all, { "map" => "function(doc) { if (doc.type == 'membership') { emit(null, doc); } }" } view :existence_check, { "map" => "function(doc) { if (doc.type == 'membership') { emit([doc.photo_id, doc.tag_id], doc); } }" } view :tags_for_photo_by_photo_id, { "map" => "function(doc) { if (doc.type == 'membership') { emit(doc.photo_id, doc); } }", "reduce" => "function(keys, vals) { results = []; for (var i in vals) { results.push(vals[i]['tag_id']); } return results; }" } view :photos_for_tag_by_tag_id, { "map" => "function(doc) { if (doc.type == 'membership') { emit(doc.tag_id, doc); } }" , "reduce" => "function(keys, vals) { results = []; for (var i in vals) { results.push(vals[i]['photo_id']); } return results; }" } view :tags_for_photo_by_photo_name, { "map" => "function(doc) { if (doc.type == 'membership') { emit(doc.photo_name, doc); } }", "reduce" => "function(keys, vals) { results = []; for (var i in vals) { results.push(vals[i]['tag_name']); } return results; }" } view :photos_for_tag_by_tag_name, { "map" => "function(doc) { if (doc.type == 'membership') { emit(doc.tag_name, doc); } }" , "reduce" => "function(keys, vals) { results = []; for (var i in vals) { results.push(vals[i]['photo_name']); } return results; }" } def assemble_hash_for_caches(remote_hash) remote_hash[:membership_id] = self.id remote_hash[:membership_rev] = self.rev remote_hash end def populate_caches photo = Photo.get(self.photo_id) tag = Tag.get(self.tag_id) tag.photos_cache.delete(photo.id) photo.tags_cache.delete(tag.id) tag.photos_cache[photo.id] = assemble_hash_for_caches(photo.hash_for_cache) photo.tags_cache[tag.id] = assemble_hash_for_caches(tag.hash_for_cache) tag.save photo.save end def self.tag_photo(photo, tag) print "trying to apply tag #{tag.name} to photo #{photo.name} .." if self.existence_check([photo.id, tag.id]).blank? hash = photo.hash_for_cache.merge(tag.hash_for_cache) m = Membership.new(hash).save print "done" else print "tag exists, skipping" end puts end def self.renew_all_caches Photo.all.each do |p| p.tags_cache = {} p.save end Tag.all.each do |t| t.photos_cache = {} t.save end Membership.all.each do |m| m.populate_caches end endend## Create the views#Photo.auto_migrate!Tag.auto_migrate!Membership.auto_migrate!## Insert some data#mg = Photo.create(:id => "mg", :name => "Migration")des = Photo.create(:id => "des", :name => "Dar es Salaam")syd = Photo.create(:id => "syd", :name => "Sydney")tok = Photo.create(:id => "tok", :name => "Tokyo")wd = Tag.create(:id => "wd", :name => "wildebeest")tz = Tag.create(:id => "tz", :name => "tanzania")city = Tag.create(:id => "city", :name => "City")night = Tag.create(:id => "night", :name => "Night View")au = Tag.create(:id => "au", :name => "Australia")jp = Tag.create(:id => "tz", :name => "Japan")Membership.tag_photo(mg, wd)Membership.tag_photo(des, tz)Membership.tag_photo(syd, city)Membership.tag_photo(tok, city)Membership.tag_photo(des, night)Membership.tag_photo(tok, night)Membership.tag_photo(syd, au)Membership.tag_photo(tok, jp)Membership.tag_photo(des, city)## Make various requests using the different methods, confirm that they all work## first up, using the memberships views and the cached information thereinputs "perform some lookups using membership views"tagged_city = Membership.photos_for_tag_by_tag_name('City').first.value.join(', ')puts "Photos tagged 'city': #{tagged_city}"tagged_night = Membership.photos_for_tag_by_tag_name('Night View').first.value.join(', ')puts "Photos tagged 'Night View': #{tagged_night}"tags_for_des = Membership.tags_for_photo_by_photo_name('Dar es Salaam').first.value.join(', ')puts "Tags for photo 'Dar es Salaam': #{tags_for_des}"# now try and do the same via in-document cachesputs "perform some lookups using in-record caches"tagged_city = Photo.by_tag_name('City').collect {|x| x.name}.join(', ')puts "Photos tagged 'city': #{tagged_city}"tagged_night = Photo.by_tag_name('Night View').collect {|x| x.name}.join(', ')puts "Photos tagged 'Night View': #{tagged_night}"tags_for_des = Tag.by_photo_name('Dar es Salaam').collect {|x| x.name}.join(', ')puts "Tags for photo 'Dar es Salaam': #{tags_for_des}"puts 'done!'