introduced FlareContext, added saved search feature, and made things more configurable through SOLR_ENV

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@515272 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erik Hatcher 2007-03-06 19:47:55 +00:00
parent 25227a299d
commit ccadb5498c
12 changed files with 237 additions and 149 deletions

View File

@ -16,32 +16,16 @@
class ApplicationController < ActionController::Base
# Pick a unique cookie name to distinguish our session data from others'
session :session_key => '_flare_session_id'
before_filter :flare_before
private
def query
queries = session[:queries]
if queries.nil? || queries.empty?
query = "*:*"
else
query = session[:queries].collect{|q| "#{q[:negative] ? '-' : ''}(#{q[:query]})"}.join(' AND ')
end
def flare_before
session[:page] ||= 1
session[:flare_context] ||= FlareContext.new(SOLR_CONFIG)
query
@flare = session[:flare_context]
end
def filters
session[:filters].collect do |filter|
value = filter[:value]
if value != "[* TO *]"
value = "\"#{value}\""
end
"#{filter[:negative] ? '-' : ''}#{filter[:field]}:#{value}"
end
end
def solr(request)
logger.info "---\n#{request.inspect}\n---"
SOLR.send(request)
end
end

View File

@ -2,8 +2,6 @@
# License:: Apache Version 2.0 (see http://www.apache.org/licenses/)
class BrowseController < ApplicationController
before_filter :flare_before
# def self.flare(options={})
# define_method() do
# end
@ -14,11 +12,6 @@ class BrowseController < ApplicationController
# end
def index
# TODO Add paging and sorting
@info = solr(Solr::Request::IndexInfo.new) # TODO move this call to only have it called when the index may have changed
@facet_fields = @info.field_names.find_all {|v| v =~ /_facet$/}
@text_fields = @info.field_names.find_all {|v| v =~ /_text$/}
session[:page] = params[:page].to_i if params[:page]
session[:page] = 1 if session[:page] <= 0
@ -26,41 +19,31 @@ class BrowseController < ApplicationController
@start = (session[:page] - 1) * @results_per_page
request = Solr::Request::Standard.new(:query => query,
:filter_queries => filters,
:rows => @results_per_page,
:start => @start,
:facets => {
:fields => @facet_fields, :limit => 20 , :mincount => 1, :sort => :count,
# :queries => session[:saved].collect {|constraints| make_query(constraints)}
},
:highlighting => {:field_list => @text_fields})
logger.info({:query => query, :filter_queries => filters}.inspect)
@response = solr(request)
#TODO: call response.field_facets(??) - maybe field_facets should be return a higher level?
@response = @flare.search(@start, @results_per_page)
end
def facet
@facets = retrieve_field_facets(params[:field_name])
puts "---- facet: #{params[:field]}"
@facets = @flare.retrieve_field_facets(params[:field])
end
def auto_complete_for_search_query
# TODO instead of "text", default to the default search field configured in schema.xml
@values = retrieve_field_facets("text", 5, params['search']['query'].downcase)
@values = @flare.retrieve_field_facets("text", 5, params['search']['query'].downcase)
render :partial => 'suggest'
end
def add_query
session[:queries] << {:query => params[:search][:query]}
@flare.queries << {:query => params[:search][:query]}
session[:page] = 1
redirect_to :action => 'index'
end
def update_query
logger.debug "update_query: #{params.inspect}"
session[:queries][params[:index].to_i][:query] = params[:value]
@flare.queries[params[:index].to_i][:query] = params[:value]
session[:page] = 1
render :update do |page|
page.redirect_to '/browse'
@ -68,89 +51,79 @@ class BrowseController < ApplicationController
end
def invert_query
q = session[:queries][params[:index].to_i]
q = @flare.queries[params[:index].to_i]
q[:negative] = !q[:negative]
session[:page] = 1
redirect_to :action => 'index'
end
def remove_query
session[:queries].delete_at(params[:index].to_i)
@flare.queries.delete_at(params[:index].to_i)
session[:page] = 1
redirect_to :action => 'index'
end
def invert_filter
f = session[:filters][params[:index].to_i]
f = @flare.filters[params[:index].to_i]
f[:negative] = !f[:negative]
session[:page] = 1
redirect_to :action => 'index'
end
def remove_filter
session[:filters].delete_at(params[:index].to_i)
@flare.filters.delete_at(params[:index].to_i)
session[:page] = 1
redirect_to :action => 'index'
end
def add_filter
session[:filters] << {:field => params[:field_name], :value => params[:value], :negative => (params[:negative] ? true : false)}
@flare.filters << {:field => params[:field], :value => params[:value], :negative => (params[:negative] ? true : false)}
session[:page] = 1
redirect_to :action => 'index'
end
def add_saved_search
@flare.applied_facet_queries << {:name => params[:name], :negative => (params[:negative] ? true : false)}
redirect_to :action => 'index'
end
def remove_saved_constraint
@flare.applied_facet_queries.delete_at(params[:index].to_i)
session[:page] = 1
redirect_to :action => 'index'
end
def clear
session[:queries] = nil
session[:filters] = nil
session[:page] = 1
flare_before
@flare.clear
redirect_to :action => 'index'
end
def show_saved
query = @flare.facet_queries[params[:name]]
@flare.applied_facet_queries << {:name => params[:name], :negative => (params[:negative] ? true : false)}
index
render :action => 'index'
end
def save
session[:saved] ||= {}
session[:saved][params[:name]] = {:filters => session[:filters], :queries => session[:queries]}
@flare.facet_queries[params[:name]] = {:filters => @flare.filters.clone, :queries => @flare.queries.clone}
redirect_to :action => 'index'
end
private
def flare_before
session[:queries] ||= []
session[:filters] ||= []
session[:page] ||= 1
def remove_saved_search
puts "---- BEFORE", @flare.to_s
@flare.facet_queries.delete(params[:name])
@flare.applied_facet_queries.delete_if {|f| params[:name] == f[:name]}
puts "---- AFTER", @flare.to_s
session[:page] = 1
redirect_to :action => 'index'
end
def retrieve_field_facets(field, limit=-1, prefix=nil)
req = Solr::Request::Standard.new(:query => query,
:filter_queries => filters,
:facets => {:fields => [field],
:mincount => 1, :limit => limit, :prefix => prefix, :missing => true, :sort => :count
},
:rows => 0
)
results = SOLR.send(req)
results.field_facets(field)
def invert_saved_constraint
f = @flare.applied_facet_queries[params[:index].to_i]
f[:negative] = !f[:negative]
session[:page] = 1
redirect_to :action => 'index'
end
def make_query(constraints)
queries = constraints[:queries]
if queries.nil? || queries.empty?
query = "*:*"
else
query = session[:queries].collect{|q| "#{q[:negative] ? '-' : ''}(#{q[:query]})"}.join(' AND ')
end
filter = constraints[:filters].collect do |filter|
value = filter[:value]
if value != "[* TO *]"
value = "\"#{value}\""
end
"#{filter[:negative] ? '-' : ''}#{filter[:field]}:#{value}"
end.join(" AND ")
"#{query} AND #{filter}"
end
end

