Index: openacs-4/packages/search/tcl/search-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/search/tcl/search-procs.tcl,v
diff -u -r1.59 -r1.60
--- openacs-4/packages/search/tcl/search-procs.tcl	8 Oct 2024 15:04:36 -0000	1.59
+++ openacs-4/packages/search/tcl/search-procs.tcl	21 Nov 2024 12:47:25 -0000	1.60
@@ -7,6 +7,87 @@
 
 namespace eval search {}
 
+ad_proc -public search::searchable_type_p  {
+    -object_type:required
+} {
+    Is this object type searchable?
+
+    @return boolean
+} {
+    return [expr {[callback::impl_exists -callback search::datasource -impl $object_type]
+                  || [acs_sc_binding_exists_p FtsContentProvider $object_type]}]
+}
+
+ad_proc -public search::object_index {
+    -object_id:required
+    {-event "UPDATE"}
+} {
+    Indexes an object by invoking the proper datasource and callbacks.
+
+    @return datasource as dict
+} {
+    set driver [search::driver_name]
+    if {$driver eq ""} {
+        return
+    }
+
+    set object_type [acs_object_type $object_id]
+    if {![search::searchable_type_p -object_type $object_type]} {
+        return
+    }
+
+    array set d {
+        mime {}
+        storage_type {}
+        keywords {}
+        package_id {}
+        relevant_date {}
+    }
+
+    if {[callback::impl_exists -callback search::datasource -impl $object_type]} {
+        array set d [lindex [callback \
+                                 -impl $object_type \
+                                 search::datasource \
+                                 -object_id $object_id] 0]
+    } else {
+        array set d [acs_sc::invoke \
+                         -contract FtsContentProvider \
+                         -operation datasource \
+                         -call_args [list $object_id] \
+                         -impl $object_type]
+    }
+
+    search::content_get txt \
+        $d(content) \
+        $d(mime) \
+        $d(storage_type) \
+        $object_id
+
+    if {[callback::impl_exists -callback search::index -impl $driver]} {
+        callback -impl $driver search::index \
+            -object_id $object_id \
+            -content $txt \
+            -title $d(title) \
+            -keywords $d(keywords) \
+            -package_id $d(package_id) \
+            -community_id $d(community_id) \
+            -relevant_date $d(relevant_date) \
+            -datasource d
+    } else {
+        set r [acs_sc::invoke \
+                   -contract FtsEngineDriver \
+                   -operation [expr {$event eq "UPDATE" ? "update_index" : "index"}] \
+                   -call_args [list \
+                                   $d(object_id) \
+                                   $txt \
+                                   $d(title) \
+                                   $d(keywords)] \
+                   -impl $driver]
+    }
+
+    return [array get d]
+}
+
 ad_proc -public search::queue {
     -object_id
     -event
@@ -103,19 +184,14 @@
     @author Jeff Davis (davis@xarg.net)
 } {
 
-    set driver [parameter::get \
-                    -package_id [apm_package_id_from_key search] \
-                    -parameter FtsEngineDriver]
-
-    if { $driver eq ""
-         || (![callback::impl_exists -callback search::index -impl $driver] \
-                 && ! [acs_sc_binding_exists_p FtsEngineDriver $driver])
-     } {
+    set driver [search::driver_name]
+    if { $driver eq "" } {
+        #
         # Nothing to do if no driver
-        ns_log Debug "search::indexer: driver=$driver binding exists? " \
-            "[acs_sc_binding_exists_p FtsEngineDriver $driver]"
+        #
         return
     }
+
     # JCD: pull out the rows all at once so we release the handle
     foreach row [db_list_of_lists search_observer_queue_entry {}] {
 
@@ -131,86 +207,37 @@
         }
 
         lassign $row object_id event_date event
-        unset -nocomplain datasource
         switch -- $event {
             UPDATE -
             INSERT {
                 # Don't bother reindexing if we've already inserted/updated this object in this run
+                set object_type [acs_object_type $object_id]
+
                 if {![info exists seen($object_id)]} {
-                    set object_type [acs_object_type $object_id]
-                    ns_log debug "\n-----DB-----\n SEARCH INDEX object type = '${object_type}' \n------------\n "
-                    if {[callback::impl_exists -callback search::datasource -impl $object_type]
-                        || [acs_sc_binding_exists_p FtsContentProvider $object_type]} {
+                    try {
+                        unset -nocomplain datasource
+                        array set datasource [search::object_index \
+                                                  -object_id $object_id]
+                    } on error {errMsg} {
+                        ns_log Error "search::indexer: error getting datasource for " \
+                            "$object_id $object_type: $errMsg\n[ad_print_stack_trace]"
+                    }
 
-                        array set datasource {mime {} storage_type {} keywords {}}
-                        if {[catch {
-                            # check if a callback exists, if not fall
-                            # back to service contract
-                            if {[callback::impl_exists -callback search::datasource -impl $object_type]} {
-                                #ns_log notice "\n-----DB-----\n SEARCH INDEX callback datasource exists for object_type '${object_type}'\n------------\n "
-                                array set datasource [lindex [callback \
-                                                                  -impl $object_type \
-                                                                  search::datasource \
-                                                                  -object_id $object_id] 0]
-                            } else {
-                                #ns_log notice "invoke contract [list acs_sc::invoke -contract FtsContentProvider -operation datasource -call_args [list $object_id] -impl $object_type]"
-                                array set datasource  [acs_sc::invoke \
-                                                           -contract FtsContentProvider \
-                                                           -operation datasource \
-                                                           -call_args [list $object_id] \
-                                                           -impl $object_type]
-                            }
+                    if {[array size datasource] > 0} {
+                        # call the action so other people who do indexey things have a hook
+                        callback -catch search::action \
+                            -action $event \
+                            -object_id $object_id \
+                            -datasource datasource \
+                            -object_type $object_type
 
-                            search::content_get txt $datasource(content) $datasource(mime) \
-                                $datasource(storage_type) $object_id
+                        # Remember seeing this object so we can avoid reindexing it later
+                        set seen($object_id) 1
 
-                            if {[callback::impl_exists -callback search::index -impl $driver]} {
-                                if {![info exists datasource(package_id)]} {
-                                    set datasource(package_id) ""
-                                }
-
-                                if {![info exists datasource(relevant_date)]} {
-                                    set datasource(relevant_date) ""
-                                }
-                                #ns_log notice "callback invoke search::index"
-                                callback -impl $driver search::index \
-                                    -object_id $object_id \
-                                    -content $txt \
-                                    -title $datasource(title) \
-                                    -keywords $datasource(keywords) \
-                                    -package_id $datasource(package_id) \
-                                    -community_id $datasource(community_id) \
-                                    -relevant_date $datasource(relevant_date) \
-                                    -datasource datasource
-                            } else {
-                                #ns_log notice "acs_sc::invoke FtsEngineDriver"
-                                set r [acs_sc::invoke \
-                                           -contract FtsEngineDriver \
-                                           -operation [expr {$event eq "UPDATE" ? "update_index" : "index"}] \
-                                           -call_args [list $datasource(object_id) \
-                                                           $txt $datasource(title) \
-                                                           $datasource(keywords)] \
-                                           -impl $driver]
-                            }
-                        } errMsg]} {
-                            ns_log Error "search::indexer: error getting datasource for " \
-                                "$object_id $object_type: $errMsg\n[ad_print_stack_trace]"
-                        } else {
-                            # call the action so other people who do indexey things have a hook
-                            callback -catch search::action \
-                                -action $event \
-                                -object_id $object_id \
-                                -datasource datasource \
-                                -object_type $object_type
-
-                            # Remember seeing this object so we can avoid reindexing it later
-                            set seen($object_id) 1
-
-                            search::dequeue \
-                                -object_id $object_id \
-                                -event_date $event_date \
-                                -event $event
-                        }
+                        search::dequeue \
+                            -object_id $object_id \
+                            -event_date $event_date \
+                            -event $event
                     }
                 }
             }
@@ -425,7 +452,19 @@
 } {
     Return the name of the current search driver.
 } {
-    return [parameter::get -package_id [apm_package_id_from_key search] -parameter FtsEngineDriver]
+    set driver [parameter::get \
+                    -package_id [apm_package_id_from_key search] \
+                    -parameter FtsEngineDriver]
+
+    if { $driver eq ""
+         || (![callback::impl_exists -callback search::index -impl $driver] \
+                 && ! [acs_sc_binding_exists_p FtsEngineDriver $driver])
+     } {
+        ns_log Debug "search::indexer: driver=$driver not properly configured."
+        return
+    }
+
+    return $driver
 }
 
 # dotlrn specific procs
Index: openacs-4/packages/search/tcl/test/search-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/search/tcl/test/search-procs.tcl,v
diff -u -r1.2 -r1.3
--- openacs-4/packages/search/tcl/test/search-procs.tcl	11 Sep 2024 06:15:53 -0000	1.2
+++ openacs-4/packages/search/tcl/test/search-procs.tcl	21 Nov 2024 12:47:25 -0000	1.3
@@ -219,3 +219,45 @@
                  -package_id [apm_package_id_from_key search] \
                  -parameter FtsEngineDriver]
     }
+
+aa_register_case \
+    -cats {api smoke} \
+    -procs {
+        search::driver_name
+        search::dotlrn::get_community_id
+    } \
+    object_utilities {
+        Test object-related utilities.
+    } {
+        set searchable_types [list]
+
+        db_foreach get_object_types {
+            select object_type from acs_object_types
+        } {
+            set expected [expr {[callback::impl_exists -callback search::datasource -impl $object_type]
+                                || [acs_sc_binding_exists_p FtsContentProvider $object_type]}]
+            set computed [search::searchable_type_p -object_type $object_type]
+            aa_equals "Is '$object_type' searchable?" $computed $expected
+
+            if {$computed} {
+                lappend searchable_types $object_type
+            }
+        }
+
+        if {[llength $searchable_types] > 0} {
+            foreach sample [db_list_of_lists get_searchable_objects [subst {
+                select min(object_id), object_type
+                from acs_objects
+                where object_type in ([ns_dbquotelist $searchable_types])
+                group by object_type
+                order by object_type asc
+            }]] {
+                lassign $sample object_id object_type
+                set datasource [search::object_index -object_id $object_id]
+
+                aa_true \
+                    "Object '$object_id' of type '$object_type' will return a non null datasource" \
+                    [llength $datasource]
+            }
+        }
+    }