require 'observer' require 'ferret' require 'pyrosoma/frame' class Pyrosoma class Database include Observable attr_reader :index def initialize(opts) f = Ferret::Index::FieldInfos.new f.add_field(:content, :index => :yes) [:id, :path, :master].each { |untok| f.add_field(untok, :index => :untokenized_omit_norms) } @index = Ferret::Index::Index.new({:key => :id, :field_infos => f}.merge(opts)) end def import(string) f = Frame.load(string) if f.include?(:id) && f[:id].kind_of?(String) self << f else raise InvalidDocument, "no document id specified" end f end def <<(frame) changed frame.db = self if frame.kind_of? Frame @index << frame.to_hash notify_observers frame self end def [](key) data = @index[key] if data frame = Frame.new(data.load) frame.db = self frame else raise IndexError, "document `#{key}' doesn't exist" end end def ids r = [] # expensive! search_each(Ferret::Search::MatchAllQuery.new, :limit => :all) { |doc, _| if id = self[doc][:id] r << id end } r end def size @index.size end def search_each(query, *options, &block) found = [] @index.search_each(query, *options) { |doc, _| id = @index[doc][:id] if id && block yield id else found << id end } block ? nil : found end def first(field, n=nil, type=:auto) r = [] pq = Ferret::Search::PrefixQuery.new(field, '') @index.search_each(pq, :limit => n || 1, :sort => Ferret::Search::Sort.new( [Ferret::Search::SortField.new(field, :type => type)])) { |doc, _| r << self[doc] } n.nil? ? r.first : r end def last(field, n=nil, type=:auto) r = [] pq = Ferret::Search::PrefixQuery.new(field, '') @index.search_each(pq, :limit => n || 1, :sort => Ferret::Search::Sort.new( [Ferret::Search::SortField.new(field, :type => type, :reverse => true)])) { |doc, _| r << self[doc] } n.nil? ? r.first : r end end class Frame module DatabaseFind def find(term, *options, &block) if block db.search_each(term, *options, &block) else r = [] db.search_each(term, *options) { |i, _| r << db[i] } r end end end include DatabaseFind end end