Class: RdfContext::MemoryStore

Inherits:
AbstractStore show all
Defined in:
lib/rdf_context/store/memory_store.rb

Overview

An integer-key-optimized-context-aware-in-memory store.

Uses nested dictionaries to store triples and context. Each triple is stored in six such indices as follows cspo[s][o] = 1 and cpos[p][s] = 1 and cosp[o][p] = 1 as well as spo[p] = [c] and pos[o] = [c] and pos[s] = [c]

Context information is used to track the ‘source’ of the triple data for merging, unmerging, remerging purposes. context aware store stores consume more memory size than non context stores.

Querying or removing triples using the store identifier (or nil) as context operate across all contexts within the store; otherwise, operations are specifiec to the specified context.

Based on Python RdfLib IOMemory

Instance Attribute Summary collapse

Attributes inherited from AbstractStore

#identifier, #nsbinding, #uri_binding

Instance Method Summary collapse

Methods inherited from AbstractStore

#bind, #bnodes, #close, #commit, #item, #namespace, #objects, #open, #predicates, #prefix, #rollback, #subjects, #transaction_aware?

Constructor Details

#initialize(identifier = nil, configuration = {}) ⇒ MemoryStore

Create a new MemoryStore Store, should be subclassed @param configuration ignored

Parameters:

  • identifier (Resource) (defaults to: nil)


37
38
39
40
# File 'lib/rdf_context/store/memory_store.rb', line 37

def initialize(identifier = nil, configuration = {})
  super
  destroy({})
end

Instance Attribute Details

#default_contextGraph

Default context for this store

Returns:



23
24
25
# File 'lib/rdf_context/store/memory_store.rb', line 23

def default_context
  @default_context
end

Instance Method Details

#add(triple, context = nil, quoted = false) ⇒ Triple

Add a triple to the store Add to default context, if context is nil

Parameters:

  • triple (Triple)
  • context (Graph) (defaults to: nil)

    (nil)

  • quoted (Boolean) (defaults to: false)

    (false) A quoted triple, for Formulae

Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rdf_context/store/memory_store.rb', line 98

def add(triple, context = nil, quoted = false)
  context ||= Graph.new(:store => self, :identifier => @identifier)
  return unless triples(triple, context).empty?

  # Assign keys for new identifiers
  si = resource_to_int(triple.subject) || gen_key(triple.subject)
  pi = resource_to_int(triple.predicate) || gen_key(triple.predicate)
  oi = resource_to_int(triple.object) || gen_key(triple.object)
  ci = resource_to_int(context) || gen_key(context)

  puts "add: #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ci}=#{context ? context.identifier : 'none'}" if ::RdfContext::debug?
  set_nested_index(@cspo, ci, si, pi, oi)
  set_nested_index(@cpos, ci, pi, oi, si)
  set_nested_index(@cosp, ci, oi, si, pi)

  unless quoted
    set_nested_index(@spo, si, pi, oi, ci)
    set_nested_index(@pos, pi, oi, si, ci)
    set_nested_index(@osp, oi, si, pi, ci)
  end
  #dump if ::RdfContext::debug?
end

#contains?(triple, context = nil) ⇒ Boolean

Check to see if this store contains the specified triple

Note, if triple contains a Literal object, need to wild-card and check each result individually due to variation in literal comparisons

Parameters:

  • triple (Triple)
  • context (Graph) (defaults to: nil)

    (nil)

Returns:

  • (Boolean)


286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/rdf_context/store/memory_store.rb', line 286

def contains?(triple, context = nil)
  #puts "contains? #{triple}"
  object = triple.object
  if object.is_a?(Literal)
    triple = Triple.new(triple.subject, triple.predicate, nil)
    triples(triple, context) do |t, cg|
      return true if t.object == object
    end
    false
  else
    !triples(triple, context).empty?
  end
end

#context_aware?true

Supports contexts

Returns:

  • (true)


27
# File 'lib/rdf_context/store/memory_store.rb', line 27

def context_aware?; true; end

#contexts(triple = nil) ⇒ Array<Graph>

Contexts containing the triple (no matching), or total number of contexts in store

Parameters:

  • triple (Triple) (defaults to: nil)

    (nil) Containing the triple/pattern if not nil

Returns:



312
313
314
315
316
317
318
319
320
# File 'lib/rdf_context/store/memory_store.rb', line 312

def contexts(triple = nil)
  if triple
    si, pi, oi = triple_to_int(triple)
    value = @spo[si][pi][oi]
    (value && value.keys.map {|ci| int_to_resource(ci)}) || []
  else
    @cspo.keys.map {|ci| int_to_resource(ci)}
  end
end

#destroy(configuration = {}) ⇒ Object

Destroy store or context If context is specified remove that context, otherwise, re-initialize the store

