# = Picky Applications # # A Picky Application is where you configure the whole search engine. # # This is a step-by-step description on how to configure your Picky app. # # Start by subclassing Application: # class MyGreatSearch < Application # # Your configuration goes here. # end # The generator # $ picky generate unicorn_server project_name # will generate an example project_name/app/application.rb file for you # with some example code inside. # # == index(name, source) # # Next, define where your data comes from. You use the index method for that: # my_index = index :some_index_name, some_source # You give the index a name (or identifier), and a source (see Sources), where its data comes from. Let's do that: # class MyGreatSearch < Application # # books = index :books, Sources::CSV.new(:title, :author, :isbn, file:'app/library.csv') # # end # Now we have an index books. # # That on itself won't do much good. # # == index.define_category(identifier, options = {}) # # Picky needs us to define categories on the data. # # Categories help your user find data. # It's best if you look at an example yourself: http://floere.github.com/picky/examples.html # # Let's go ahead and define a category: # class MyGreatSearch < Application # # books = index :books, Sources::CSV.new(:title, :author, :isbn, file:'app/library.csv') # books.define_category :title # # end # Now we could already run the indexer: # $ rake index # # (You can define similarity or partial search capabilities on a category, see http://github.com/floere/picky/wiki/Categories-configuration for info) # # So now we have indexed data (the title), but nobody to ask the index anything. # # == Query::Full.new(*indexes, options = {}) # # We need somebody who asks the index (a Query object, also see http://github.com/floere/picky/wiki/Queries-Configuration). That works like this: # full_books_query = Query::Full.new books # Full just means that the ids are returned with the results. # Picky also offers a Query that returns live results, Query::Live. But that's not important right now. # # Now we have somebody we can ask about the index. But no external interface. # # == route(/regexp1/ => query1, /regexp2/ => query2, ...) # # Let's add a URL path (a Route, see http://github.com/floere/picky/wiki/Routing-configuration) to which we can send our queries. We do that with the route method: # route %r{^/books/full$} => full_books_query # In full glory: # class MyGreatSearch < Application # # books = index :books, Sources::CSV.new(:title, :author, :isbn, file:'app/library.csv') # books.define_category :title # # full_books_query = Query::Full.new books # # route %r{^/books/full$} => full_books_query # # end # That's it! # # Now run the indexer and server: # $ rake index # $ rake start # Run your first query: # $ curl 'localhost:8080/books/full?query=hello server' # # Nice, right? Your first query! # # Maybe you don't find everything. We need to process the data before it goes into the index. # # == default_indexing(options = {}) # # That's what the default_indexing method is for: # default_indexing options # Read more about the options here: http://github.com/floere/picky/wiki/Indexing-configuration # # Same thing with the search text – we need to process that as well. # # == default_querying(options = {}) # # Analog to the default_indexing method, we use the default_querying method. # default_querying options # Read more about the options here: http://github.com/floere/picky/wiki/Querying-Configuration # # And that's all there is. It's incredibly powerful though, as you can combine, weigh, refine to the max. # # == Wiki # # Read more in the Wiki: http://github.com/floere/picky/wiki # # Have fun! # # == Full example # # Our example, fully fleshed out with indexing, querying, and weights: # class MyGreatSearch < Application # # default_indexing removes_characters: /[^a-zA-Z0-9\.]/, # stopwords: /\b(and|or|in|on|is|has)\b/, # splits_text_on: /\s/, # removes_characters_after_splitting: /\./, # substitutes_characters_with: CharacterSubstituters::WestEuropean.new, # normalizes_words: [ # [/(.*)hausen/, 'hn'], # [/\b(\w*)str(eet)?/, 'st'] # ] # # default_querying removes_characters: /[^a-zA-Z0-9\s\/\-\,\&\"\~\*\:]/, # stopwords: /\b(and|the|of|it|in|for)\b/, # splits_text_on: /[\s\/\-\,\&]+/, # removes_characters_after_splitting: /\./, # substitutes_characters_with: CharacterSubstituters::WestEuropean.new, # maximum_tokens: 4 # # books = index :books, Sources::CSV.new(:title, :author, :isbn, file:'app/library.csv') # books.define_category :title, # qualifiers: [:t, :title, :titre], # partial: Partial::Substring.new(:from => 1), # similarity: Similarity::Phonetic.new(2) # books.define_category :author, # partial: Partial::Substring.new(:from => -2) # books.define_category :isbn # # query_options = { :weights => { [:title, :author] => +3, [:author, :title] => -1 } } # # full_books_query = Query::Full.new books, query_options # live_books_query = Query::Full.new books, query_options # # route %r{^/books/full$} => full_books_query # route %r{^/books/live$} => live_books_query # # end # That's actually already a full-blown Picky App! # class Application class << self # API # # Returns a configured tokenizer that # is used for indexing by default. # def default_indexing options = {} Tokenizers::Index.default = Tokenizers::Index.new(options) end # Returns a configured tokenizer that # is used for querying by default. # def default_querying options = {} Tokenizers::Query.default = Tokenizers::Query.new(options) end # Create a new index for indexing and for querying. # # Parameters: # * name: The identifier of the index. Used: # - to identify an index (e.g. by you in Rake tasks). # - in the frontend to describe which index a result came from. # - index directory naming (index/development/the_identifier/) # * source: The source the data comes from. See Sources::Base. # TODO Sources (all). # # Options: # * result_identifier: Use if you'd like a different identifier/name in the results JSON than the name of the index. # def index name, source, options = {} IndexAPI.new name, source, options end # Routes. # delegate :route, :root, :to => :routing # # API # A Picky application implements the Rack interface. # # Delegates to its routing to handle a request. # def call env routing.call env end def routing # :nodoc: @routing ||= Routing.new end # Finalize the subclass as soon as it # has finished loading. # attr_reader :apps # :nodoc: def initialize_apps # :nodoc: @apps ||= [] end def inherited app # :nodoc: initialize_apps apps << app end def finalize_apps # :nodoc: initialize_apps apps.each &:finalize end # Finalizes the routes. # def finalize # :nodoc: check routing.freeze end # Checks app for missing things. # # Warns if something is missing. # # TODO Good specs. # def check # :nodoc: warnings = [] warnings << check_external_interface puts "\n#{warnings.join(?\n)}\n\n" unless warnings.all? &:nil? end def check_external_interface "WARNING: No routes defined for application configuration in #{self.class}." if routing.empty? end # TODO Add more info if possible. # def to_s # :nodoc: "#{self.name}:\n#{routing}" end end end