View File

@ -13,13 +13,8 @@
class SimileController < ApplicationController
def exhibit
# TODO this code was copied from BrowseController#index, and is here only as a quick and dirty prototype.
# TODO figuring out where these calls cleanly belong is the key.
req = Solr::Request::Standard.new :query => query, :filter_queries => filters
@data = @flare.search(0, 10)
@data = SOLR.send(req)
# Exhibit seems to require a label attribute to be happy
@data.each {|d| d['label'] = d['title_text']}
@ -30,15 +25,8 @@ class SimileController < ApplicationController
end
def timeline
# TODO this code was copied from BrowseController#index, and is here only as a quick and dirty prototype.
# TODO figuring out where these calls cleanly belong is the key.
@info = SOLR.send(Solr::Request::IndexInfo.new) # TODO move this call to only have it called when the index may have changed
req = Solr::Request::Standard.new :query => query, :filter_queries => filters
@data = SOLR.send(req)
@data = @flare.search(0, 10)
respond_to do |format|
format.html # renders timeline.rhtml
format.xml # renders timeline.rxml

View File

@ -6,7 +6,7 @@
<div>
Queries:
<div id="queries">
<% session[:queries].each_with_index do |q,i| %>
<% @flare.queries.each_with_index do |q,i| %>
<%=link_to q[:negative] ? "-" : '+', :action => :invert_query, :index => i%>
<span id="query_<%=i%>"><%=q[:query]%></span>
<%= in_place_editor "query_#{i}", :url=> url_for(:action=>"update_query", :index=>i) %>
@ -18,7 +18,7 @@ Queries:
<div>
Filters:
<div id="filters">
<% session[:filters].each_with_index do |filter, i| %>
<% @flare.filters.each_with_index do |filter, i| %>
<%=link_to filter[:negative] ? "-" : "+", :action => :invert_filter, :index => i%>
<%=filter[:field]%>:<%=filter[:value]%>
<%=link_to image_tag("x-close.gif"), :action => :remove_filter, :index => i %>
@ -32,10 +32,11 @@ Filters:
</div>
<h1><%=params[:field]%></h1>
<% @facets.each do |f| %>
<% if f.name %>
<%= link_to f.name, :action => 'add_filter', :field_name => params[:field_name], :value => f.name %> (<%=f.value%>)
<%= link_to f.name, :action => 'add_filter', :field => params[:field], :value => f.name %> (<%=f.value%>)
<% else %>
<%= link_to '---- NO VALUE ----', :action => 'add_filter', :field_name => params[:field_name], :value => "[* TO *]", :negative => true %> (<%=f.value%>)
<%= link_to '---- NO VALUE ----', :action => 'add_filter', :field => params[:field], :value => "[* TO *]", :negative => true %> (<%=f.value%>)
<% end %>
<% end%>

