Class: RDF::LMDB::Repository

Inherits:
Repository
  • Object
show all
Defined in:
lib/rdf/lmdb.rb

Overview

RDF::LMDB::Repository implements a lightweight, transactional, locally-attached data store using Symas LMDB.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir = nil, uri: nil, title: nil, **options, &block) ⇒ Repository

Returns a new instance of Repository.

Raises:

  • (ArgumentError)


678
679
680
681
682
683
684
685
686
687
688
689
690
# File 'lib/rdf/lmdb.rb', line 678

def initialize dir = nil, uri: nil, title: nil, **options, &block
  dir ||= options.delete(:dir) if options[:dir]

  # wtf no idea why this won't inherit
  @tx_class ||= options.delete(:transaction_class) { DEFAULT_TX_CLASS }
  raise ArgumentError, "Invalid transaction class #{@tx_class}" unless
    @tx_class.is_a? Class and @tx_class <= DEFAULT_TX_CLASS

  @icache = {}

  init_lmdb dir, **options
  super uri: uri, title: title, **options, &block
end

Instance Attribute Details

#dirObject (readonly) Also known as: path

Returns the value of attribute dir.



702
703
704
# File 'lib/rdf/lmdb.rb', line 702

def dir
  @dir
end

Instance Method Details

#clearObject



706
707
708
709
710
711
# File 'lib/rdf/lmdb.rb', line 706

def clear
  env.transaction? { env.databases.each { |db| env[db].clear } }

  # we do not clear the main database; that nukes the sub-databases
  # @lmdb.database.clear
end

#closeObject



717
718
719
# File 'lib/rdf/lmdb.rb', line 717

def close
  env.close
end

#countObject



890
891
892
# File 'lib/rdf/lmdb.rb', line 890

def count
  env[:stmt2g].size
end

#delete_insert(deletes, inserts) ⇒ Object

def apply_changeset changeset

@lmdb.transaction do |t|
  delete_insert(changeset.deletes, changeset.inserts)
end

end



904
905
906
907
908
909
# File 'lib/rdf/lmdb.rb', line 904

def delete_insert deletes, inserts
  ret = nil
  ret = env.transaction? { super(deletes, inserts) }
  commit_transaction # this is to satiate the test suite
  ret
end

#delete_statement(statement) ⇒ Object



745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
# File 'lib/rdf/lmdb.rb', line 745

def delete_statement statement
  # note that this does not get called by `delete_statements`,
  # because we want the transaction on the outside.
  complete! statement
  # warn "WTF LOL #{statement.inspect}"
  env.transaction? do |t|
    if statement.variable?
      query(statement).each { |stmt| rm_one stmt }
    else
      rm_one statement
    end

    log_mtime
  end
  nil
end

#delete_statements(statements) ⇒ Object



774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
# File 'lib/rdf/lmdb.rb', line 774

def delete_statements statements
  env.transaction? do
    hashes = []

    # we don't know what's in here but it may contain statements
    # with variables, in which case we have to handle them
    enums = [statements]
    # also note that RDF::Util::Coercions#coerce_statements
    # which is called in advance of this is supposed to take
    # care of this situation but doesn't for some reason.
    until enums.empty?
      statements = enums.shift
      statements.each do |statement|
        if statement.variable?
          enums << query(statement)
        else
          hashes += rm_one statement, scan: false
        end
      end
    end

    clean_terms hashes.uniq

    log_mtime
  end

  nil
end

#each(&block) ⇒ Object

data retrieval



805
806
807
808
809
# File 'lib/rdf/lmdb.rb', line 805

def each &block
  return enum_for :each unless block

  each_maybe_with_graph(&block)
end

#each_graph(&block) ⇒ Object



845
846
847
848
849
850
851
852
853
854
855
# File 'lib/rdf/lmdb.rb', line 845

