Class: EntityStoreSequel::PostgresEntityStore

Inherits:
Object
  • Object
show all
Includes:
EntityStore::Logging
Defined in:
lib/entity_store_sequel/postgres_entity_store.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.connect_timeout=(value) ⇒ Object (writeonly)

Sets the attribute connect_timeout

Parameters:

  • value

    the value to set the attribute connect_timeout to.



15
16
17
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 15

def connect_timeout=(value)
  @connect_timeout = value
end

.connection_stringObject

Returns the value of attribute connection_string.



14
15
16
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 14

def connection_string
  @connection_string
end

.native_json_hashObject

Returns the value of attribute native_json_hash.



16
17
18
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 16

def native_json_hash
  @native_json_hash
end

Class Method Details

.databaseObject



18
19
20
21
22
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 18

def database
  return @_database if @_database

  self.database = Sequel.connect(connection_string)
end

.database=(db) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 24

def database=(db)
  @_database = db

  if db.adapter_scheme == :postgres
    @_database.extension :pg_json
    self.native_json_hash = true
  else
    self.native_json_hash = false
  end

  @_database
end

.initObject



37
38
39
40
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 37

def init
  migration_path = File.expand_path("../../sequel/migrations", __FILE__)
  Sequel::Migrator.run(self.database, migration_path, :table=>:entity_store_schema_migration)
end

Instance Method Details

#add_entity(entity, id = BSON::ObjectId.new) ⇒ Object



64
65
66
67
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 64

def add_entity(entity, id = BSON::ObjectId.new)
  entities.insert(:id => id.to_s, :_type => entity.class.name, :version => entity.version)
  id.to_s
end

#add_events(items) ⇒ Object



111
112
113
114
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 111

def add_events(items)
  events_with_id = items.map { |e| [ BSON::ObjectId.new, e ] }
  add_events_with_ids(events_with_id)
end

#add_events_with_ids(event_id_map) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 116

def add_events_with_ids(event_id_map)
  event_id_map.each do |id, event|
    doc = {
      :id => id.to_s,
      :_type => event.class.name,
      :_entity_id => BSON::ObjectId.from_string(event.entity_id).to_s,
      :entity_version => event.entity_version,
      :data => PigeonHole.generate(hash_without_keys(event.attributes, :entity_id)),
    }
    events.insert(doc)
  end
end

#clearObject



55
56
57
58
59
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 55

def clear
  open.drop_table(:entities, :entity_events)
  @entities_collection = nil
  @events_collection = nil
end

#ensure_indexesObject



61
62
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 61

def ensure_indexes
end

#entitiesObject



47
48
49
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 47

def entities
  @entities_collection ||= open[:entities]
end

#eventsObject



51
52
53
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 51

def events
  @events_collection ||= open[:entity_events]
end

#get_entities(ids, options = {}) ⇒ Object

Public: loads the entity from the store, including any available snapshots then loads the events to complete the state

ids - Array of Strings representation of BSON::ObjectId options - Hash of options (default: {})

:raise_exception - Boolean (default: true)

Returns an array of entities



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 137

def get_entities(ids, options={})
  ids.each do |id|
    begin
      BSON::ObjectId.from_string(id)
    rescue BSON::InvalidObjectId
      raise NotFound.new(id) if options.fetch(:raise_exception, true)
      nil
    end
  end

  entities.where(:id => ids).map do |attrs|
    begin
      entity_type = EntityStore::Config.load_type(attrs[:_type])

      # Check if there is a snapshot key in use
      if entity_type.respond_to? :entity_store_snapshot_key
        active_key = entity_type.entity_store_snapshot_key

        # Discard the snapshot if the keys don't match
        unless active_key == attrs[:snapshot_key]
          attrs.delete(:snapshot)
        end
      end

      if attrs[:snapshot]
        if self.class.native_json_hash
          hash = attrs[:snapshot].to_h
        else
          hash = PigeonHole.parse(attrs[:snapshot])
        end

        entity = entity_type.new(hash)
      else
        entity = entity_type.new({'id' => attrs[:id].to_s })
      end
    rescue => e
      log_error "Error loading type #{attrs[:_type]}", e
      raise
    end

    entity
  end

end

#get_events(criteria) ⇒ Object

Public: get events for an array of criteria objects

because each entity could have a different reference
version this allows optional criteria to be specifed

criteria - Hash :id mandatory, :since_version optional

Examples

get_events_for_criteria([ { id: “23232323”}, { id: “2398429834”, since_version: 4 } ] )

Returns Hash with id as key and Array of Event instances as value



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 194

def get_events(criteria)
  return {} if criteria.empty?

  result = {}

  criteria.each do |item|
    raise ArgumentError.new(":id missing from criteria") unless item[:id]

    query = events.where(:_entity_id => item[:id])

    if item[:since_version]
      query = query.where('entity_version > ?', item[:since_version])
    end

    result[item[:id]] = query.order(:entity_version, :id).map do |attrs|
      begin
        if self.class.native_json_hash
          hash = attrs[:data].to_h
        else
          hash = PigeonHole.parse(attrs[:data])
        end

        hash[:_id] = attrs[:id]
        hash[:entity_version] = attrs[:entity_version]
        hash[:entity_id] = attrs[:_entity_id]
        EntityStore::Config.load_type(attrs[:_type]).new(hash)
      rescue => e
        log_error "Error loading type #{attrs[:_type]}", e
        next
      end
    end.compact
  end

  result
end

#openObject



43
44
45
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 43

def open
  PostgresEntityStore.database
end

#remove_entity_snapshot(id) ⇒ Object

Public - remove the snapshot for an entity



95
96
97
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 95

def remove_entity_snapshot(id)
  entities.where(:id => id).update(:snapshot => nil)
end

#remove_snapshots(type = nil) ⇒ Object

Public: remove all snapshots

type - String optional class name for the entity



103
104
105
106
107
108
109
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 103

def remove_snapshots(type=nil)
  if type
    entities.where(:_type => type).update(:snapshot => nil)
  else
    entities.update(:snapshot => nil)
  end
end

#save_entity(entity) ⇒ Object



69
70
71
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 69

def save_entity(entity)
  entities.where(:id => entity.id).update(:version => entity.version)
end

#snapshot_entity(entity) ⇒ Object

Public: create a snapshot of the entity and store in the entities collection



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/entity_store_sequel/postgres_entity_store.rb', line 75

def snapshot_entity(entity)
  if entity.class.respond_to? :entity_store_snapshot_key
    # If there is a snapshot key, store it too
    snapshot_key = entity.class.entity_store_snapshot_key
  else
    # Otherwise, make sure there isn't one set
    snapshot_key = nil
  end

  unless entities[:id => entity.id]
    entities.insert(:id => entity.id, :_type => entity.class.name, :version => entity.version)
  end

  entities
    .where(:id => entity.id)
    .update(:snapshot => PigeonHole.generate(entity.attributes), :snapshot_key => snapshot_key )
end