Commit 0650546d authored by Frank Bergmann's avatar Frank Bergmann

Initial Import

parents
<?xml version="1.0" encoding="ISO-8859-1"?>
<message_catalog package_key="search" package_version="5.1.3" locale="en_US" charset="ISO-8859-1">
<msg key="________Search_took">.
Search took</msg>
<msg key="Advanced_Search">Advanced Search</msg>
<msg key="advanced_search">&quot;advanced search&quot;</msg>
<msg key="anytime">anytime</msg>
<msg key="Date_Range">Date Range</msg>
<msg key="details">details</msg>
<msg key="Feeling_Lucky">Feeling Lucky</msg>
<msg key="lt_bstopwordsb_is_a_very">&quot;&lt;b&gt;%stopwords%&lt;/b&gt;&quot; is a very common word and was not included in your search.</msg>
<msg key="lt_FtsEngineDriver_not_a">FtsEngineDriver not available!</msg>
<msg key="lt_Make_sure_all_words_a">Make sure all words are spelled correctly.</msg>
<msg key="lt_No_pages_were_found_c">No pages were found containing &quot;</msg>
<msg key="lt_The_following_words_a">The following words are very common and were not included in your search:</msg>
<msg key="lt_Tip_In_most_browsers_">Tip: In most browsers you can just hit the return key instead of clicking on the search button.</msg>
<msg key="lt_Try_different_keyword">Try different keywords.</msg>
<msg key="lt_Try_more_general_keyw">Try more general keywords.</msg>
<msg key="lt_Try_your_query_on_stw">Try your query on: %stw;noquote%</msg>
<msg key="lt_You_must_specify_some">You must specify some keywords</msg>
<msg key="nbspDisplay">&amp;nbsp;Display</msg>
<msg key="Next">Next</msg>
<msg key="of_about">of about</msg>
<msg key="past_3_months">past 3 months</msg>
<msg key="past_6_months">past 6 months</msg>
<msg key="past_year">past year</msg>
<msg key="Previous">Previous</msg>
<msg key="Result_page">Result page:</msg>
<msg key="Results">Results</msg>
<msg key="results">results</msg>
<msg key="Search">Search</msg>
<msg key="Searched_for_query">Searched for: %query%</msg>
<msg key="seconds">seconds.</msg>
<msg key="selected">selected</msg>
<msg key="Suggestions">Suggestions:</msg>
<msg key="The">&quot;&lt;b&gt;AND&lt;/b&gt;&quot; operator is unnecessary -- we include all search terms by default.</msg>
<msg key="Try_fewer_keywords">Try fewer keywords.</msg>
<msg key="Untitled">Untitled</msg>
</message_catalog>
<?xml version="1.0"?>
<!-- Generated by the OpenACS Package Manager -->
<package key="search" url="http://openacs.org/repository/apm/packages/search" type="apm_application">
<package-name>Search</package-name>
<pretty-plural>Search</pretty-plural>
<initial-install-p>t</initial-install-p>
<singleton-p>f</singleton-p>
<version name="5.1.5" url="http://openacs.org/repository/download/apm/search-5.1.5.apm">
<owner url="http://openacs.org">OpenACS</owner>
<release-date>2004-02-28</release-date>
<maturity>3</maturity>
<summary>Site wide search</summary>
<vendor url="http://openacs.org">OpenACS</vendor>
<description format="text/html">Site wide search implemented with service contracts, currently
only supports postgres via the OpenFTS driver.</description>
<provides url="search" version="5.1.4"/>
<callbacks>
</callbacks>
<parameters>
<parameter datatype="number" min_n_values="1" max_n_values="1" name="FtsEngineDriver" default="openfts-driver" description="Which search engine driver to use? You should enter the name of the related binding (e.g. openfts-driver, swish-driver,...)"/>
<parameter datatype="number" min_n_values="1" max_n_values="1" name="LimitDefault" default="10" description="Default limit value"/>
<parameter datatype="number" min_n_values="1" max_n_values="1" name="SearchIndexerInterval" default="30" description="SearchIndexerInterval"/>
<parameter datatype="string" min_n_values="1" max_n_values="1" name="SearchTheWeb" default="http://av.com/sites/search/web?q=%s AltaVista http://search.excite.com/search.gw?s=%s Excite http://www.google.com/search?q=%s Google http://hotbot.com/?MT=%s HotBot http://lycos.com/cgi-bin/pursuit?query=%s Lycos http://search.yahoo.com/search?p=%s Yahoo!" description="Search the Web (e.g. Google)"/>
<parameter datatype="string" min_n_values="1" max_n_values="1" name="Symbol2Interval" default="m3 {3 month ago} m6 {6 month ago} y1 {1 year ago}" description="Translate symbol to date interval."/>
</parameters>
</version>
</package>
@@ search-tables-create.sql
@@ search-packages-create.sql
@@ search-sc-create.sql
@@ search-sc-drop.sql
@@ search-packages-drop.sql
@@ search-tables-drop.sql
--
-- Search Observer
--
create or replace package search_observer
as
procedure enqueue (
object_id in search_observer_queue.object_id%TYPE,
event in search_observer_queue.event%TYPE
);
procedure dequeue (
object_id in search_observer_queue.object_id%TYPE,
event_date in search_observer_queue.event_date%TYPE,
event in search_observer_queue.event%TYPE
);
end search_observer;
/
show errors
create or replace package body search_observer
as
procedure enqueue (
object_id in search_observer_queue.object_id%TYPE,
event in search_observer_queue.event%TYPE
)
is
begin
insert
into search_observer_queue
(object_id, event)
values
(enqueue.object_id, enqueue.event);
end enqueue;
procedure dequeue (
object_id in search_observer_queue.object_id%TYPE,
event_date in search_observer_queue.event_date%TYPE,
event in search_observer_queue.event%TYPE
)
is
begin
delete
from search_observer_queue
where object_id = dequeue.object_id
and event = dequeue.event
and event_date = dequeue.event_date;
end dequeue;
end search_observer;
/
show errors
drop package body search_observer;
drop package search_observer;
declare
foo integer;
begin
--
-- ACS-SC Contract: FtsEngineDriver
--
foo := acs_sc_contract.new(
contract_name => 'FtsEngineDriver',
contract_desc => 'Full Text Search Engine Driver'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Search.InputType',
msg_type_spec => 'query:string,offset:integer,limit:integer,user_id:integer,df:timestamp,dt:timestamp'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Search.OutputType',
msg_type_spec => 'ids:integer[],stopwords:string[]'
);
foo := acs_sc_operation.new(
contract_name => 'FtsEngineDriver',
operation_name => 'search',
operation_desc => 'Search',
operation_iscachable_p => 'f',
operation_nargs => 6,
operation_inputtype => 'FtsEngineDriver.Search.InputType',
operation_outputtype => 'FtsEngineDriver.Search.OutputType'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Index.InputType',
msg_type_spec => 'object_id:integer,txt:string,title:string,keywords:string'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Index.OutputType',
msg_type_spec => ''
);
foo := acs_sc_operation.new(
contract_name => 'FtsEngineDriver',
operation_name => 'index',
operation_desc => 'Index',
operation_iscachable_p => 'f',
operation_nargs => 4,
operation_inputtype => 'FtsEngineDriver.Index.InputType',
operation_outputtype => 'FtsEngineDriver.Index.OutputType'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Unindex.InputType',
msg_type_spec => 'object_id:integer'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Unindex.OutputType',
msg_type_spec => ''
);
foo := acs_sc_operation.new(
contract_name => 'FtsEngineDriver',
operation_name => 'unindex',
operation_desc => 'Unindex',
operation_iscachable_p => 'f',
operation_nargs => 1,
operation_inputtype => 'FtsEngineDriver.Unindex.InputType',
operation_outputtype => 'FtsEngineDriver.Unindex.OutputType'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.UpdateIndex.InputType',
msg_type_spec => 'object_id:integer,txt:string,title:string,keywords:string'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.UpdateIndex.OutputType',
msg_type_spec => ''
);
foo := acs_sc_operation.new(
contract_name => 'FtsEngineDriver',
operation_name => 'update_index',
operation_desc => 'Update Index',
operation_iscachable_p => 'f',
operation_nargs => 4,
operation_inputtype => 'FtsEngineDriver.UpdateIndex.InputType',
operation_outputtype => 'FtsEngineDriver.UpdateIndex.OutputType'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Summary.InputType',
msg_type_spec => 'query:string,txt:string'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Summary.OutputType',
msg_type_spec => 'summary:string'
);
foo := acs_sc_operation.new(
contract_name => 'FtsEngineDriver',
operation_name => 'summary',
operation_desc => 'Summary',
operation_iscachable_p => 'f',
operation_nargs => 2,
operation_inputtype => 'FtsEngineDriver.Summary.InputType',
operation_outputtype => 'FtsEngineDriver.Summary.OutputType'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Info.InputType',
msg_type_spec => ''
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsEngineDriver.Info.OutputType',
msg_type_spec => 'package_key:string,version:version,automatic_and_queries_p:boolean,stopwords_p:boolean'
);
foo := acs_sc_operation.new(
contract_name => 'FtsEngineDriver',
operation_name => 'info',
operation_desc => 'Information about the driver',
operation_iscachable_p => 'f',
operation_nargs => 1,
operation_inputtype => 'FtsEngineDriver.Info.InputType',
operation_outputtype => 'FtsEngineDriver.Info.OutputType'
);
--
-- ACS-SC Contract: FtsContentProvider
--
foo := acs_sc_contract.new(
contract_name => 'FtsContentProvider',
contract_desc => 'Full Text Search Content Provider'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsContentProvider.Datasource.InputType',
msg_type_spec => 'object_id:integer'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsContentProvider.Datasource.OutputType',
msg_type_spec => 'object_id:integer,title:string,content:string,mime:string,storage_type:string'
);
foo := acs_sc_operation.new(
contract_name => 'FtsContentProvider',
operation_name => 'datasource',
operation_desc => 'Data Source',
operation_iscachable_p => 'f',
operation_nargs => 1,
operation_inputtype => 'FtsContentProvider.Datasource.InputType',
operation_outputtype => 'FtsContentProvider.Datasource.OutputType'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsContentProvider.Url.InputType',
msg_type_spec => 'object_id:integer'
);
foo := acs_sc_msg_type.new(
msg_type_name => 'FtsContentProvider.Url.OutputType',
msg_type_spec => 'url:uri'
);
foo := acs_sc_operation.new(
contract_name => 'FtsContentProvider',
operation_name => 'url',
operation_desc => 'URL',
operation_iscachable_p => 'f',
operation_nargs => 1,
operation_inputtype => 'FtsContentProvider.Url.InputType',
operation_outputtype => 'FtsContentProvider.Url.OutputType'
);
end;
/
show errors
declare
begin
acs_sc_contract.del(contract_name => 'FtsContentProvider');
acs_sc_msg_type.del(msg_type_name => 'FtsContentProvider.Datasource.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsContentProvider.Datasource.OutputType');
acs_sc_msg_type.del(msg_type_name => 'FtsContentProvider.Url.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsContentProvider.Url.OutputType');
acs_sc_contract.del(contract_name => 'FtsEngineDriver');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Search.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Search.OutputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Index.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Index.OutputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Unindex.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Unindex.OutputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.UpdateIndex.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.UpdateIndex.OutputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Summary.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Summary.OutputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Info.InputType');
acs_sc_msg_type.del(msg_type_name => 'FtsEngineDriver.Info.OutputType');
end;
/
show errors
create table search_observer_queue (
object_id integer,
event_date date
default sysdate,
event varchar(6)
constraint search_observer_queue_event_ck
check (event in ('INSERT','DELETE','UPDATE'))
);
drop table search_observer_queue;
\i search-tables-create.sql
\i search-packages-create.sql
\i search-sc-create.sql
\i search-sc-drop.sql
\i search-packages-drop.sql
\i search-tables-drop.sql
--
-- Search Observer
--
create function search_observer__enqueue(integer,varchar)
returns integer as '
declare
p_object_id alias for $1;
p_event alias for $2;
begin
insert into search_observer_queue (
object_id,
event
) values (
p_object_id,
p_event
);
return 0;
end;' language 'plpgsql';
create function search_observer__dequeue(integer,timestamptz,varchar)
returns integer as '
declare
p_object_id alias for $1;
p_event_date alias for $2;
p_event alias for $3;
begin
delete from search_observer_queue
where object_id = p_object_id
and event = p_event
and to_char(event_date,''yyyy-mm-dd hh24:mi:ss.us-tz'') = to_char(p_event_date,''yyyy-mm-dd hh24:mi:ss.us-tz'');
return 0;
end;' language 'plpgsql';
drop function search_observer__dequeue(integer,timestamptz,varchar);
drop function search_observer__enqueue(integer,varchar);
--
-- ACS-SC Contract: FtsEngineDriver
--
select acs_sc_contract__new (
'FtsEngineDriver', -- contract_name
'Full Text Search Engine Driver' -- contract_desc
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Search.InputType',
'query:string,offset:integer,limit:integer,user_id:integer,df:timestamp,dt:timestamp'
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Search.OutputType',
'ids:integer[],stopwords:string[]'
);
select acs_sc_operation__new (
'FtsEngineDriver', -- contract_name
'search', -- operation_name
'Search', -- operation_desc
'f', -- operation_iscachable_p
6, -- operation_nargs
'FtsEngineDriver.Search.InputType', -- operation_inputtype
'FtsEngineDriver.Search.OutputType' -- operation_outputtype
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Index.InputType',
'object_id:integer,txt:string,title:string,keywords:string'
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Index.OutputType',
''
);
select acs_sc_operation__new (
'FtsEngineDriver', -- contract_name
'index', -- operation_name
'Index', -- operation_desc
'f', -- operation_iscachable_p
4, -- operation_nargs
'FtsEngineDriver.Index.InputType', -- operation_inputtype
'FtsEngineDriver.Index.OutputType' -- operation_outputtype
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Unindex.InputType',
'object_id:integer'
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Unindex.OutputType',
''
);
select acs_sc_operation__new (
'FtsEngineDriver', -- contract_name
'unindex', -- operation_name
'Unindex', -- operation_desc
'f', -- operation_iscachable_p
1, -- operation_nargs
'FtsEngineDriver.Unindex.InputType', -- operation_inputtype
'FtsEngineDriver.Unindex.OutputType' -- operation_outputtype
);
select acs_sc_msg_type__new (
'FtsEngineDriver.UpdateIndex.InputType',
'object_id:integer,txt:string,title:string,keywords:string'
);
select acs_sc_msg_type__new (
'FtsEngineDriver.UpdateIndex.OutputType',
''
);
select acs_sc_operation__new (
'FtsEngineDriver', -- contract_name
'update_index', -- operation_name
'Update Index', -- operation_desc
'f', -- operation_iscachable_p
4, -- operation_nargs
'FtsEngineDriver.UpdateIndex.InputType', -- operation_inputtype
'FtsEngineDriver.UpdateIndex.OutputType' -- operation_outputtype
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Summary.InputType',
'query:string,txt:string'
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Summary.OutputType',
'summary:string'
);
select acs_sc_operation__new (
'FtsEngineDriver', -- contract_name
'summary', -- operation_name
'Summary', -- operation_desc
'f', -- operation_iscachable_p
2, -- operation_nargs
'FtsEngineDriver.Summary.InputType', -- operation_inputtype
'FtsEngineDriver.Summary.OutputType' -- operation_outputtype
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Info.InputType',
''
);
select acs_sc_msg_type__new (
'FtsEngineDriver.Info.OutputType',
'package_key:string,version:version,automatic_and_queries_p:boolean,stopwords_p:boolean'
);
select acs_sc_operation__new (
'FtsEngineDriver', -- contract_name
'info', -- operation_name
'Information about the driver', -- operation_desc
'f', -- operation_iscachable_p
1, -- operation_nargs
'FtsEngineDriver.Info.InputType', -- operation_inputtype
'FtsEngineDriver.Info.OutputType' -- operation_outputtype
);
--
-- ACS-SC Contract: FtsContentProvider
--
select acs_sc_contract__new (
'FtsContentProvider', -- contract_name
'Full Text Search Content Provider' -- contract_desc
);
select acs_sc_msg_type__new (
'FtsContentProvider.Datasource.InputType',
'object_id:integer'
);
select acs_sc_msg_type__new (
'FtsContentProvider.Datasource.OutputType',
'object_id:integer,title:string,content:string,mime:string,storage_type:string'
);
select acs_sc_operation__new (
'FtsContentProvider', -- contract_name
'datasource', -- operation_name
'Data Source', -- operation_desc
'f', -- operation_iscachable_p
1, -- operation_nargs
'FtsContentProvider.Datasource.InputType', -- operation_inputtype
'FtsContentProvider.Datasource.OutputType' -- operation_outputtype
);
select acs_sc_msg_type__new (
'FtsContentProvider.Url.InputType',
'object_id:integer'
);
select acs_sc_msg_type__new (
'FtsContentProvider.Url.OutputType',
'url:uri'
);
select acs_sc_operation__new (
'FtsContentProvider', -- contract_name
'url', -- operation_name
'URL', -- operation_desc
'f', -- operation_iscachable_p
1, -- operation_nargs
'FtsContentProvider.Url.InputType', -- operation_inputtype
'FtsContentProvider.Url.OutputType' -- operation_outputtype
);
select acs_sc_contract__delete('FtsContentProvider');
select acs_sc_msg_type__delete ('FtsContentProvider.Datasource.InputType');
select acs_sc_msg_type__delete ('FtsContentProvider.Datasource.OutputType');
select acs_sc_msg_type__delete ('FtsContentProvider.Url.InputType');
select acs_sc_msg_type__delete ('FtsContentProvider.Url.OutputType');
select acs_sc_contract__delete('FtsEngineDriver');
select acs_sc_msg_type__delete ('FtsEngineDriver.Search.InputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Search.OutputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Index.InputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Index.OutputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Unindex.InputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Unindex.OutputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.UpdateIndex.InputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.UpdateIndex.OutputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Summary.InputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Summary.OutputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Info.InputType');
select acs_sc_msg_type__delete ('FtsEngineDriver.Info.OutputType');
create table search_observer_queue (
object_id integer,
event_date timestamptz default current_timestamp,
event varchar(6)
constraint search_observer_queue_event_ck
check (event in ('INSERT','DELETE','UPDATE'))
);
drop table search_observer_queue;
-- packages/search/sql/postgresql/upgrade/upgrade-4.2-4.5.sql
--
-- @author jon@jongriffi.com
-- @creation-date 2002-08-02
-- @cvs-id $Id$
--
-- search-packages-create.sql
drop function search_observer__dequeue(integer,timestamp with time zone,varchar);
create function search_observer__dequeue(integer,timestamp with time zone,varchar)
returns integer as '
declare
p_object_id alias for $1;
p_event_date alias for $2;
p_event alias for $3;
begin
delete from search_observer_queue
where object_id = p_object_id
and event = p_event
and event_date = p_event_date;
return 0;
end;' language 'plpgsql';
--
alter table search_observer_queue rename column date to event_date;
ad_schedule_proc -thread t [parameter::get_from_package_key -package_key search -parameter SearchIndexerInterval -default 60 ] search_indexer
<?xml version="1.0"?>
<queryset>
<rdbms><type>oracle</type><version>8.1.6</version></rdbms>
<fullquery name="search_indexer.search_observer_dequeue_entry">
<querytext>
declare
begin
search_observer.dequeue(
object_id => :object_id,
event_date => :date,
event => :event
);
end;
</querytext>
</fullquery>
</queryset>
<?xml version="1.0"?>
<queryset>
<rdbms><type>postgresql</type><version>7.1</version></rdbms>
<fullquery name="search_indexer.search_observer_dequeue_entry">
<querytext>
select search_observer__dequeue(
:object_id,
:event_date,
:event
);
</querytext>
</fullquery>
</queryset>
ad_library {
full-text search engine
@author Neophytos Demetriou (k2pts@yahoo.com)
@cvs-id $Id$
}
ad_proc search_indexer {} {
@author Neophytos Demetriou
} {
set driver [ad_parameter -package_id [apm_package_id_from_key search] FtsEngineDriver]
if { [empty_string_p $driver] } {
# Nothing to do if no driver
return
}
db_foreach search_observer_queue_entry {} {
switch $event {
INSERT {
set object_type [acs_object_type $object_id]
if {[acs_sc_binding_exists_p FtsContentProvider $object_type]} {
array set datasource [acs_sc_call FtsContentProvider datasource [list $object_id] $object_type]
search_content_get txt $datasource(content) $datasource(mime) $datasource(storage_type)
acs_sc_call FtsEngineDriver index [list $datasource(object_id) $txt $datasource(title) $datasource(keywords)] $driver
}
# Remember seeing this object so we can avoid reindexing it later
set seen($object_id) 1
}
DELETE {
acs_sc_call FtsEngineDriver unindex [list $object_id] $driver
}
UPDATE {
# Don't bother reindexing if we've already inserted/updated this object in this run
if {![info exists seen($object_id)]} {
set object_type [acs_object_type $object_id]
if {[acs_sc_binding_exists_p FtsContentProvider $object_type]} {
array set datasource [acs_sc_call FtsContentProvider datasource [list $object_id] $object_type]
search_content_get txt $datasource(content) $datasource(mime) $datasource(storage_type)
acs_sc_call FtsEngineDriver update_index [list $datasource(object_id) $txt $datasource(title) $datasource(keywords)] $driver
}
# Remember seeing this object so we can avoid reindexing it later
set seen($object_id) 1
}
}
}
db_exec_plsql search_observer_dequeue_entry {}
}
}
ad_proc search_content_get {
_txt
content
mime
storage_type
} {
@author Neophytos Demetriou
@param content
holds the filename if storage_type=file
holds the text data if storage_type=text
holds the lob_id if storage_type=lob
} {
upvar $_txt txt
set txt ""
switch $storage_type {
text {
set data $content
}
file {
set data [db_blob_get get_file_data {}]
}
lob {
db_transaction {
set data [db_blob_get get_lob_data {}]
}
}
}
search_content_filter txt data $mime
}
ad_proc search_content_filter {
_txt
_data
mime
} {
@author Neophytos Demetriou
} {
upvar $_txt txt
upvar $_data data
switch -glob -- $mime {
{text/plain*} {
set txt $data
}
{text/html*} {
set txt $data
}
}
}
ad_proc search_choice_bar { items links values {default ""} } {
@author Neophytos Demetriou
} {
set count 0
set return_list [list]
foreach value $values {
if {[string compare $default $value] == 0} {
lappend return_list "<font color=\"a90a08\"><strong>[lindex $items $count]</strong></font>"
} else {
lappend return_list "<a href=\"[lindex $links $count]\"><font color=\"000000\">[lindex $items $count]</font></a>"
}
incr count
}
if {[llength $return_list] > 0} {
return "[join $return_list " "]"
} else {
return ""
}
}
<?xml version="1.0"?>
<queryset>
<fullquery name="search_indexer.search_observer_queue_entry">
<querytext>
select object_id, event_date, event
from search_observer_queue
order by event_date asc
</querytext>
</fullquery>
<fullquery name="search_content_get.get_file_data">
<querytext>
select :content as content,
'file' as storage_type
from dual
</querytext>
</fullquery>
<fullquery name="search_content_get.get_lob_data">
<querytext>
select :content as content,
'lob' as storage_type
from dual
</querytext>
</fullquery>
</queryset>
<master>
<property name="title">#search.Advanced_Search#</property>
<property name="context">"advanced search"</property>
<form method=GET action=search>
<input type=text name=q size=41 maxlength=256 value="@q@">
<input type=submit value="Search" name=t>
<br>
#search.Date_Range#
<select name=dfs>
<option value=all> #search.anytime#
<option value=m3> #search.past_3_months#
<option value=m6> #search.past_6_months#
<option value=y1> #search.past_year#
</select>
#search.nbspDisplay#
<select name=num>
<option value=10 <if @num@ eq 10>#search.selected#</if>>10 #search.results#
<option value=20 <if @num@ eq 20>#search.selected#</if>>20 #search.results#
<option value=30 <if @num@ eq 30>#search.selected#</if>>30 #search.results#
<option value=50 <if @num@ eq 50>#search.selected#</if>>50 #search.results#
<option value=100 <if @num@ eq 100>#search.selected#</if>>100 #search.results#
</select>
</form>
ad_page_contract {
@author Neophytos Demetriou
} {
{q ""}
{num 0}
}
set package_id [ad_conn package_id]
if { $num == 0 } {
set num [ad_parameter -package_id $package_id LimitDefault]
}
set title "Advanced Search"
set context "advanced search"
set context_bar [ad_context_bar $title]
ad_return_template
\ No newline at end of file
<html>
<head>
<title>How to make an object type searchable?</title>
</head>
<body bgcolor=ffffff text=000000>
<blockquote>
<h2>How to make an object type searchable?</h2>
by Neophytos Demetriou (<a href=mailto:k2pts@cytanet.com.cy>k2pts@cytanet.com.cy</a>)
<hr>
Making an object type searchable involves three steps:
<ul>
<li> Choose the object type
<li> Implement FtsContentProvider
<li> Add triggers
</ul>
<h3>Choose the object type</h3>
In most of the cases, choosing the object type is straightforward. However, if your object type
uses the content repository then you should make sure that your object type
is a subclass of the "content_revision" class. You should also make sure
all content is created using that subclass, rather than simply create
content with the "content_revision" type.
<ul>
<li>Object types that don't use the CR, can be specified using
<code>acs_object_type__create_type</code>,
but those that use the CR need to use <code>content_type__create_type</code>.
<code>content_type__create_type</code> overloads <code>acs_object_type__create_type</code>
and provides two views for inserting and viewing content data, and the CR depends on these views.
<li>Whenever you call content_item__new, call it with
'content_revision' as the item_subtype and 'your_content_type' as the content_type.
</ul>
<h3>Implement FtsContentProvider</h3>
FtsContentProvider is comprised of two abstract operations, namely
<code>datasource</code> and <code>url</code>.
The specification for these operations can be found in
<code>packages/search/sql/postgresql/search-sc-create.sql</code>.
You have to implement these operations for your object type by writing concrete functions that follow
the specification. For example, the implementation of
<code>datasource</code> for the object type <code>note</code>, looks like this:
<code><pre>
ad_proc notes__datasource {
object_id
} {
@author Neophytos Demetriou
} {
db_0or1row notes_datasource {
select n.note_id as object_id,
n.title as title,
n.body as content,
'text/plain' as mime,
'' as keywords,
'text' as storage_type
from notes n
where note_id = :object_id
} -column_array datasource
return [array get datasource]
}
</pre></code>
When you are done with the implementation of <code>FtsContentProvider</code> operations,
you should let the system know of your implementation. This is accomplished by an SQL file which
associates the implementation with a contract name. The implementation of
<code>FtsContentProvider</code> for the object type <code>note</code> looks like:
<code><pre>
select acs_sc_impl__new(
'FtsContentProvider', -- impl_contract_name
'note', -- impl_name
'notes' -- impl_owner_name
);
</pre></code>
You should adapt this association to reflect your implementation. That is,
change <code>impl_name</code> with your object type and the <code>impl_owner_name</code>
to the package key. Next, you have to create associations between the operations of
<code>FtsContentProvider</code> and your concrete functions. Here's how an association
between an operation and a concrete function looks like:
<code><pre>
select acs_sc_impl_alias__new(
'FtsContentProvider', -- impl_contract_name
'note', -- impl_name
'datasource', -- impl_operation_name
'notes__datasource', -- impl_alias
'TCL' -- impl_pl
);
</pre></code>
Again, you have to make some changes. Change the <code>impl_name</code>
from <code>note</code> to your object type and the <code>impl_alias</code>
from <code>notes__datasource</code> to the name that you gave to the
function that implements the operation <code>datasource</code>.
<h3>Add triggers</h3>
If your object type uses the content repository to store its items, then you are done. If not, an
extra step is required to inform the search_observer_queue of new content items, updates or deletions.
We do this by adding triggers on the table that stores the content items of your object type. Here's
how that part looks like for <code>note</code>.
<code><pre>
create function notes__itrg ()
returns opaque as '
begin
perform search_observer__enqueue(new.note_id,''INSERT'');
return new;
end;' language 'plpgsql';
create function notes__dtrg ()
returns opaque as '
begin
perform search_observer__enqueue(old.note_id,''DELETE'');
return old;
end;' language 'plpgsql';
create function notes__utrg ()
returns opaque as '
begin
perform search_observer__enqueue(old.note_id,''UPDATE'');
return old;
end;' language 'plpgsql';
create trigger notes__itrg after insert on notes
for each row execute procedure notes__itrg ();
create trigger notes__dtrg after delete on notes
for each row execute procedure notes__dtrg ();
create trigger notes__utrg after update on notes
for each row execute procedure notes__utrg ();
</pre></code>
<h3>Questions & Answers</h3>
<ol>
<li>
Q: If content is some binary file (like a pdf file stored in file storage, for example),
will the content still be indexable/searchable? <br><br>
A: For each mime type we require some type of handler. Once the handler is available, i.e. pdf2txt,
it is very easy to incorporate support for that mime type into the search package. Content items
with unsupported mime types will be ignored by the indexer.
<br><br>
<li>
Q: Can the search package handle lobs and files?<br><br>
A: Yes, the search package will convert everything into text
based on the content and storage_type attributes. Here is the
convention to use while writing the implementation of datasource:<br><br>
<ul>
<li>Content is a filename when storage_type='file'.
<li>Content is a lob id when storage_type='lob'.
<li>Content is text when storage_type='text'.
</ul>
</ol>
</blockquote>
</body>
</html>
\ No newline at end of file
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Search</title>
</head>
<body>
<h1>Search</h1>
<a href="../">OpenACS documentation</a>
<hr>
<ul>
<li><a href="./guidelines.html">How to make an object searchable</a></li>
</ul>
<hr>
<address><a href="mailto:vkurup@massmed.org">Vinod Kurup</a></address>
<!-- Created: Fri Sep 13 08:36:28 EDT 2002 -->
<!-- hhmts start -->
Last modified: Fri Sep 13 08:44:16 EDT 2002
<!-- hhmts end -->
</body>
</html>
<html>
<head>
<title>The Basics of Our Search</title>
</head>
<body bgcolor=ffffff text=000000>
<blockquote>
<h2>The Basics of Our Search</h2>
<ul>
<li> <a href="#basic">Basic search</a> </li>
<li> <a href="#and">Automatic "and" queries</a> </li>
<li> <a href="#stopwords"> What is a stop word?</a> </li>
<li> <a href="#context">See your search terms in context</a> </li>
<li> <a href="#stemming">Does our search use stemming?</a> </li>
<li> <a href="#case">Does capitalization matter?</a> </li>
</ul>
<a name=basic><h3>Basic Search</h3></a>
<p> To enter a query, just type in a few descriptive words
and hit the 'enter' key (or click on the Search button) for a
list of relevant pages.</p>
<p>Our search uses sophisticated text-matching techniques to find pages that
are both important and relevant to your search. For instance, when our search
analyzes a page, it assigns higher relevance to pages in which
your query terms appear near each other. </p>
<a name=and><h3>Automatic "and" Queries</h3></a>
<p> By default, our search only returns pages that include all of your search
terms. There is no need to include "and" between terms.
To restrict a search further, just include more terms. </p>
<a name=stopwords><h3>What is a stop word?</h3></a>
<p> Our search ignores common words and characters (known as stop words) as
they tend to slow down searches without improving the quality of the
results. Terms such as <em>"where"</em> and <em>"how"</em>, as well
as certain single digits and single letters, are not included in searches.</p>
<a name="context"><h3>See your search terms in context</h3> </a>
<p> Each search result contains at least one excerpt from the found
web page, which shows how your search terms are used in context on that
page. Your search terms are bolded so you can tell at a glance whether
the result is a page you want to visit. </p>
<a name="stemming"><h3>Does our search use stemming?</h3> </a>
Our search allows to find same words with different
endings. For example, it will also try to find the word "test" if
"testing" or "tests" is given in search query.
<a name=case><h3>Does capitalization matter?</h3></a>
<p> The searches are <b>not</b> case sensitive. All letters, regardless
of how you type them, will be understood as lower case. For example,
searches for "george washington", "George Washington", and "gEoRgE wAsHiNgToN"
will all return the same results.</p>
</blockquote>
</body>
</html>
<master>
<property name="title">#search.Search#</property>
<property name="context">#search.Search#</property>
<center>
<form method=GET action=search>
<small>
<a href=advanced-search>#search.Advanced_Search#</a>
<br>
<input type=text name=q size=80 maxlength=256>
<br>
<input type=submit value="#search.Search#" name=t>
<input type=submit value="#search.Feeling_Lucky#" name=t>
</small>
</form>
</center>
# Nothing to see here...
ad_return_template
\ No newline at end of file
<master>
<property name="title">@page_title@</property>
<property name="context">@context;noquote@</property>
<form method=GET action=search>
<small>
<a href=@url_advanced_search@>#search.Advanced_Search#</a>
<br>
<input type=text name=q size=31 maxlength=256 value="@query@">
<input type=submit value="#search.Search#" name=t>
<input type=submit value="#search.Feeling_Lucky#" name=t>
</small>
</form>
<if @t@ eq "Search">
<i>#search.lt_Tip_In_most_browsers_#</i><br><br>
</if>
<if @and_queries_notice_p@ eq 1>
<font color=6f6f6f>
#search.The#
[<a href=help/basics#and>#search.details#</a>]<br>
</font>
</if>
<if @nstopwords@ eq 1>
<font color=6f6f6f>
#search.lt_bstopwordsb_is_a_very#
[<a href=help/basics#stopwords>#search.details#</a>]<br>
</font>
</if>
<if @nstopwords@ gt 1>
<font color=6f6f6f>
#search.lt_The_following_words_a# <b>@stopwords@</b>.
[<a href=help/basics#stopwords>#search.details#</a>]<br>
</font>
</if>
<multiple name="searchresult">
<if @searchresult.title_summary@ nil>
<a href=@searchresult.url_one@>#search.Untitled#</a><br>
</if>
<else>
<a href=@searchresult.url_one@>@searchresult.title_summary;noquote@</a><br>
</else>
<if @searchresult.txt_summary@ nil>
</if>
<else>
@searchresult.txt_summary;noquote@<br>
</else>
<font color=green>@searchresult.url_one@</font><br><br>
</multiple>
<if @count@ eq 0>
Your search - <b>@query@</b> - did not match any documents.
<br>#search.lt_No_pages_were_found_c#<b>@query@</b>".
<br><br>#search.Suggestions#
<ul>
<li>#search.lt_Make_sure_all_words_a#
<li>#search.lt_Try_different_keyword#
<li>#search.lt_Try_more_general_keyw#
<if @nquery@ gt 2>
<li>#search.Try_fewer_keywords#
</if>
</ul>
</if>
<else>
<table width=100% bgcolor=3366cc border=0 cellpadding=3 cellspacing=0>
<tr><td>
<font color=white>
#search.Searched_for_query#
</font>
</td><td align=right>
<font color=white>
#search.Results# <b>@low@-@high@</b> #search.of_about# <b>@count@</b>#search.________Search_took# <b>@elapsed@</b> #search.seconds#
</font>
</td></tr>
</table>
<br clear=all>
</else>
<if @from_result_page@ lt @to_result_page@>
<center>
<small>#search.Result_page#</small>
<if @from_result_page@ lt @current_result_page@>
<small><a href=@url_previous@><font color=0000cc><b>#search.Previous#</b></font></a></small>
</if>
&nbsp;@choice_bar;noquote@&nbsp;
<if @current_result_page@ lt @to_result_page@>
<small><a href=@url_next@><font color=0000cc><b>#search.Next#</b></font></a></small>
</if>
</center>
</if>
<if @count@ gt 0>
<center>
<table border=0 cellpadding=3 cellspacing=0>
<tr><td nowrap>
<form method=GET action=search>
<center>
<small>
<input type=text name=q size=31 maxlength=256 value="@query@">
<input type=submit value=Search>
</small>
</center>
</form>
</td></tr>
</table>
</center>
<if @stw@ not nil>
<center>
<font size=-1>#search.lt_Try_your_query_on_stw#</font></center>
</center>
</if>
</if>
ad_page_contract {
@author Neophytos Demetriou <k2pts@cytanet.com.cy>
@creation-date September 01, 2001
@cvs-id $Id$
} {
q:notnull,trim
{t:trim ""}
{offset:integer 0}
{num:integer 0}
{dfs:trim ""}
{dts:trim ""}
} -errors {
q:notnull {[_ search.lt_You_must_specify_some].}
}
set page_title "Search Results"
set package_id [ad_conn package_id]
set package_url [ad_conn package_url]
set package_url_with_extras $package_url
set context [list]
set context_base_url $package_url
set user_id [ad_conn user_id]
set driver [ad_parameter -package_id $package_id FtsEngineDriver]
array set info [acs_sc_call FtsEngineDriver info [list] $driver]
if { [array get info] == "" } {
ReturnHeaders
ns_write "[_ search.lt_FtsEngineDriver_not_a]"
return
}
if { $num <= 0} {
set limit [ad_parameter -package_id $package_id LimitDefault]
} else {
set limit $num
}
set df ""
set dt ""
if { $dfs == "all" } { set dfs "" }
array set symbol2interval [ad_parameter -package_id $package_id Symbol2Interval]
if { $dfs != "" } { set df [db_exec_plsql get_df "select now() + '$symbol2interval($dfs)'::interval"] }
if { $dts != "" } { set dt [db_exec_plsql get_dt "select now() + '$symbol2interval($dts)'::interval"] }
set q [string tolower $q]
set urlencoded_query [ad_urlencode $q]
if { $offset < 0 } { set offset 0 }
set t0 [clock clicks -milliseconds]
array set result [acs_sc_call FtsEngineDriver search [list $q $offset $limit $user_id $df $dt] $driver]
set tend [clock clicks -milliseconds]
if { $t == "Feeling Lucky" && $result(count) > 0} {
set object_id [lindex $result(ids) 0]
set object_type [acs_object_type $object_id]
set url [acs_sc_call FtsContentProvider url [list $object_id] $object_type]
ad_returnredirect $url
ad_script_abort
}
set elapsed [format "%.02f" [expr double(abs($tend - $t0)) / 1000.0]]
if { $offset >= $result(count) } { set offset [expr ($result(count) / $limit) * $limit] }
set low [expr $offset + 1]
set high [expr $offset + $limit]
if { $high > $result(count) } { set high $result(count) }
if { $info(automatic_and_queries_p) && ([lsearch -exact $q and] > 0) } {
set and_queries_notice_p 1
} else {
set and_queries_notice_p 0
}
set url_advanced_search ""
append url_advanced_search "advanced-search?q=${urlencoded_query}"
if { $num > 0 } { append url_advanced_search "&num=${num}" }
set query $q
set nquery [llength $q]
set stopwords $result(stopwords)
set nstopwords [llength $result(stopwords)]
set count $result(count)
template::multirow create searchresult title_summary txt_summary url_one
for { set __i 0 } { $__i < [expr $high - $low +1] } { incr __i } {
set object_id [lindex $result(ids) $__i]
set object_type [acs_object_type $object_id]
array set datasource [acs_sc_call FtsContentProvider datasource [list $object_id] $object_type]
search_content_get txt $datasource(content) $datasource(mime) $datasource(storage_type)
set title_summary [acs_sc_call FtsEngineDriver summary [list $q $datasource(title)] $driver]
set txt_summary [acs_sc_call FtsEngineDriver summary [list $q $txt] $driver]
set url_one [acs_sc_call FtsContentProvider url [list $object_id] $object_type]
# Replace the "index" with ETP as this is not needed for accessing the page
if {[string equal $object_type "etp_page_revision"]} {
set url_one [string trimright $url_one "index"]
}
template::multirow append searchresult $title_summary $txt_summary $url_one
}
set from_result_page 1
set current_result_page [expr ($low / $limit) + 1]
set to_result_page [expr ceil(double($result(count)) / double($limit))]
set url_previous ""
set url_next ""
append url_previous "search?q=${urlencoded_query}"
append url_next "search?q=${urlencoded_query}"
if { [expr $current_result_page - 1] > $from_result_page } {
append url_previous "&offset=[expr ($current_result_page - 2) * $limit]"
}
if { $current_result_page < $to_result_page } {
append url_next "&offset=[expr $current_result_page * $limit]"
}
if { $num > 0 } {
append url_previous "&num=$num"
append url_next "&num=$num"
}
set items [list]
set links [list]
set values [list]
for { set __i $from_result_page } { $__i <= $to_result_page} { incr __i } {
set link ""
append link "search?q=${urlencoded_query}"
if { $__i > 1 } { append link "&offset=[expr ($__i - 1) * $limit]" }
if { $num > 0 } { append link "&num=$num" }
lappend items $__i
lappend links $link
lappend values $__i
}
set search_the_web [ad_parameter -package_id $package_id SearchTheWeb]
if [llength $search_the_web] {
set stw ""
foreach {url site} $search_the_web {
append stw "<a href=[format $url $urlencoded_query]>$site</a> "
}
}
set choice_bar [search_choice_bar $items $links $values $current_result_page]
<master>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment