module ActiveGraph module Core module Schema FILTER = { 3 => [:type, 'node_unique_property'], 4 => [:uniqueness, 'UNIQUE'], } def version @version ||= read_transaction do # BTW: community / enterprise could be retrieved via `result.first.edition` query('CALL dbms.components()', {}, skip_instrumentation: true).first[:versions][0] .then(&Gem::Version.method(:new)) end end def version?(requirement) Gem::Requirement.create(requirement).satisfied_by?(Gem::Version.new(version)) end def indexes normalize(raw_indexes, *%i[type state]) end def normalize(result, *extra) result.map do |row| definition(row, version?('<4') ? :index_cypher_v3 : :index_cypher) .merge(extra.to_h { |key| [key, row[key].to_sym] }) end end def constraints if version?('<4.3') raw_indexes.select(&method(:constraint_owned?)) else raw_constraints.select(&method(:constraint_filter)) end.map { |row| definition(row, :constraint_cypher).merge(type: :uniqueness) } end private def raw_constraints read_transaction do query('SHOW CONSTRAINTS YIELD *', {}, skip_instrumentation: true).to_a end end def raw_indexes read_transaction do query(version?('<4.3') ? 'CALL db.indexes()' : 'SHOW INDEXES YIELD *', {}, skip_instrumentation: true) .reject { |row| row[:type] == 'LOOKUP' } end end def constraint_owned?(record) FILTER[major]&.then { |(key, value)| record[key] == value } || record[:owningConstraint] end private def major @major ||= version.segments.first end def constraint_filter(record) %w[UNIQUENESS RELATIONSHIP_PROPERTY_EXISTENCE NODE_PROPERTY_EXISTENCE NODE_KEY].include?(record[:type]) end def index_cypher_v3(label, properties) "INDEX ON :#{label}#{com_sep(properties, nil)}" end def index_cypher(label, properties) "INDEX FOR (n:#{label}) ON #{com_sep(properties)}" end def constraint_cypher(label, properties) "CONSTRAINT ON (n:#{label}) ASSERT #{com_sep(properties)} IS UNIQUE" end def com_sep(properties, prefix = 'n.') "(#{properties.map { |prop| "#{prefix}#{prop}" }.join(', ')})" end def definition(row, template) { label: label(row), properties: properties(row), name: row[:name], create_statement: row[:createStatement] || send(template,label(row), row[:properties]) } end def label(row) row[version?('>=4') ? :labelsOrTypes : :tokenNames].first.to_sym end def properties(row) row[:properties].map(&:to_sym) end end end end