Hi, I'm attempting to duplicate the sort functionality of a traditional database view of nodes. Here's what that looks like.

Relationships: Content: Type (content taxonomy field)
Relationships: Content: Color (content taxonomy field)
Sort: Taxonomy Weight (Relationship to Content: Type)
Sort: Taxonomy Term ID (Relationship to Content: Type)
Sort: Taxonomy Term (Relationship to Content: Color)

What this does is to create a relationship to the node's taxonomy terms across two different vocabularies, then sorts first by the term weight of one vocabulary, then by the term ID, and finally by term from the second vocabulary. Fairly complex but it works well.

OK, so now I've set out to do this in Solr. Even if it's not exactly the same, I'd like to get close. The only thing I have been able to succeed with so far is simply add the sort for Taxonomy: Terms. It definitely sorts the results differently, but across different vocabularies and not in the way I'd like.

What I really need is some type of relationship to the term vocabulary so I can sort on just the terms from that vocabulary. According to #621818: Possibility to add relationship to nodes, it seems like relationships aren't going to be workable.

So I started down a different path. I added a custom sort handler for taxonomy, then I added option_definition() and options_form() allowing the user to select a vocabulary (obtained from taxonomy_get_vocabularies()). This part works well - now in the views UI when I add the taxonomy sort, there is an option to select vocabulary and this is in turn passed in to the query() function. So far so good, now I have a sort by taxonomy with the correct VID argument.

class apachesolr_views_handler_sort_tid extends views_handler_sort {

  function option_definition() {
    $options = parent::option_definition();

    $vocabularies = taxonomy_get_vocabularies();

    // Just use whatever the first vocabulary is as the default
    $default = array_slice($vocabularies, 0, 1);
    $options['vocabulary'] = array('default' => $default[0]->vid);

    return $options;
  }

  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);

    $vocabularies = taxonomy_get_vocabularies();
    $options = array();
    foreach ($vocabularies as $vid => $vocabulary) {
      $options[$vid] = t($vocabulary->name);
    }

    $form['vocabulary'] = array(
      '#type' => 'radios',
      '#title' => t('Vocabulary'),
      '#options' => $options,
      '#description' => t('The specific vocabulary to use for filtering'),
      '#default_value' => $this->options['vocabulary'],
    );
  }

The problem now is what to do with it. When I get to query(), I have access to the selected vocabulary ID: $vid = $this->options['vocabulary']; Now the question becomes: how to I essentially limit the sort to only terms from that vocabulary? Write a custom $formula? I'm stumped at this point and would love a few suggestions or ideas, even if they are "you are going down the wrong path."

Here's a similar issue, with traditional database views: #1187134: Creating custom views sort handler to sort by difference of two fields. They are doing a custom sort after taking action on information from the query/view.

If you help me get this working, I will dance like these guys! (Sorry, no video of me :)
http://www.youtube.com/watch?v=QuQIwHSbmC4

Thanks in advance for thoughts or input.

Comments

rjbrown99’s picture

Status: Active » Fixed

Ok so I figured this out. Posting back here in case it helps others.

I found this to be very helpful:
http://drupal.org/node/728088#comment-4621418

What I did was based on that approach. Here's how it worked.

1) In my custom module, I do a hook_apachesolr_update_index(), and I created a custom field in the Solr index. In my case, my node type where I want to apply the sort has exactly one term per node (IE, it's not a multiselect or tags, just one TID per node.) Because of that, I can index the weight of only that term as it applies to the node. So here you go.

function mymodule_apachesolr_update_index(&$document, $node) {
  switch ($node->type) {
    case 'mynodetype':
      mymodule_addweight_type($document, $node);
      break;
  }
}
function mymodule_addweight_type(&$document, $node) {
  $term = taxonomy_get_term($node->field_mytaxonomyfield[0]['value']);
  if (!empty($term->weight)) {
    $document->fs_mytaxonomyfieldweight_type = $term->weight;
  }
}

Do a complete reindex at that point to populate the value. Then you need to add it to apachesolr_views, similar to how they do it in that module I linked above. Basically you hook_views_data_alter() for this part.

function mymodule_views_data_alter(&$data) {
  foreach (module_invoke_all('apachesolr_entities') as $base_table => $definition) {
    // Custom views handler for taxonomy weight for Type vocabulary
    $data['apachesolr_' . $base_table]['fs_mytaxonomyfieldweight_type'] = array(
      'title' => t('Taxonomy Weight: My Field'),
      'help' => t('Weight of the My Field taxonomy term associated with the node'),
      'sort' => array(
        'handler' => 'apachesolr_views_handler_sort'
      ),
    );
  }
}

Clear your views cache, and now this should show up as a sort option for apachesolr_views.

I had one other challenge, in that I have a taxonomy hierarchy with parent and child terms. Some of my terms have the same weight, so this sort ended up mixing thsoe up. I figured I'd just add a second sort by taxonomy term, but by default it indexes ALL terms (parent and child) for a node. So that also resulted in mixed up sorts. To fix that, I added another custom field in the solr index, where I index ONLY the child term for a node and not the parents. This enabled me to sort first by weight, then by only the child term.

Mission accomplished, and without making any changes to the actual apachesolr_views module. Hope this helps someone trying to do the same thing.

dstuart’s picture

Status: Fixed » Closed (fixed)