Class: Mxrb::Semantic::Index
- Inherits:
-
Object
- Object
- Mxrb::Semantic::Index
- Defined in:
- lib/mxrb/semantic/index.rb
Overview
Builds a semantic index directly from an opened MPR. Ruby is the public API; the CLI only formats these results.
Constant Summary collapse
- CACHE_VERSION =
3- CACHE_METADATA_KEYS =
%i[ allowed_roles bson_type documentation excluded exposed from mark_as_used to ].freeze
- FIELD_RELATIONS =
{ "microflow" => :calls, "nanoflow" => :calls, "javaaction" => :calls, "javascriptaction" => :calls, "workflow" => :calls, "form" => :opens, "page" => :opens, "layout" => :uses_layout, "snippet" => :uses_snippet, "entity" => :uses_entity, "attribute" => :uses_attribute, "association" => :uses_association, "enumeration" => :uses_enumeration }.freeze
- CALL_KINDS =
%i[microflow nanoflow java_action javascript_action workflow].freeze
- TOKEN =
/[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*){1,2}/- MEMBER_TOKEN =
/[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*\/[A-Za-z_][A-Za-z0-9_]*/
Instance Attribute Summary collapse
-
#artifacts ⇒ Object
readonly
Returns the value of attribute artifacts.
-
#references ⇒ Object
readonly
Returns the value of attribute references.
-
#unresolved_references ⇒ Object
readonly
Returns the value of attribute unresolved_references.
Class Method Summary collapse
Instance Method Summary collapse
- #callees_of(source) ⇒ Object
- #callers_of(target) ⇒ Object
- #describe(artifact) ⇒ Object
- #find(name, kind: nil) ⇒ Object
- #find_all(name) ⇒ Object
- #impact_of(target, transitive: true) ⇒ Object
-
#initialize(project) ⇒ Index
constructor
A new instance of Index.
- #references_from(source) ⇒ Object
- #references_to(target) ⇒ Object
- #search(query, kind: nil, module_name: nil) ⇒ Object
-
#semantic_search(query, limit: 10, backend: :auto) ⇒ Object
(also: #semantic_search_artifacts)
Semantic similarity search with optional sqlite-vec acceleration.
- #semantic_search_hits(query, limit: 10, backend: :auto) ⇒ Object
Constructor Details
#initialize(project) ⇒ Index
Returns a new instance of Index.
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/mxrb/semantic/index.rb', line 74 def initialize(project) @project = project reset_state @fingerprint = compute_fingerprint json = @fingerprint && @project.mpr.read_index_cache(@fingerprint) unless json && restore_from_cache(json) reset_state build @project.mpr.write_index_cache(@fingerprint, serialize_to_cache) if @fingerprint end end |
Instance Attribute Details
#artifacts ⇒ Object (readonly)
Returns the value of attribute artifacts.
54 55 56 |
# File 'lib/mxrb/semantic/index.rb', line 54 def artifacts @artifacts end |
#references ⇒ Object (readonly)
Returns the value of attribute references.
54 55 56 |
# File 'lib/mxrb/semantic/index.rb', line 54 def references @references end |
#unresolved_references ⇒ Object (readonly)
Returns the value of attribute unresolved_references.
54 55 56 |
# File 'lib/mxrb/semantic/index.rb', line 54 def unresolved_references @unresolved_references end |
Class Method Details
.fingerprint(project) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/mxrb/semantic/index.rb', line 56 def self.fingerprint(project) rows = project.query( "SELECT UnitID, ContainerID, ContainmentName, ContentsHash " \ "FROM Unit ORDER BY UnitID" ) digest = Digest::SHA256.new digest << CACHE_VERSION.to_s rows.each do |row| row.each do |value| bytes = value.to_s.b digest << [bytes.bytesize].pack("Q>") << bytes end end digest.hexdigest rescue StandardError nil end |
Instance Method Details
#callees_of(source) ⇒ Object
159 160 161 162 163 164 165 |
# File 'lib/mxrb/semantic/index.rb', line 159 def callees_of(source) artifact = resolve_argument(source) references_from(artifact) .select { _1.relation == :calls } .map(&:target) .uniq(&:id) end |
#callers_of(target) ⇒ Object
151 152 153 154 155 156 157 |
# File 'lib/mxrb/semantic/index.rb', line 151 def callers_of(target) artifact = resolve_argument(target) references_to(artifact) .select { _1.relation == :calls } .map(&:source) .uniq(&:id) end |
#describe(artifact) ⇒ Object
132 133 134 135 136 137 138 139 |
# File 'lib/mxrb/semantic/index.rb', line 132 def describe(artifact) resolved = resolve_argument(artifact) ArtifactDetails.new( resolved, references_to(resolved).freeze, references_from(resolved).freeze ) end |
#find(name, kind: nil) ⇒ Object
86 87 88 89 90 91 92 |
# File 'lib/mxrb/semantic/index.rb', line 86 def find(name, kind: nil) return name if name.is_a?(Artifact) matches = @by_name[name.to_s] matches = matches.select { _1.kind == kind.to_sym } if kind matches.one? ? matches.first : nil end |
#find_all(name) ⇒ Object
94 95 96 |
# File 'lib/mxrb/semantic/index.rb', line 94 def find_all(name) @by_name[name.to_s].dup end |
#impact_of(target, transitive: true) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/mxrb/semantic/index.rb', line 167 def impact_of(target, transitive: true) root = resolve_argument(target) seen = Set[root.id] queue = [root] affected = [] traversed = [] until queue.empty? current = queue.shift incoming = references_to(current) traversed.concat(incoming) incoming.each do |reference| source = reference.source next if seen.include?(source.id) seen << source.id affected << source queue << source if transitive end end Impact.new(root, affected.freeze, traversed.uniq.freeze) end |
#references_from(source) ⇒ Object
146 147 148 149 |
# File 'lib/mxrb/semantic/index.rb', line 146 def references_from(source) artifact = resolve_argument(source) @references.select { _1.source.id == artifact.id } end |
#references_to(target) ⇒ Object
141 142 143 144 |
# File 'lib/mxrb/semantic/index.rb', line 141 def references_to(target) artifact = resolve_argument(target) @references.select { _1.target.id == artifact.id } end |
#search(query, kind: nil, module_name: nil) ⇒ Object
98 99 100 101 102 103 104 105 |
# File 'lib/mxrb/semantic/index.rb', line 98 def search(query, kind: nil, module_name: nil) matcher = query.is_a?(Regexp) ? query : /#{Regexp.escape(query.to_s)}/i @artifacts.select do |artifact| matcher.match?(artifact.qualified_name) && (!kind || artifact.kind == kind.to_sym) && (!module_name || artifact.module_name == module_name.to_s) end.sort_by { [_1.module_name.to_s, _1.kind.to_s, _1.qualified_name] } end |
#semantic_search(query, limit: 10, backend: :auto) ⇒ Object Also known as: semantic_search_artifacts
Semantic similarity search with optional sqlite-vec acceleration.
108 109 110 |
# File 'lib/mxrb/semantic/index.rb', line 108 def semantic_search(query, limit: 10, backend: :auto) semantic_search_hits(query, limit:, backend:).map(&:artifact).freeze end |
#semantic_search_hits(query, limit: 10, backend: :auto) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/mxrb/semantic/index.rb', line 113 def semantic_search_hits(query, limit: 10, backend: :auto) size = Integer(limit) raise ArgumentError, "semantic search limit must be positive" unless size.positive? = (backend) store = vec_store() return ruby_semantic_hits(query, , size) unless store begin store.rebuild!(@artifacts) unless store.compatible? store.search(query, limit: size).filter_map do |hit| artifact = @by_id[hit[:id]] SemanticSearchHit.new(artifact, cosine_distance(hit[:distance])) if artifact end.freeze rescue SQLite3::Exception, LoadError ruby_semantic_hits(query, , size) end end |