A client has requested certain keywords have synonyms when searching. Unfortunately, the search does not seem to have a way to specify synonyms to specific keywords.

Currently, if one uses a term as a keyword, all nodes in that term will come up in the result. Yet, it won't associate term synonyms with the nodes when searching.

For example, say we sell products, and have a category "Copy Machines" with a specific product "HP Photocopier." The issue we're having is that if someone searches "xerox" they will get no results because we offer no xerox brand copy machines.

Since we can anticipate such branding confusion, we would like to have some way of associating certain keywords with synonyms.

I attempted to associate category synonyms with nodes utilizing this script:

<?php

$nodeTerms = taxonomy_node_get_terms(arg(1));

foreach($nodeTerms as $nodeTerm) {
	$parents = taxonomy_get_parents_all($nodeTerm->tid);
	foreach($parents as $parent) {
		print '<a href="'. drupal_get_path_alias('taxonomy/term/'. $parent->tid) .'" title="'. $parent->name .'">'. $parent->name .'</a> ';
		$parentSynonyms = taxonomy_get_synonyms($parent->tid);
		foreach($parentSynonyms as $parentSynonym) {
			print '<a href="'. drupal_get_path_alias('taxonomy/term/'. $parent->tid) .'" title="'. $parentSynonym .'">'. $parentSynonym .'</a> ';
		}
	}
}

?>

It successfully will list all categories, parent categories and synonyms related to the node, but the Drupal search is unable to index the results of the script. When the script is placed in the node's template, I imagine Drupal's search can't see it at all... and when placed within the node's content fields, I imagine Drupal's search doesn't parse the result of the script before indexing.

We don't want to have to list keywords for each individual node. We will we if we have to, but it's inefficient if all "copy machines" will have the keyword "xerox"... it would be better to have it apply automatically.

Alternatively to the category association, it would be nice if there was an administrative ability to custom associate keywords. In this fashion, after setting synonyms, when a user searches for "xerox", Drupal search would automatically include "photocopier" and "copy machine" in it's results.

I'm certainly not afraid to hack any modules if need be to acheive some sort of result. One path I tried going down was within the search results itself... if I search for a term, I notice in the search's teaser results that the node's term is listed as "(term)" at the seemingly end of the content. If there was any way I could modify that to include parent terms and synonyms that would solve my problem! But, I can't seem to find how that is done.

Any tips or hints, or even suggestions of where to look would be most helpful.

Comments

sin’s picture

Look at http://api.drupal.org/api/4.7/group/search.
So the idea is to manually add synonyms to index during indexing process rather then modifying search results.
You can put synonyms into index in hook_update_index or hack search_index to include synonyms in keyword weighting and indexing.
Just a suggestion. I never tried both myself. Please report back if you have any advancements on this.

sin’s picture

Why the results of your script are not indexed?

I examined node module indexing hook in Drupal CVS:

/**
 * Implementation of hook_update_index().
 */
function node_update_index() {
  global $last_change, $last_nid;

  register_shutdown_function('node_update_shutdown');

  $last = variable_get('node_cron_last', 0);
  $last_nid = variable_get('node_cron_last_nid', 0);
  $limit = (int)variable_get('search_cron_limit', 100);

  // Store the maximum possible comments per thread (used for ranking by reply count)
  variable_set('node_cron_comments_scale', 1.0 / max(1, db_result(db_query('SELECT MAX(comment_count) FROM {node_comment_statistics}'))));
  variable_set('node_cron_views_scale', 1.0 / max(1, db_result(db_query('SELECT MAX(totalcount) FROM {node_counter}'))));

  $result = db_query_range('SELECT GREATEST(c.last_comment_timestamp, n.changed) as last_change, n.nid FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND ((GREATEST(n.changed, c.last_comment_timestamp) = %d AND n.nid > %d) OR (n.changed > %d OR c.last_comment_timestamp > %d)) ORDER BY GREATEST(n.changed, c.last_comment_timestamp) ASC, n.nid ASC', $last, $last_nid, $last, $last, $last, 0, $limit);

  while ($node = db_fetch_object($result)) {
    $last_change = $node->last_change;
    $last_nid = $node->nid;
    $node = node_load($node->nid);

    // Build the node body.
    $node = node_build_content($node, FALSE, FALSE);
    $node->body = drupal_render($node->content);

    $text = '<h1>'. check_plain($node->title) .'</h1>'. $node->body;

    // Fetch extra data normally not visible
    $extra = node_invoke_nodeapi($node, 'update index');
    foreach ($extra as $t) {
      $text .= $t;
    }

    // Update index
    search_index($node->nid, 'node', $text);
  }
}

First you can include synonyms in node_load.