View File

@ -7,18 +7,33 @@
</div>
<!-- <h2>browse facets</h2> -->
<% @facet_fields.each do |field|%>
<h4>
<%=h field%> <%=link_to "[browse]", :action => 'facet', :field_name => field%>
<!-- <%=link_to field, :action => 'facet', :field_name => field%> -->
</h4>
<h4>
saved searches
</h4>
<ul>
<% @flare.facet_queries.each do |name,value|
count = @response.data['facet_counts']['facet_queries'][value[:real_query]]
%>
<li>
<%= image_tag "pie_#{(count * 100.0 / @response.total_hits).ceil rescue 0}.png"%>
<%= link_to "#{name} (#{count})", {:action => 'add_saved_search', :name=>name}, {:title => value.inspect}%>
<%= link_to "*", {:action => 'show_saved', :name => name}, {:target => '_blank'}%>
<%=link_to image_tag("x-close.gif"), :action => :remove_saved_search, :name => name %>
</li>
<% end %>
</ul>
<% @flare.facet_fields.each do |field|%>
<h4>
<%=h field%> <%=link_to "[browse]", :action => 'facet', :field => field%>
<!-- <%=link_to field, :action => 'facet', :field => field%> -->
</h4>
<ul>
<% @response.field_facets(field).each do |f| %>
<li>
<%= image_tag "pie_#{(f.value * 100.0 / @response.total_hits).ceil}.png"%>
<%= link_to "#{f.name} (#{f.value})", :action => 'add_filter', :field_name=>field, :value=>f.name%>
<%= link_to "#{f.name} (#{f.value})", :action => 'add_filter', :field=>field, :value=>f.name%>
</li>
<% end %>
</ul>
@ -32,9 +47,17 @@
</div>
<div>
<span class="varheader">Saved searches:</span>
<div id="queries">
<% @flare.applied_facet_queries.each_with_index do |q, i| %>
<%=link_to q[:negative] ? "-" : "+", :action => :invert_saved_constraint, :index => i%>
<%=q[:name]%>
<%=link_to image_tag("x-close.gif"), :action => :remove_saved_constraint, :index => i %><br/>
<% end %>
</div>
<span class="varheader">Queries:</span>
<div id="queries">
<% session[:queries].each_with_index do |q,i| %>
<% @flare.queries.each_with_index do |q,i| %>
<%=link_to q[:negative] ? "-" : '+', :action => :invert_query, :index => i%>
<span id="query_<%=i%>"><%=q[:query]%></span>
<%= in_place_editor "query_#{i}", :url=> url_for(:action=>"update_query", :index=>i, :script=>true) %>
@ -46,7 +69,7 @@
<div>
<span class="varheader">Filters:</span>
<div id="filters">
<% session[:filters].each_with_index do |filter, i| %>
<% @flare.filters.each_with_index do |filter, i| %>
<%=link_to filter[:negative] ? "-" : "+", :action => :invert_filter, :index => i%>
<%=filter[:field]%>:<%=filter[:value]%>
<%=link_to image_tag("x-close.gif"), :action => :remove_filter, :index => i %><br/>
@ -58,9 +81,14 @@
<%=link_to '[clear constraints]', :action => 'clear'%>
</div>
<% form_tag(:action=>'save') do %>
<%= text_field_tag :name %>
<%=submit_tag "save"%>
<% end %>
</div>
<div class="resultsheader">Results <strong><%=@start + 1%>-<%=[@response.total_hits,@results_per_page + @start].min%></strong> of <strong><%=@response.total_hits%></strong></div>
<div class="resultsheader">Results <strong><%=[@start + 1,@response.total_hits].min%>-<%=[@response.total_hits,@results_per_page + @start].min%></strong> of <strong><%=@response.total_hits%></strong></div>
<div id="results"><table cellpadding="10">
<% @response.each do |doc| %>
@ -69,11 +97,5 @@
</table>
</div>
<div class="resultsheader"><%=link_to_if session[:page] != 1, "<<", :page => session[:page] - 1%> Results <strong><%=@start + 1%>-<%=[@response.total_hits,@results_per_page + @start].min%></strong> of <strong><%=@response.total_hits%></strong><%=link_to_if session[:page] < (@response.total_hits.to_f / @results_per_page).ceil, ">>", :page => session[:page] + 1%></div>
<div class="resultsheader"><%=link_to_if session[:page] != 1, "<<", :page => session[:page] - 1%> Results <strong><%=[@start + 1,@response.total_hits].min%>-<%=[@response.total_hits,@results_per_page + @start].min%></strong> of <strong><%=@response.total_hits%></strong><%=link_to_if session[:page] < (@response.total_hits.to_f / @results_per_page).ceil, ">>", :page => session[:page] + 1%></div>
<!-- <h3>index info</h3><%=debug @info %>
<h3>search/facet response header</h3><%=debug @response.data['responseHeader']%> -->

