I've been testing a 'search.inc' plugin that would allow users to save node searches into their MySite pages. This would allow a user to save a search for all posts containing specific keywords. The most recent matches would appear on the user's MySite page.

------
One (major) issue:

The search module uses temporary tables to store search data, on the assumption that only one search per page will be run. By design, temporary tables are dropped after the sequence exits (i.e. after a page in Drupal loads).

In the case of MySite, a user may wish to display the results of multiple searches on one page. When that occurs, this error appears:

user warning: Table 'temp_search_sids' already exists query: do_search CREATE TEMPORARY TABLE temp_search_sids SELECT i.type, i.sid, SUM(i.score * t.count) AS relevance, COUNT(*) AS matches FROM search_index i INNER JOIN search_total t ON i.word = t.word INNER JOIN node n ON n.nid = i.sid INNER JOIN users u ON n.uid = u.uid WHERE n.status = 1 AND (i.word = 'this') AND i.type = 'node' GROUP BY i.type, i.sid HAVING COUNT(*) >= 1 in example.com/includes/database.mysql.inc on line 167.

In a nutshell, this error says "The database already created this temporary table, so I can't create it again."

Now, it is possible to DROP temporary tables explictly, but this approach fails if the table has not been created. The relevant error is:

user warning: Unknown table 'temp_search_results' query: mysite_type_search_data DROP table temp_search_results in /example.com/drupal5/includes/database.mysql.inc on line 167.

If using PostGRES, we can use the db_table_exists() function from pgsql.inc before executing the drop statement. There does not appear to be an equivalent Drupal function for MySQL.

Is there a good solution to this problem?

The current code is attached. Look especially at the function mysite_type_search_data(), lines 83-123.

------------------
One minor issue:

Using node_search instead of a unique search function (using hook_search) will return the matches as defined by he Content Ranking on http://example.com/?q=admin/settings/search, not the most recent posts matching the keywords.

The solution to this seems to be writing a custom search function instead of relying on node_search.

Input welcome.

------------------
Relevant APIs:

http://api.drupal.org/api/HEAD/function/db_query_temporary
http://api.drupal.org/api/HEAD/function/db_table_exists
http://api.drupal.org/api/HEAD/function/do_search
http://api.drupal.org/api/HEAD/function/node_search

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

agentrickard’s picture

Note that db_table_exists() will not work in MySQL on temporary tables.

http://dev.mysql.com/doc/refman/5.1/en/temporary-table-problems.html

The SHOW TABLES statement does not list TEMPORARY tables.
agentrickard’s picture

agentrickard’s picture

FileSize
7.59 KB

The attached seems to fix this problem.

One additional issue was that searches that matched no results will not create the temp_search_results table, so we can't blanket delete the table. We also can't check to see if it exists (see above).

We can, however, force behavior that tells us if those tables exists.

This solution works by incrementing a global variable to count successful searches.

global $mysite_search_loops

When a search returns results, we increment the var:

    // since we are searching nodes, better to use node_search instead of do_search
    $result = node_search('search', $keywords);
    // if matches, then increment the counter so we can wipe the tables
    if (count($result) > 0) {
      $mysite_search_loops++;
    }

Then, before we begin the next search, we run a wipe check for the two known temporary tables.

    // search.module creates temporary tables, we must explicitly DROP them 
    // to perform more than one search on a single page view.
    if ($mysite_search_loops > 0) {
      if (($count = db_result(db_query('SELECT COUNT(*) FROM temp_search_results'))) > 0) {
        db_query_temporary("DROP table temp_search_results");    
      }
      if (($count = db_result(db_query('SELECT COUNT(*) FROM temp_search_sids'))) > 0) {
        db_query_temporary("DROP table temp_search_sids");    
      }
    }

Apparently, the DROP works with temporary tables. In my test install (MySQL 4.1.x, I get no errors).

Anyone able to test on PGSQL?

agentrickard’s picture

FileSize
7.4 KB

Only the first IF statement is necessary here.

if ($mysite_search_loops > 0) {
if (($count = db_result(db_query('SELECT COUNT(*) FROM temp_search_results'))) > 0) {
db_query_temporary("DROP table temp_search_results");
}
if (($count = db_result(db_query('SELECT COUNT(*) FROM temp_search_sids'))) > 0) {
db_query_temporary("DROP table temp_search_sids");
}
}

By default, the $mysite_search_loops var is only > 1 if the other two conditions are TRUE.

Attached is the fix.

agentrickard’s picture

Category: support » feature
agentrickard’s picture

Status: Active » Postponed
mdekkers’s picture

very cool. We have developed something similar, but it uses the faceted search module. We allow a user to save a faceted search, and add that search as a mysite Item. if you are interested I am happy to upload the code. However, it also depends on the faceted search author to be interested in using the save functionality.

agentrickard’s picture

I'd love to see it. Not sure I understand the last comment, though.

You could, in theory, use MySite to save the faceted search if the other module doesn't support saving searches. Take a look at the 'refine.inc' file in the contrib folder of the download.

Using the {mysite_content} table and the function mysite_get_myid(), you can store the user's search inside the MySite module.

mdekkers’s picture

we seperated the functionality - we allow faceted search to save searches, so we can reuse those saved searches throughout the site. we then added the ability to display those saved searches as a mysite Item. not sure if that makes more sense. If you like, I can give you access to the site, so you can have a look for yourself.

agentrickard’s picture

Sure, I'd love to see it in action.

I was merely saying that if the faceted search author doesn't want to save searches, you have the option of using internal MySite functions to do so. In fact, that's what search.inc does.

mdekkers’s picture

been really busy (going live with a new site does that to you :) ) but we will get back to this issue soon, and will provide some patches and stuff

agentrickard’s picture

Well, Drupal 6 has fixed this issue (temporary tables), so after the port to D6, we can probably revive this feature.

agentrickard’s picture

Status: Postponed » Closed (won't fix)