Parameters:

  • configuration (Hash) (defaults to: {})

    a customizable set of options

Options Hash (configuration):

  • :context (Graph)

    Remove the specified context



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rdf_context/store/memory_store.rb', line 62

def destroy(configuration = {})
  if ctx = configuration[:context]
    remove(Triple.new(nil, nil, nil), ctx)
    
    ci = resource_to_int(ctx)
    if ci
      @reverse.delete(ctx.hash)
      @forward.delete(ci)
    end
  else
    # indexed by [context][subject][predicate][object] = 1
    @cspo = {}
    # indexed by [context][predicate][object][subject] = 1
    @cpos = {}
    # indexed by [context][object][subject][predicate] = 1
    @cosp = {}
    # indexed by [subject][predicate][object] = [context]
    @spo = {}
    # indexed by [predicate][object][subject] = [context]
    @pos = {}
    # indexed by [object][subject][predicate] = [context]
    @osp = {}
    # indexes integer keys to identifiers
    @forward = {}
    # reverse index of forward
    @reverse = {}
  end
end

#dumpObject



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rdf_context/store/memory_store.rb', line 46

def dump
  puts "MemoryStore: #{identifier}\n" +
    "  cspo: #{@cspo.inspect}\n" +
    "  cpos: #{@cpos.inspect}\n" +
    "  cosp: #{@cosp.inspect}\n" +
    "  spo: #{@spo.inspect}\n" +
    "  pos: #{@pos.inspect}\n" +
    "  osp: #{@osp.inspect}\n" +
    "  forward: #{@forward.inspect}\n" +
    "  reverse: #{@reverse.inspect}\n"
end

#formula_aware?true

Supports formulae

Returns:

  • (true)


31
# File 'lib/rdf_context/store/memory_store.rb', line 31

def formula_aware?; true; end

#inspectObject



42
43
44
# File 'lib/rdf_context/store/memory_store.rb', line 42

def inspect
  "#{self.class}[identifier=#{identifier}, forward=#{@forward.size}, contexts=#{@cspo.size}]"
end

#remove(triple, context = nil)

This method returns an undefined value.

Remove a triple from the context and store If subject, predicate and object are nil and context is not nil, the context is removed

Parameters:

  • triple (Triple)
  • context (Graph) (defaults to: nil)

    (nil)



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rdf_context/store/memory_store.rb', line 127

def remove(triple, context = nil)
  context = nil if context == @identifier || (context.respond_to?(:identifier) && context.identifier == @identifier)

  puts "remove: #{triple.subject}, #{triple.predicate}, #{triple.object}, #{context ? context.identifier : 'none'}" if ::RdfContext::debug?
  # Iterate over all matching triples and contexts
  triples(triple, context) do |t, cg|
    si, pi, oi = triple_to_int(t)
    ci = resource_to_int(cg)
    #puts "remove: si=#{si}, pi=#{pi}, oi=#{oi}, ci=#{ci}"
  
    # Remove triple from context
    remove_nested_index(@cspo, ci, si, pi, oi)
    remove_nested_index(@cpos, ci, pi, oi, si)
    remove_nested_index(@cosp, ci, oi, si, pi)

    # Remove context from triple
    remove_nested_index(@spo, si, pi, oi, ci)
    remove_nested_index(@pos, pi, oi, si, ci)
    remove_nested_index(@osp, oi, si, pi, ci)
  end
end

#result(v, si, pi, oi, ctx) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/rdf_context/store/memory_store.rb', line 179

def result(v, si, pi, oi, ctx)
  triple = int_to_triple(si, pi, oi)
  if block_given?
    if v.is_a?(Hash)
      # keys are contexts
      v.keys.each do |ci|
        context = int_to_resource(ci)
        puts "triples= #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ci}=#{context ? context.identifier : 'none'}" if ::RdfContext::debug?
        yield triple, context
      end
    else
      puts "triples= #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ctx ? ctx.identifier : 'none'}" if ::RdfContext::debug?
      yield triple, ctx
    end
  else
    puts "triples= #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ctx ? ctx.identifier : 'none'}" if ::RdfContext::debug?
  end
  triple
end

#size(context = nil) ⇒ Integer

Number of Triples in the graph

Parameters:

  • context (Graph) (defaults to: nil)

    (nil)

Returns:

  • (Integer)


303
304
305
306
307
# File 'lib/rdf_context/store/memory_store.rb', line 303

def size(context = nil)
  context = nil if (context.respond_to?(:identifier) ? context.identifier : context) == @identifier

  triples(Triple.new(nil, nil, nil), context).length
end

#triples(triple, context = nil) {|triple, context| ... } ⇒ Array<Triplle>

A generator over all matching triples

Parameters:

  • triple (Triple)
  • context (Graph) (defaults to: nil)

    (nil)

Yields:

  • (triple, context)