View File

@ -1,13 +1,13 @@
<tr valign="top">
<td>
<%=image_tag "http://images.amazon.com/images/P/#{@doc[:asin_text]}.01.MZZZZZZZ" %>
<%=image_tag "http://images.amazon.com/images/P/#{doc['asin_text']}.01.MZZZZZZZ" %>
</td>
<td>
<table class="entry">
<tr>
<td class="title" colspan="2"><%=link_to @doc['title_text'], "http://www.amazon.com/exec/obidos/ASIN/#{@doc[:asin_text]}"%></td>
<td class="title" colspan="2"><%=link_to doc['title_text'], "http://www.amazon.com/exec/obidos/ASIN/#{doc['asin_text']}"%></td>
</tr>
<% @doc.each do |k,v|; highlighting = @response.highlighted(@doc['id'], k) %>
<% doc.each do |k,v|; highlighting = response.highlighted(doc['id'], k) %>
<tr><td class="field"><%=k%>:</td><td><%= highlighting ? "...#{highlighting}..." : (v.respond_to?('join') ? v.join(',') : v.to_s)%></td></tr>
<% end %>
</table>

View File

@ -1,27 +1,27 @@
<%
body_zh_highlighted = @response.highlighted(@doc['id'], 'body_zh_text')
body_en_highlighted = @response.highlighted(@doc['id'], 'body_en_text')
body_zh_highlighted = response.highlighted(doc['id'], 'body_zh_text')
body_en_highlighted = response.highlighted(doc['id'], 'body_en_text')
-%>
<tr valign="top">
<td>
<table class="entry">
<tr>
<td class="title" colspan="2"><%=@doc['title_zh_text']%> (<%=@doc['title_en_text']%>)</td>
<td class="title" colspan="2"><%=doc['title_zh_text']%> (<%=doc['title_en_text']%>)</td>
</tr>
<tr>
<td class="field">author:</td><td><%=@doc['author_zh_facet']%> (<%=@doc['author_en_facet']%>)</td>
<td class="field">author:</td><td><%=doc['author_zh_facet']%> (<%=doc['author_en_facet']%>)</td>
</tr>
<tr>
<td class="field">type:</td><td><%=@doc['type_zh_facet']%> (<%=@doc['type_en_facet']%>)</td>
<td class="field">type:</td><td><%=doc['type_zh_facet']%> (<%=doc['type_en_facet']%>)</td>
</tr>
<tr>
<td class="field">body:</td>
<td>
<blockquote>
<%= body_zh_highlighted ? "...#{body_zh_highlighted}..." : @doc['body_zh_text'] %>
<%= body_zh_highlighted ? "...#{body_zh_highlighted}..." : doc['body_zh_text'] %>
</blockquote>
<blockquote>
<%= body_en_highlighted ? "...#{body_en_highlighted}..." : @doc['body_en_text'] %>
<%= body_en_highlighted ? "...#{body_en_highlighted}..." : doc['body_en_text'] %>
</blockquote>
</td>
</tr>