def each_graph &block
  return enum_for :each_graph unless block

  env.transaction? true do
    env[:g2stmt].cursor do |c|
      while (k, _ = c.next true)
        block.call RDF::Graph.new(graph_name: resolve_term(k), data: self)
      end
    end
  end
end

#each_object(&block) ⇒ Object



833
834
835
836
837
838
839
840
841
842
843
# File 'lib/rdf/lmdb.rb', line 833

def each_object &block
  return enum_for :each_object unless block

  env.transaction? true do
    env[:o2stmt].cursor do |c|
      while (k, _ = c.next true)
        block.call(resolve_term k)
      end
    end
  end
end

#each_predicate(&block) ⇒ Object



822
823
824
825
826
827
828
829
830
831
# File 'lib/rdf/lmdb.rb', line 822

def each_predicate &block
  return enum_for :each_predicate unless block
  env.transaction? true do
    env[:p2stmt].cursor do |c|
      while (k, _ = c.next true)
        block.call(resolve_term k)
      end
    end
  end
end

#each_subject(&block) ⇒ Object



811
812
813
814
815
816
817
818
819
820
# File 'lib/rdf/lmdb.rb', line 811

def each_subject &block
  return enum_for :each_subject unless block
  env.transaction? true do
    env[:s2stmt].cursor do |c|
      while (k, _ = c.next true)
        block.call(resolve_term k)
      end
    end
  end
end

#each_term(&block) ⇒ Object



857
858
859
860
861
862
863
864
865
866
867
868
869
# File 'lib/rdf/lmdb.rb', line 857

def each_term &block
  return enum_for :each_term unless block

  env.transaction? true do
    env[:int2term].cursor do |c|
      while (_, v = c.next)
        # yield RDF::NTriples::Reader.unserialize v
        v.force_encoding 'utf-8'
        block.call RDF::NTriples::Reader.parse_object(v, intern: true)
      end
    end
  end
end

#empty?Boolean

Returns:

  • (Boolean)


894
895
896
# File 'lib/rdf/lmdb.rb', line 894

def empty?
  count == 0
end

#envObject



911
912
913
# File 'lib/rdf/lmdb.rb', line 911

def env
  (@lmdb ||= {})[Process.pid] ||= ::LMDB.new dir, @lmdb_opts
end

#has_graph?(graph_name) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


934
935
936
937
938
939
940
941
942
943
944
# File 'lib/rdf/lmdb.rb', line 934

def has_graph? graph_name
  raise ArgumentError, 'graph_name must be an RDF::Term' unless
    graph_name.is_a? RDF::Term

  env.transaction? true do
    if int = int_for(graph_name)
      pack = [int].pack ?J
      env[:g2stmt].has? pack
    end
  end
end

#has_object?(object) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


973
974
975
976
977
978
979
980
981
982
983
# File 'lib/rdf/lmdb.rb', line 973

def has_object? object
  raise ArgumentError, 'object must be an RDF::Term' unless
    object.is_a? RDF::Term

  env.transaction? true do
    if int = int_for(object)
      pack = [int].pack ?J
      env[:o2stmt].has? pack
    end
  end
end

#has_predicate?(predicate) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


961
962
963
964
965
966
967
968
969
970
971
# File 'lib/rdf/lmdb.rb', line 961

def has_predicate? predicate
  raise ArgumentError, 'predicate must be an RDF::Term' unless
    predicate.is_a? RDF::Term

  env.transaction? true do
    if int = int_for(predicate)
      pack = [int].pack ?J
      env[:p2stmt].has? pack
    end
  end
end

#has_quad?(quad) ⇒ Boolean

Returns:

  • (Boolean)


998
999
1000
# File 'lib/rdf/lmdb.rb', line 998

def has_quad? quad
  has_statement? check_triple_quad quad, quad: true
end

#has_statement?(statement) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


928
929
930
931
932
# File 'lib/rdf/lmdb.rb', line 928

