Small BM25+ implementation for probibalistic result ranking
You should use built-ins to simplify the code:

def containing_term(term)
  total = 0
  @docs.each do |doc|
    total += 1 if doc.include?(term) 
   end
 total
end


Becomes:

def containing_term(term)
  @docs.count {|doc| doc.include?(term) }
end


And

def avg_dl
  doc_lengths = 0
  @docs.each do |doc|
    doc_lengths += doc.length
  end
  doc_lengths / total_docs
end


Becomes:

def avg_dl
  @docs.map(&amp;:length).inject(:+) / total_docs
end


You are close to immutable,  make the last step

In Collection you use sort!, disallowing method chaining, sort and returning a new object would be more expected.

Use a Hash for an easy but massive optimization

The method:

def term_freq(term)
   # Will need something better here to clean out the document.
   # This is temporary ;)
  @body.gsub(/[^\s\p{Alnum}\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}]/,'')
   .downcase
   .split
   .count(term) 
end


Should be a Hash lookup, where the Hash is created at init time, time complexity would become O(1) with an Hash instead of O(n).