The second way, I see node_invoke_nodeapi queries additional 'not visible' data before indexing node content. So add synonyms fetched by your script in hook_nodeapi 'update index'. This way you can index words not visible when viewing a node.

ceardach’s picture

Phew! I almost gave up there. Your suggestions were certainly over my head, as I am no PHP writer, but I kept staring and staring and staring until something made sense. I finally edited node.module with a tweak of my script to end up with this:

<?php 
    // Fetch extra data normally not visible
    $extra = node_invoke_nodeapi($node, 'update index');
    foreach ($extra as $t) {

/* EDITED: Add Term Synonyms to Search Index */

	$getTerms = taxonomy_node_get_terms($node->nid);
	foreach($getTerms as $nodeTerm) {
		$text .= ' '. $nodeTerm->name .' ';
		$childSynonyms = taxonomy_get_synonyms($nodeTerm->tid);
		foreach($childSynonyms as $childSynonym){
			$text .= $childSynonym .' ';
		}
		$parents = taxonomy_get_parents($nodeTerm->tid);
		foreach($parents as $parent) {
			$text .= ' '. $parent->name .' ';
			$synonyms = taxonomy_get_synonyms($parent->tid);
			foreach($synonyms as $synonym){
				$text .= $synonym .' ';
			}
		}
	}

/* end-EDITED */

      $text .= $t;
    }
?>

I have no idea if thats the most efficient way of doing it or not, but it got the job done. Now all keyword searches of a term's synonym will come up with all nodes in that term. Works for me!

Although, in the future, it may be a good addition to have the search.module have an administrative capability to have keyword synonyms. Perhaps I'll post it as a feature request.

sin’s picture

Now try not to modify a core (Drupal upgrade problems), but write a module.
0. Revert node.module to original state.
1. Create 'synonyms' directory inside /modules.
2. Create empty synonyms.module file inside /modules/synonyms.
3. Put the code inside synonyms.module:

function synonyms_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { 
  if ($op == 'update index') { 
    $text = '';
//---------------------------
    $getTerms = taxonomy_node_get_terms($node->nid);
    foreach($getTerms as $nodeTerm) {
        $text .= ' '. $nodeTerm->name .' ';
        $childSynonyms = taxonomy_get_synonyms($nodeTerm->tid);
        foreach($childSynonyms as $childSynonym){
            $text .= $childSynonym .' ';
        }
        $parents = taxonomy_get_parents($nodeTerm->tid);
        foreach($parents as $parent) {
            $text .= ' '. $parent->name .' ';
            $synonyms = taxonomy_get_synonyms($parent->tid);
            foreach($synonyms as $synonym){
                $text .= $synonym .' ';
            }
        }
    }
//-----------------
    return $text;
  } 
}

4. Enable synonyms.module in module settings page.

node_invoke_nodeapi means that node.module queries all other modules for additional content before indexing. And your module will answer and provide synonyms for all nodes.

You can add a feature request to Drupal search or taxonomy module for integrating synonyms with search, provide your code as an example.

ceardach’s picture

Wow. I had no idea it was that easy to create a module. Of course I'll have to look over the changes you did make and see what it all means. Perhaps I'll be able to do that again for other tweaks I do :)

Thanks a lot for the help!

adminfor@inforo.com.ar’s picture

I have a week looking for this.
It´s great.
God bless you

alippai’s picture

Can you write it to work with Drupal 5?

sin’s picture

Converting 4.7.x modules to 5.x (.info).
Add .info file, this gives the ability to enable module in Drupal 5.

danwassink’s picture

I have tried creating the module and adding the info file. Drupal 5 recognizes the module itself and once I turn it on I get this error:

Parse error: syntax error, unexpected T_IF in /home/thewassi/public_html/sites/buildersgenie.com/modules/synonyms/synonyms.module on line 3

Will this work on version 5.x?

sin’s picture

The error is not Drupal 5 specific, something wrong in the code. Check parenthesis matching.

David Lesieur’s picture

By the way, this patch was trying to provide this functionality (and more) in Drupal core, but never caught much attention. If anyone's interested in this feature, please review and test the patch, and comment it. :-) The patch is not currently working against Drupal 6.x, but it still applies to 5.x.

If there's enough interest, there might be hope to make it happen in Drupal 6.x.

bojanz’s picture

I have created a small module, based on the code from this thread, so you can just download, enable, and enjoy!

http://www.drupal.org/project/synonyms

ulechka’s picture

thank you.
it's really what I need : )

Moosa’s picture

hello,
i just downloaded and enabled your module, it didn't work or show any result.
i have taxonomy terms listed in a menu and url named by url alias module,
how should i make them searchable by normal drupal search form?