def has_statement? statement
  raise ArgumentError, 'Argument must be an RDF::Statement' unless
    statement.is_a? RDF::Statement
  !query_pattern(statement.to_h).to_a.empty?
end

#has_subject?(subject) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


946
947
948
949
950
951
952
953
954
955
956
957
958
959
# File 'lib/rdf/lmdb.rb', line 946

def has_subject? subject
  raise ArgumentError, 'subject must be an RDF::Term' unless
    subject.is_a? RDF::Term

  env.transaction? true do
    if int = int_for(subject)
      pack = [int].pack ?J
      # XXX fix this upstream
      !!env[:s2stmt].has?(pack)
    else
      false
    end
  end
end

#has_term?(term) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


985
986
987
988
989
990
991
992
# File 'lib/rdf/lmdb.rb', line 985

def has_term? term
  raise ArgumentError, 'term must be an RDF::Term' unless
    term.is_a? RDF::Term

  env.transaction? true do
    env[:hash2term].has? hash_term(term)
  end
end

#has_triple?(triple) ⇒ Boolean

Returns:

  • (Boolean)


994
995
996
# File 'lib/rdf/lmdb.rb', line 994

def has_triple? triple
  has_statement? check_triple_quad triple
end

#insert_statement(statement) ⇒ Object

data manipulation



736
737
738
739
740
741
742
743
# File 'lib/rdf/lmdb.rb', line 736

def insert_statement statement
  complete! statement
  env.transaction? do |t|
    add_one statement
    log_mtime
  end
  nil
end

#insert_statements(statements) ⇒ Object



762
763
764
765
766
767
768
769
770
771
772
# File 'lib/rdf/lmdb.rb', line 762

def insert_statements statements
  env.transaction? do
    statements.each do |statement|
      complete! statement
      add_one statement
    end
    log_mtime
  end

  nil
end

#isolation_levelObject



698
699
700
# File 'lib/rdf/lmdb.rb', line 698

def isolation_level
  :serializable
end

#mtimeTime

Return a Time object representing when the store was last written.

Returns:

  • (Time)

    said modification time



725
726
727
728
729
730
731
732
# File 'lib/rdf/lmdb.rb', line 725

def mtime
  if packed = env[:control]['mtime']
    nsecs = Rational(packed.unpack1(?q), 10 ** 9)
    Time.at nsecs, in: ?Z
  else
    log_mtime
  end
end

#open(dir, **options) ⇒ Object



713
714
715
# File 'lib/rdf/lmdb.rb', line 713

def open dir, **options
  init_lmdb dir, **options
end

#project_graph(graph_name, &block) ⇒ Object



871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
# File 'lib/rdf/lmdb.rb', line 871

def project_graph graph_name, &block
  return enum_for :project_graph, graph_name unless block

  op = -> _ = nil do
    gint  = graph_name ? int_for(graph_name) : 0
    return unless gint
    gpack = [gint].pack ?J
    cache = {}
    env[:statement].each do |spack, spo|
      next unless env[:stmt2g].has? spack, gpack
      spo = resolve_terms spo, cache: cache, write: true

      block.call RDF::Statement(*spo, graph_name: graph_name)
    end
  end

  env.transaction? true, &op
end

#supports?(feature) ⇒ Boolean

housekeeping

Returns:

  • (Boolean)


694
695
696
# File 'lib/rdf/lmdb.rb', line 694

def supports? feature
  !!SUPPORTS[feature.to_s.to_sym]
end

#transaction(mutable: false, &block) ⇒ Object



915
916
917
918
919
920
921
922
923
924
925
926
# File 'lib/rdf/lmdb.rb', line 915

def transaction mutable: false, &block
  return begin_transaction mutable: mutable unless block

  begin
    begin_transaction mutable: mutable, &block
  rescue => error
    rollback_transaction # to sate the test suite
    raise error
  end
  #commit_transaction # to sate the test suite
  self
end