Class: ClaudeMemory::Core::FactRanker
- Inherits:
-
Object
- Object
- ClaudeMemory::Core::FactRanker
- Defined in:
- lib/claude_memory/core/fact_ranker.rb
Overview
Pure business logic for ranking, sorting, and deduplicating facts Follows Functional Core pattern (Gary Bernhardt) - no I/O, just transformations
Class Method Summary collapse
-
.dedupe_and_sort(results, limit) ⇒ Array<Hash>
Deduplicate full fact results by signature and sort by source + creation time.
-
.dedupe_and_sort_index(results, limit) ⇒ Array<Hash>
Deduplicate index results by fact signature and sort by source priority.
-
.dedupe_by_fact_id(results, limit) ⇒ Array<Hash>
Deduplicate semantic search results by fact_id, keeping highest similarity.
-
.merge_search_results(vector_results, text_results, limit, explain: false) ⇒ Array<Hash>
Merge vector and text search results using Reciprocal Rank Fusion.
-
.sort_by_scope_priority(facts_with_provenance, project_path) ⇒ Array<Hash>
Sort facts by scope priority: current project > global > other projects.
Class Method Details
.dedupe_and_sort(results, limit) ⇒ Array<Hash>
Deduplicate full fact results by signature and sort by source + creation time
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/claude_memory/core/fact_ranker.rb', line 35 def self.dedupe_and_sort(results, limit) seen_signatures = Set.new unique_results = [] results.each do |result| fact = result[:fact] sig = "#{fact[:subject_name]}:#{fact[:predicate]}:#{fact[:object_literal]}" next if seen_signatures.include?(sig) seen_signatures.add(sig) unique_results << result end unique_results.sort_by do |item| source_priority = (item[:source] == :project) ? 0 : 1 [source_priority, item[:fact][:created_at]] end.first(limit) end |
.dedupe_and_sort_index(results, limit) ⇒ Array<Hash>
Deduplicate index results by fact signature and sort by source priority
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/claude_memory/core/fact_ranker.rb', line 12 def self.dedupe_and_sort_index(results, limit) seen_signatures = Set.new unique_results = [] results.each do |result| sig = "#{result[:subject]}:#{result[:predicate]}:#{result[:object_preview]}" next if seen_signatures.include?(sig) seen_signatures.add(sig) unique_results << result end # Sort by source priority (project first) unique_results.sort_by do |item| source_priority = (item[:source] == :project) ? 0 : 1 [source_priority] end.first(limit) end |
.dedupe_by_fact_id(results, limit) ⇒ Array<Hash>
Deduplicate semantic search results by fact_id, keeping highest similarity
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/claude_memory/core/fact_ranker.rb', line 72 def self.dedupe_by_fact_id(results, limit) seen = {} results.each do |result| fact_id = result[:fact][:id] # Keep the result with highest similarity for each fact if !seen[fact_id] || seen[fact_id][:similarity] < result[:similarity] seen[fact_id] = result end end seen.values.sort_by { |r| -r[:similarity] }.take(limit) end |
.merge_search_results(vector_results, text_results, limit, explain: false) ⇒ Array<Hash>
Merge vector and text search results using Reciprocal Rank Fusion
91 92 93 |
# File 'lib/claude_memory/core/fact_ranker.rb', line 91 def self.merge_search_results(vector_results, text_results, limit, explain: false) RRFusion.fuse(vector_results, text_results, limit, explain: explain) end |
.sort_by_scope_priority(facts_with_provenance, project_path) ⇒ Array<Hash>
Sort facts by scope priority: current project > global > other projects
58 59 60 61 62 63 64 65 66 |
# File 'lib/claude_memory/core/fact_ranker.rb', line 58 def self.sort_by_scope_priority(facts_with_provenance, project_path) facts_with_provenance.sort_by do |item| fact = item[:fact] is_current_project = fact[:project_path] == project_path is_global = fact[:scope] == "global" [is_current_project ? 0 : 1, is_global ? 0 : 1] end end |