Yield Parameters:

Returns:

Raises:



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/rdf_context/store/memory_store.rb', line 157

def triples(triple, context = nil, &block) # :yields: triple, context
  context = nil if context == @identifier || (context.respond_to?(:identifier) && context.identifier == @identifier)

  if context.nil?
    spo = @spo
    pos = @pos
    osp = @osp
  else
    ci = resource_to_int(context)
    return [] if context.frozen? || !ci
    spo = @cspo[ci]
    pos = @cpos[ci]
    osp = @cosp[ci]
    return [] unless spo && pos && osp
  end

  #self.dump

  results = []
  si, pi, oi = triple_to_int(triple)
  puts "triples? #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ci}=#{context ? context.identifier : 'none'}" if ::RdfContext::debug?

  def result(v, si, pi, oi, ctx)
    triple = int_to_triple(si, pi, oi)
    if block_given?
      if v.is_a?(Hash)
        # keys are contexts
        v.keys.each do |ci|
          context = int_to_resource(ci)
          puts "triples= #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ci}=#{context ? context.identifier : 'none'}" if ::RdfContext::debug?
          yield triple, context
        end
      else
        puts "triples= #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ctx ? ctx.identifier : 'none'}" if ::RdfContext::debug?
        yield triple, ctx
      end
    else
      puts "triples= #{si}=#{triple.subject}, #{pi}=#{triple.predicate}, #{oi}=#{triple.object}, #{ctx ? ctx.identifier : 'none'}" if ::RdfContext::debug?
    end
    triple
  end

  if si # subject is given
    if spo.has_key?(si)
      #puts "spo[#{si}] = #{spo[si].inspect}" if ::RdfContext::debug?
      if pi # subject+predicate is given
        if spo[si].has_key?(pi)
          if oi # subject+predicate+object is given
            #puts "spo[#{si}][#{pi}][#{oi}] = #{spo[si][pi][oi].inspect}"
            results << result(spo[si][pi][oi], si, pi, oi, context, &block) if spo[si][pi].has_key?(oi)
          elsif triple.object.nil? # subject+predicate is given, object unbound
            spo[si][pi].each_pair do |oi, value|
              results << result(value, si, pi, oi, context, &block)
            end
            oi = nil
          end
        end
      elsif triple.predicate.nil? # subject given, predicate unbound
        spo[si].keys.each do |pi|
          #puts "spo[#{si}][#{pi}] = #{spo[si][pi].inspect}" if ::RdfContext::debug?
          if oi # object is given
            results << result(spo[si][pi][oi], si, pi, oi, context, &block) if spo[si][pi].has_key?(oi)
          else # object unbound
            #puts "spo[#{si}][#{pi}] = #{spo[si][pi].inspect}"
            spo[si][pi].each_pair do |oi, value|
              #puts "spo[#{si}][#{pi}][#{oi}] = #{spo[si][pi][oi].inspect}" if ::RdfContext::debug?
              results << result(value, si, pi, oi, context, &block)
            end
            oi = nil
          end
        end
      end
    end
  elsif !triple.subject.nil?
    # Subject specified, but not found, skip
  elsif pi # subject unbound, predicate given
    if pos.has_key?(pi)
      if oi # subject unbound, predicate+object given
        if pos[pi].has_key?(oi)
          pos[pi][oi].each_pair do |si, value|
            results << result(value, si, pi, oi, context, &block)
          end
        end
      elsif triple.object.nil? # subject unbound, predicate given, object unbound
        pos[pi].keys.each do |oi|
          pos[pi][oi].each_pair do |si, value|
            results << result(value, si, pi, oi, context, &block)
          end
        end
        oi = nil
      end
    end
  elsif !triple.predicate.nil?
    # Subject unspecified, predicate specified but not found, skip
  elsif oi # subject+predicate unbound, object given
    if osp.has_key?(oi)
      osp[oi].keys.each do |si|
        osp[oi][si].each_pair do |pi, value|
          results << result(value, si, pi, oi, context, &block)
        end
      end
    end
  elsif !triple.object.nil?
    # Subject+predicate unspecified, object specified but not found, skip
  else # subject+predicate+object unbound
    #puts "spo = #{spo.inspect}" if ::RdfContext::debug?
    spo.keys.each do |si|
      #puts "spo[#{si}] = #{spo[si].inspect}" if ::RdfContext::debug?
      spo[si].keys.each do |pi|
        #puts "spo[#{si}][#{pi}] = #{spo[si][pi].inspect}" if ::RdfContext::debug?
        spo[si][pi].each_pair do |oi, value|
          #puts "spo[#{si}][#{pi}][#{oi}] = #{spo[si][pi][oi].inspect}" if ::RdfContext::debug?
          results << result(value, si, pi, oi, context, &block)
        end
      end
    end
  end
  results
end