-
Notifications
You must be signed in to change notification settings - Fork 2
Architecture Overview
For the generally curious, and those who wish to be helpful, here’s an overview of how this library works internally.
The index consists of two database-backed models, generated into your app with the provided migration:
-
Word index (
IndexedSearch::Word
) - every unique word in the index, after parsing, case folding, etc. Also includes cached information about each word that makes querying faster and easier (such as what its soundex value is, how frequently the word is found, etc). -
Result entry index (
IndexedSearch::Entry
) - many-to-many relationship between words and model rows. Also includes cached information about each hit, such as a rank for how important that word is in that model row (being in the name of the object is better than being in the description, for example).
Looking up and displaying results is a 4 step process internally:
-
Parsing a literal query string into words or search terms. For example:
query = IndexedSearch::Query.new('whole string of whole words') # query => ['whole', 'string', 'of', 'words'] (similar to an array, but with extra methods)
-
Look in the
IndexedSearch::Word
model table to find what indexed words match the parsed query. This uses all the enabled matchers internally to do this (IndexedSearch::Match::Base
subclasses, enabled by being listed inIndexedSearch::Match.perform_match_types
). For example:
results = IndexedSearch::Match::ResultList.new(query) results = query.results # results => similar to array of IndexedSearch::Match::Result # query.results is a shortcut, with results cached in the query object
-
Query the big
IndexedSearch::Entry
model table to find the actual app model matches
scope = IndexedSearch::Entry.matching_query(results).ranked_rows(results).paged(page_size, page_number) scope = IndexedSearch::Entry.find_results(query, page_size, page_number) # scope => an entry table scope that has one row per found result (#find_results is a shortcut)
-
loop over the resulting models and display them in a pretty way, for example here’s a simplified version:
scope.each { |s| puts s.model.to_s } # models are cached in the entry objects so you can call them multiple times without pain # note scope can be lazily loaded at the last possible moment in your view
The search index can be built one of two ways: manually via rake tasks, and automatically as your data changes. TODO: finish explaining this.