View File

@ -16,7 +16,7 @@
<div id="exhibit-view-panel"></div>
</td>
<td width="25%">
<div id="exhibit-browse-panel" ex:facets=".subject_genre_facet, .subject_era_facet, .subject_topic_facet, .subject_geographic_facet, .year_facet"></div>
<div id="exhibit-browse-panel" ex:facets="<%= SOLR_CONFIG[:exhibit_facets] -%>"></div>
</td>
</tr>
</table>

View File

@ -11,7 +11,7 @@
eventSource: eventSource,
date: "Jun 28 2006 00:00:00 GMT",
width: "70%",
intervalUnit: Timeline.DateTime.CENTURY,
intervalUnit: Timeline.DateTime.DECADE,
intervalPixels: 200
}),
Timeline.createBandInfo({

View File

@ -68,14 +68,16 @@ solr_environments = {
:delicious => {
# :image_url => Proc.new {|d| "http://images.amazon.com/images/P/#{d[:asin_text]}.01.MZZZZZZZ"},
:timeline_dates => :published_year_facet
:timeline_dates => :published_year_facet,
:exhibit_facets => ".medium_facet, .country_facet, .signed_facet, .rating_facet, .language_facet, .genre_facet, .publisher_facet, .published_year_facet"
},
:tang => {
},
:marc => {
:timeline_dates => :year_facet
:timeline_dates => :year_facet,
:exhibit_facets => ".subject_genre_facet, .subject_era_facet, .subject_topic_facet, .subject_geographic_facet, .year_facet"
},
# TODO: :uva could inherit :marc settings, only overriding the template for VIRGO links
@ -86,5 +88,5 @@ solr_environments = {
SOLR_ENV = ENV["SOLR_ENV"] || "development"
SOLR_CONFIG = solr_environments[SOLR_ENV.to_sym]
puts "#{SOLR_ENV}: SOLR_CONFIG = #{SOLR_CONFIG.inspect}"
solr_url = SOLR_CONFIG[:solr_url] || "http://localhost:8983/solr"
SOLR = Solr::Connection.new(solr_url)
SOLR_CONFIG[:solr_url] ||= "http://localhost:8983/solr"
#SOLR = Solr::Connection.new(SOLR_CONFIG[:solr_url])

View File

@ -13,7 +13,9 @@ ActionController::Routing::Routes.draw do |map|
# -- just remember to delete public/index.html.
map.connect '', :controller => "browse"
map.connect 'browse/facet/:field', :controller => "browse", :action => "facet"
map.connect 'browse/facet', :controller => "browse", :action => "facet"
map.connect 'saved/:name', :controller => 'browse', :action => 'show_saved'
# Allow downloading Web Service WSDL as a file with an extension
# instead of a file named 'wsdl'

View File

@ -0,0 +1,116 @@
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# TODO Add sorting
class FlareContext
attr_accessor :queries, :filters, :facet_queries, :applied_facet_queries
attr_reader :facet_fields, :text_fields
def initialize(solr_config)
@connection = Solr::Connection.new(solr_config[:solr_url])
clear
@facet_queries = {} # name => {:queries => [], :filters => []}
puts "initialize\n-------","#{solr_config.inspect}"
@index_info = @connection.send(Solr::Request::IndexInfo.new)
@facet_fields = @index_info.field_names.find_all {|v| v =~ /_facet$/}
@text_fields = @index_info.field_names.find_all {|v| v =~ /_text$/}
end
def clear
puts "clear\n-------"
@queries = []
@filters = []
@applied_facet_queries = []
# this is cleared for development purposes - allowing flare to stay running but different Solr datasets swapping
@index_info = @connection.send(Solr::Request::IndexInfo.new)
@facet_fields = @index_info.field_names.find_all {|v| v =~ /_facet$/}
@text_fields = @index_info.field_names.find_all {|v| v =~ /_text$/}
# facet_queries not cleared as their lifetime is different than constraints
end
def search(start, max)
facet_queries = @facet_queries.collect do |k,v|
clauses = filter_queries(v[:filters])
clauses << build_boolean_query(v[:queries])
query = clauses.join(" AND ")
@facet_queries[k][:real_query] = query
query
end
qa = applied_facet_queries.collect {|map| q = @facet_queries[map[:name]][:real_query]; map[:negative] ? "-(#{q})" : q}
qa << build_boolean_query(@queries)
request = Solr::Request::Standard.new(:query => qa.join(" AND "),
:filter_queries => filter_queries(@filters),
:start => start,
:rows => max,
:facets => {
:fields => @facet_fields, :limit => 20 , :mincount => 1, :sort => :count,
:queries => facet_queries
},
:highlighting => {:field_list => @text_fields})
#TODO: call response.field_facets(??) - maybe field_facets should be return a higher level?
# logger.info({:query => query, :filter_queries => filters}.inspect)
@connection.send(request)
end
def retrieve_field_facets(field, limit=-1, prefix=nil)
req = Solr::Request::Standard.new(:query => build_boolean_query(@queries),
:filter_queries => filter_queries(@filters),
:facets => {:fields => [field],
:mincount => 1, :limit => limit, :prefix => prefix, :missing => true, :sort => :count
},
:rows => 0
)
results = @connection.send(req)
results.field_facets(field)
end
def to_s
<<-TO_S
------
Applied facet queries: #{applied_facet_queries.inspect}
Queries: #{queries.inspect}
Filters: #{filters.inspect}
Facet queries: #{facet_queries.inspect}
------
TO_S
end
private
def build_boolean_query(queries)
if queries.nil? || queries.empty?
query = "*:*"
else
query = queries.collect{|q| "#{q[:negative] ? '-' : ''}(#{q[:query]})"}.join(' AND ')
end
query
end
def filter_queries(filters)
filters.collect do |filter|
value = filter[:value]
if value != "[* TO *]"
value = "\"#{value}\""
end
"#{filter[:negative] ? '-' : ''}#{filter[:field]}:#{value}"
end
end
end