-- -- packages/site-wide-search/sql/search-tables.sql -- -- @author khy@arsdigita.com -- @creation-date 2000-11-24 -- @cvs-id $Id: search-tables.sql,v 1.1 2002/07/09 17:35:12 rmello Exp $ -- create table sws_search_contents ( content_id integer constraint sws_search_content_id_pk primary key constraint sws_search_content_id_fk references acs_contents(content_id) on delete cascade, -- application that owns this content application_id integer constraint sws_search_appl_id_fk references apm_applications on delete cascade, node_id integer constraint sws_search_node_id_fk references site_nodes on delete cascade, permission_req varchar2(100), data_store char(1) ); comment on table sws_search_contents is 'Store rows from acs_contents that should be indexed. Table is populated/modified by three triggers, on acs_contents, based on the searchable_p state change. Insert when: 1. insert into acs_contents and searchble_p is true 2. update searchable_p from false to true. Update when: 1. update other columns besides searchable_p, when searchable_p stays true Delete as follows: 1. Delete acs_contents row 2. update searchable_p from true to false. Note: the application_id, node_id, and permission_req are populated when on insert and update conditions. '; comment on column sws_search_contents.application_id is 'The application_id that is handling the object.'; comment on column sws_search_contents.node_id is 'The site node where the object display page resides'; comment on column sws_search_contents.permission_req is 'The permission needed for search to display content in the search result. If null, then no permission is checked.'; -- Package contains: -- 1. Wrapper functions to handle the 'sws_display' and 'sws_indexing' interface method calls. -- a. 'sws_display' interface defines methods to retrieving display and site node from each object type -- b. 'sws_indexing' method for object types to specify their own content to index instead -- of the content in acs_contents table. -- 2. Rebuild index function. -- 3. Function to walk up context tree and returns the first object of a specified type. create or replace package sws_service as -- the first 6 are wrapper functions for the 'sws_display' interface methods function get_content_node_id ( content_id in sws_search_contents.content_id%TYPE ) return sws_search_contents.node_id%TYPE; function get_content_application_id ( content_id in sws_search_contents.content_id%TYPE ) return sws_search_contents.application_id%TYPE; function get_content_permission ( content_id in sws_search_contents.content_id%TYPE ) return sws_search_contents.permission_req%TYPE; function get_content_url ( content_id in sws_search_contents.content_id%TYPE ) return varchar2; function get_content_title ( content_id in sws_search_contents.content_id%TYPE ) return varchar2; function get_content_summary ( content_id in sws_search_contents.content_id%TYPE ) return varchar2; procedure content_sws_index_proc ( content_id in sws_search_contents.content_id%TYPE, bdata in out nocopy blob ); -- wrapper function for 'sws_indexing' interface method procedure sws_index_proc ( rid in rowid, bdata in out nocopy blob ); -- procedure updates the application_id, node_id, -- and permission for an object procedure update_content_info ( content_id in sws_search_contents.content_id%TYPE ); -- procedure updates the application_id, node_id, -- and permission for all object types procedure update_content_obj_type_info ( object_type in acs_object_types.object_type%TYPE ); -- rebuilds index for the content, since the last time index was ran. procedure rebuild_index; -- reindexes all content procedure rebuild_all_index; -- given an object id, function returns its context object whose type is object type function first_obj_type_in_context_tree ( object_id in acs_objects.object_id%TYPE, object_type in acs_object_types.object_type%TYPE ) return acs_objects.object_id%TYPE; end sws_service; / create or replace package body sws_service as -- the first 6 are wrapper functions for the 'sws_display' interface methods function get_content_node_id ( content_id in sws_search_contents.content_id%TYPE ) return sws_search_contents.node_id%TYPE is v_node_id sws_search_contents.node_id%TYPE; v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_result integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_display', programming_language => 'pl/sql', object_type => v_object_type ); -- construct dynamic sql to invoke the sws_site_node_id method within the object type package -- object types are searchable only if they implement the interface v_code := 'select '||v_object_type||'.sws_site_node_id(:content_id) from dual'; execute immediate v_code into v_node_id using content_id; return v_node_id; exception when others then return null; end get_content_node_id; function get_content_application_id ( content_id in sws_search_contents.content_id%TYPE ) return sws_search_contents.application_id%TYPE is v_cursor_id integer; v_application_id sws_search_contents.application_id%TYPE; v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_result integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_display', programming_language => 'pl/sql', object_type => v_object_type ); v_code := 'select '||v_object_type||'.sws_application_id(:content_id) from dual'; -- construct dynamic sql to invoke the sws_application_id method within the object type package -- object types are searchable only if they implement the interface execute immediate v_code into v_application_id using content_id; return v_application_id; exception when others then return null; end get_content_application_id; function get_content_permission ( content_id in sws_search_contents.content_id%TYPE ) return sws_search_contents.permission_req%TYPE is v_cursor_id integer; v_permission sws_search_contents.permission_req%TYPE; v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_result integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_display', programming_language => 'pl/sql', object_type => v_object_type ); v_code := 'select '||v_object_type||'.sws_req_permission(:content_id) from dual'; -- construct dynamic sql to invoke the sws_req_permission method within the object type package -- object types are searchable only if they implement the interface execute immediate v_code into v_permission using content_id; return v_permission; exception when others then return null; end get_content_permission; function get_content_url ( content_id in sws_search_contents.content_id%TYPE ) return varchar2 is v_cursor_id integer; v_link varchar2(1000); v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_result integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_display', programming_language => 'pl/sql', object_type => v_object_type ); -- construct dynamic sql to invoke the sws_url method within the object type package -- object types are searchable only if they implement the interface v_code := 'select '||v_object_type||'.sws_url(:content_id) from dual'; execute immediate v_code into v_link using content_id; return v_link; exception when others then return null; end get_content_url; function get_content_title ( content_id in sws_search_contents.content_id%TYPE ) return varchar2 is v_cursor_id integer; v_title varchar2(1000); v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_result integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_display', programming_language => 'pl/sql', object_type => v_object_type ); v_code := 'select '||v_object_type||'.sws_title(:content_id) from dual'; -- construct dynamic sql to invoke the sws_title method within the object type package -- object types are searchable only if they implement the interface execute immediate v_code into v_title using content_id; return v_title; exception when others then return null; end get_content_title; function get_content_summary ( content_id in sws_search_contents.content_id%TYPE ) return varchar2 is v_cursor_id integer; v_summary varchar2(4000); v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_result integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_display', programming_language => 'pl/sql', object_type => v_object_type ); v_code := 'select '||v_object_type||'.sws_summary(:content_id) from dual'; -- construct dynamic sql to invoke the sws_content_summary method within the object type package -- object types are searchable only if they implement the interface execute immediate v_code into v_summary using content_id; return v_summary; exception when others then return null; end get_content_summary; -- wrapper function for 'sws_indexing' interface method procedure content_sws_index_proc ( content_id in sws_search_contents.content_id%TYPE, bdata in out nocopy blob ) is v_cursor_id integer; v_code varchar2(4000); v_object_type acs_object_types.object_type%TYPE; v_implement_p char(1); v_result integer; v_bdata blob; v_size integer; begin v_object_type := acs_object_util.get_object_type ( object_id => content_id ); v_object_type := acs_interface.obj_provide_implement ( interface_name => 'sws_indexing', programming_language => 'pl/sql', object_type => v_object_type ); v_code := 'begin '||v_object_type||'.sws_index_proc( :content_id, :bdata ); end;'; execute immediate v_code using content_id, in out bdata; end content_sws_index_proc; -- the procedure that is called by teh procedure sws_index_proc( rid in rowid, bdata in out nocopy blob ) is v_imp_sws_display_p char(1); v_content_id sws_search_contents.content_id%TYPE; v_bdata blob; v_content_length integer; begin select content_id into v_content_id from sws_search_contents where rowid = rid; v_imp_sws_display_p := acs_interface.object_id_implement_p ( interface_name => 'sws_indexing' , programming_language => 'pl/sql' , object_id => v_content_id ); -- if an object is an instance of an object type that supports the indexing interface, -- call the object interface procedure if v_imp_sws_display_p = 't' then dbms_lob.trim(bdata,0); sws_service.content_sws_index_proc ( v_content_id, bdata ); else -- else just get the content directly from acs_contents dbms_lob.trim(bdata,0); select content, dbms_lob.getlength(content) into v_bdata, v_content_length from acs_contents where content_id = v_content_id; if v_content_length > 0 then dbms_lob.copy ( dest_lob => bdata , src_lob => v_bdata , amount => v_content_length ); end if; end if; end sws_index_proc; -- function to update the application_id, node_id, -- and permission for an object procedure update_content_info ( content_id in sws_search_contents.content_id%TYPE ) is v_imp_interface_p char(1); v_node_id sws_search_contents.node_id%TYPE; v_application_id sws_search_contents.application_id%TYPE; v_permission sws_search_contents.permission_req%TYPE; v_code varchar2(4000); v_cursor_id integer; begin -- check that object id is an object type that implements that interface v_imp_interface_p := acs_interface.object_id_implement_p ( interface_name => 'sws_display', programming_language => 'pl/sql', object_id => content_id ); if v_imp_interface_p = 'f' then return; end if; -- call the wrapper functions to populate the table -- node_id, application_id, and permissions v_node_id := sws_service.get_content_node_id ( content_id => content_id ); v_application_id := sws_service.get_content_application_id ( content_id => content_id ); v_permission := sws_service.get_content_permission( content_id => content_id ); -- update the content display information update sws_search_contents set node_id = v_node_id , application_id = v_application_id , permission_req = v_permission , data_store = 'a' where content_id = sws_service.update_content_info.content_id; end update_content_info; procedure update_content_obj_type_info ( object_type in acs_object_types.object_type%TYPE ) is begin update sws_search_contents set data_store = 'a' where acs_object_util.get_object_type(content_id) = object_type; end update_content_obj_type_info; -- procedure that rebuilds the site wide search index procedure rebuild_index is v_code varchar2(4000); v_cursor_id integer; v_result integer; begin -- The rows that were updated, we compute the application, permission, and subsite info for content_row in ( select content_id from sws_search_contents ssc, ctxsys.ctx_user_pending cup where ssc.rowid = cup.pnd_rowid) loop sws_service.update_content_info(content_row.content_id); end loop; -- construct dynamic sql to build index v_cursor_id := dbms_sql.open_cursor; -- DRB: use the "online" variant if you have Oracle Enterprise Edition -- v_code := 'alter index sws_srch_cts_ds_iidx rebuild online parameters (''sync'')'; v_code := 'alter index sws_srch_cts_ds_iidx rebuild parameters (''sync'')'; dbms_sql.parse (v_cursor_id, v_code, dbms_sql.native); v_result := dbms_sql.execute (v_cursor_id); dbms_sql.close_cursor(v_cursor_id); end rebuild_index; procedure rebuild_all_index is begin -- mark all data for reindexing -- by updating the data_store column to 'a' update sws_search_contents set data_store = 'a'; sws_service.rebuild_index; end; -- given an object id, function returns its context object whose type is object type function first_obj_type_in_context_tree ( object_id in acs_objects.object_id%TYPE, object_type in acs_object_types.object_type%TYPE ) return acs_objects.object_id%TYPE is v_object_id integer; v_object_type_p char(1); cursor v_cursor is select object_id from acs_objects start with object_id = sws_service.first_obj_type_in_context_tree.object_id connect by prior context_id = object_id; v_cursor_row v_cursor%ROWTYPE; begin open v_cursor; fetch v_cursor into v_cursor_row; -- traverse up the context hierarchy and find first object whose -- type is the desired object type. while v_cursor%found loop v_object_type_p := acs_object_util.object_type_p ( object_id => v_cursor_row.object_id, object_type => sws_service.first_obj_type_in_context_tree.object_type ); if v_object_type_p = 't' then return v_cursor_row.object_id; end if; fetch v_cursor into v_cursor_row; end loop; return null; end first_obj_type_in_context_tree; end sws_service; / -- The 2 triggers that enforce the insert and update -- delete conditions on sws_search_contents uses delete on cascade -- if new row is added to acs_contents and searchable_p is true, -- then add row to sws_search_contents create or replace trigger acs_contents_sws_itr after insert on acs_contents for each row declare v_valid_type_p char(1); begin if :new.searchable_p = 't' then insert into sws_search_contents ( content_id, data_store ) values ( :new.content_id, 'a' ); end if; end; / -- if an update was done: -- change t --> t = update data_store column to 'a', -- change t --> f = delete row from sws_search_contents -- change f --> t = insert row into sws_search_contents create or replace trigger acs_contents_sws_utr after update on acs_contents for each row declare begin -- if the data is updated, then re-index the content if :new.searchable_p = :old.searchable_p and :old.searchable_p = 't' then update sws_search_contents set data_store = 'a' where content_id = :new.content_id; elsif :new.searchable_p = 't' then -- If previously the content was not ready for searching -- now include content for searching. insert into sws_search_contents ( content_id, data_store ) values ( :new.content_id, 'a' ); else delete from sws_search_contents where content_id = :new.content_id; end if; end; / -- Declaractions for 'sws_display' and 'sws_indexing' interfaces. declare v_interface_id integer; v_method_id integer; v_param_id integer; begin v_interface_id := acs_interface.new ( interface_name => 'sws_display', programming_language => 'pl/sql', enabled_p => 't', description => 'Interface for object types to include in site wide search' ); -- Get the application id for object -- v_method_id := acs_interface.add_method ( interface_name => 'sws_display', programming_language => 'pl/sql', method_name => 'sws_application_id', method_type => 'function', return_type => 'apm_packages.package_id%TYPE', method_desc => 'returns the application id that owns/maintains object' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the acs object id' ); v_method_id := acs_interface.add_method ( interface_name => 'sws_display', programming_language => 'pl/sql', method_name => 'sws_site_node_id', method_type => 'function', return_type => 'site_nodes.node_id%TYPE', method_desc => 'returns the node id' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the acs object id' ); v_method_id := acs_interface.add_method ( interface_name => 'sws_display', programming_language => 'pl/sql', method_name => 'sws_req_permission', method_type => 'function', return_type => 'acs_permissions.privilege%TYPE', method_desc => 'returns the permission required to display' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the acs object id' ); -- Get link to the object -- v_method_id := acs_interface.add_method ( interface_name => 'sws_display', programming_language => 'pl/sql', method_name => 'sws_url', method_type => 'function', return_type => 'varchar2(4000)', method_desc => 'returns the link to the object id' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the acs object id' ); -- Get title for the object -- v_method_id := acs_interface.add_method ( interface_name => 'sws_display', programming_language => 'pl/sql', method_name => 'sws_title', method_type => 'function', return_type => 'varchar2(2000)', method_desc => 'returns the title for the object' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the acs object id' ); -- Get summary for the object -- v_method_id := acs_interface.add_method ( interface_name => 'sws_display', programming_language => 'pl/sql', method_name => 'sws_summary', method_type => 'function', return_type => 'varchar2(4000)', method_desc => 'returns the summary for the object' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the acs object id' ); -- Setup the interface for the site wide index. Object types may define -- their own procedures for building data to be indexed v_interface_id := acs_interface.new ( interface_name => 'sws_indexing', programming_language => 'pl/sql', enabled_p => 't', description => 'Procedures to get object content to get indexed' ); -- add procedure to, not used currently v_method_id := acs_interface.add_method ( interface_name => 'sws_indexing', programming_language => 'pl/sql', method_name => 'sws_index_proc', method_type => 'procedure', return_type => '', method_desc => 'procedure that builds the data to be indexed' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'object_id', param_type => 'acs_objects.object_id%TYPE', param_desc => 'the object to be indexed' ); acs_interface.add_param_to_method ( method_id => v_method_id, param_name => 'bdata', param_type => 'blob', param_desc => 'the data to be indexed', param_ref_p => 't', param_spec => 'nocopy' ); end; /