Index: Solr_Base_Query.php =================================================================== --- Solr_Base_Query.php (revision 30471) +++ Solr_Base_Query.php (working copy) @@ -136,7 +136,12 @@ function __clone() { $this->id = ++self::$idCount; } + + public function remove_keys() { + unset($this->keys); + } + public function add_filter($field, $value, $exclude = FALSE) { $this->fields[] = array('#exclude' => $exclude, '#name' => $field, '#value' => trim($value)); } Index: apachesolr.module =================================================================== --- apachesolr.module (revision 30471) +++ apachesolr.module (working copy) @@ -966,6 +966,13 @@ '#description' => t('A facet can be generated corresponding to all documents entirely missing this field.'), '#default_value' => isset($facet_missing[$module][$delta]) ? $facet_missing[$module][$delta] : 0, ); + $reflect_hierarchy = variable_get('apachesolr_facet_reflect_hierarchy', array()); + $form['apachesolr_facet_reflect_hierarchy'] = array( + '#type' => 'checkbox', + '#title' => t('Links reflect hierarchy'), + '#description' => t('Facet will be sorted hierarchically if it bases on a hierarchical taxonomy vocabulary.'), + '#default_value' => !empty($reflect_hierarchy[$module][$delta]), + ); return $form; } @@ -986,6 +993,9 @@ $facet_missing = variable_get('apachesolr_facet_missing', array()); $facet_missing[$module][$delta] = (int)$edit['apachesolr_facet_missing']; variable_set('apachesolr_facet_missing', $facet_missing); + $reflect_hierarchy = variable_get('apachesolr_facet_reflect_hierarchy', array()); + $reflect_hierarchy[$module][$delta] = $edit['apachesolr_facet_reflect_hierarchy']; + variable_set('apachesolr_facet_reflect_hierarchy', $reflect_hierarchy); } /** @@ -1445,8 +1455,11 @@ drupal_add_js(drupal_get_path('module', 'apachesolr') . '/apachesolr.js'); // Split items array into displayed and hidden. $hidden_items = array_splice($items, $display_limit); - foreach ($hidden_items as $link) { - $items[] = array('data' => $link, 'class' => 'apachesolr-hidden-facet'); + foreach ($hidden_items as $hidden_item) { + if (!is_array($hidden_item)) { + $hidden_item = array('data' => $hidden_item); + } + $items[] = $hidden_item + array('class' => 'apachesolr-hidden-facet'); } } $admin_link = ''; Index: apachesolr_search.module =================================================================== --- apachesolr_search.module (revision 30471) +++ apachesolr_search.module (working copy) @@ -562,78 +562,99 @@ // Get information needed by the taxonomy blocks about limits. $initial_limits = variable_get('apachesolr_facet_query_initial_limits', array()); $limit_default = variable_get('apachesolr_facet_query_initial_limit_default', 10); + $reflect_hierarchy = variable_get('apachesolr_facet_reflect_hierarchy', array()); // Handle taxonomy vocabulary facets if ((strpos($delta, 'im_vid_') === 0) && module_exists('taxonomy')) { if (is_object($response->facet_counts->facet_fields->$delta)) { - $contains_active = FALSE; - $terms = array(); + $vid = substr($delta, 7); + if (is_numeric($vid)) { + $vocab = taxonomy_vocabulary_load($vid); + $terms = array(); + foreach ($response->facet_counts->facet_fields->$delta as $tid => $count) { + if ($tid == '_empty_') { + // TODO - for now we don't handle facet missing. + continue; + } + $unclick_link = ''; + unset($active); - foreach ($response->facet_counts->facet_fields->$delta as $tid => $count) { - $options = array(); - if ($tid == '_empty_') { - // TODO - for now we don't handle facet missing. - continue; - } - $unclick_link = ''; - unset($active); - $term = taxonomy_get_term($tid); - $new_query = clone $query; - if ($active = $query->has_filter('tid', $tid)) { - $contains_active = TRUE; - $new_query->remove_filter('tid', $term->tid); - $options['query'] = $new_query->get_url_queryvalues(); - $link = theme('apachesolr_unclick_link', $term->name, $new_query->get_path(), $options); - } - else { - $new_query->add_filter('tid', $term->tid); - $options['query'] = $new_query->get_url_queryvalues(); - $link = theme('apachesolr_facet_link', $term->name, $new_query->get_path(), $options, $count, $active, $response->response->numFound); + $parents = array(); + if ($reflect_hierarchy['apachesolr_search'][$delta]) { + $parents = taxonomy_get_parents_all($tid); + if (!empty($parents[1]) && !$query->has_filter('tid', $parents[1]->tid)) { + continue; + } + + } + else if ($term = taxonomy_get_term($tid)) { + $parents[] = $term; + } + + $parents = array_reverse($parents); + + foreach ($parents as $key => $term) { + $parents[$key] = $term; + } + + $options = array(); + $active = $query->has_filter('tid', $tid); + if ($active) { + $remove = array(array('#name' => 'tid', '#value' => $tid)); + + if ($reflect_hierarchy['apachesolr_search'][$delta]) { + $childs = taxonomy_get_tree($vid, $tid); + foreach ($childs as $child) { + $remove[] = array('#name' => 'tid', '#value' => $child->tid); + } + } + + $link = apachesolr_search_get_unclick_link($query, $term->name, $remove); + } + else { + $new_query = clone $query; + $new_query->add_filter('tid', $tid); + $options = array('query' => $new_query->get_url_queryvalues()); + $link = theme('apachesolr_facet_link', $term->name, $new_query->get_path(), $options, $count, $active, $response->response->numFound); + } + + // if numdocs == 1 and !active, don't add. + if ($response->response->numFound > 1 || $active) { + $sort_key = ''; + $term = NULL; + foreach ($parents as $term) { + $count = 0; + if (property_exists($response->facet_counts->facet_fields->$delta, $term->tid)) { + $count = $response->facet_counts->facet_fields->$delta->{$term->tid}; + } + $countsort = $count == 0 ? '' : 1 / $count; + $countsort = $query->has_filter('tid', $term->tid) ? $countsort : 1 + $countsort; + $sort_key .= ($sort_key ? '|' : '') . $countsort . ':' . $term->name; + } + $terms[$term->vid][$sort_key] = $link; + } } - $countsort = $count == 0 ? '' : 1 / $count; - // if numdocs == 1 and !active, don't add. - if ($response->response->numFound > 1 || $active) { - $terms[$term->vid][$active ? $countsort . $term->name : 1 + $countsort . $term->name] = $link; + + // Process all terms into an item list + if (is_array($terms) && isset($terms[$vid]) && is_array($terms[$vid])) { + + // Create array of nested items + $items = _apachesolr_search_nested_facet_items($terms[$vid]); + $limit = isset($initial_limits['apachesolr_search'][$delta]) ? $initial_limits['apachesolr_search'][$delta] : $limit_default; + return array( + 'subject' => t('Filter by @name', array('@name' => $vocab->name)), + 'content' => theme('apachesolr_facet_list', $items, $limit), + ); } } } - $vid = substr($delta, 7); - $vocab = taxonomy_vocabulary_load($vid); - if (is_numeric($vid) && is_array($terms) && isset($terms[$vid]) && is_array($terms[$vid])) { - ksort($terms[$vid]); - $limit = isset($initial_limits['apachesolr_search'][$delta]) ? $initial_limits['apachesolr_search'][$delta] : $limit_default; - return array( - 'subject' => t('Filter by @name', array('@name' => $vocab->name)), - 'content' => theme('apachesolr_facet_list', $terms[$vid], $limit), - ); - } return; } switch ($delta) { case 'currentsearch': - $fields = $query->get_filters(); - $path = $query->get_path(); - $options = array(); - if (!$fields) { - $options['attributes']['class'] = 'active'; - } - $links[] = apachesolr_l($query->get_query_basic(), $path, $options); - foreach($fields as $field) { - if ($field['#name']) { - $new_query = clone $query; - $new_query->remove_filter($field['#name'], $field['#value']); - $options['query'] = $new_query->get_url_queryvalues(); - $fielddisplay = theme("apachesolr_breadcrumb_". $field['#name'], $field['#value']); - if (!$fielddisplay) { - $fielddisplay = $field['#value']; - } - $links[] = theme('apachesolr_unclick_link', $fielddisplay, $new_query->get_path(), $options); - } - } - $content = theme('apachesolr_currentsearch', $response->response->numFound, $links); - return array('subject' => t('Current search'), 'content' => $content); + return apachesolr_search_currentsearch_block($response, $query); case 'is_book_bid': return apachesolr_facet_block($response, $query, 'apachesolr_search', $delta, $delta, t('Filter by book'), 'apachesolr_search_get_book'); case 'language': @@ -676,6 +697,38 @@ } /** + * Creates a link to trigger a new solr search after removing at least one of previously + * selected search filters. Therefor you have to pass the filters' field names and values. + * + * @param $query object current apachesolr query + * @param $facet_text string + * @param $remove array array(array('#name' => 'field name', '#value' => 'field value'), array('#name' => 'field name', '#value' => 'field value')) + * @return string themed unclick link + */ +function apachesolr_search_get_unclick_link(&$query, $facet_text, $remove) { + static $unclick_queries = array(); + + sort($remove); + $unclick_query_key = array_reduce($remove, '_apachesolr_search_get_unclick_query_key'); + + if (empty($unclick_queries[$unclick_query_key])) { + $new_query = clone $query; + + foreach($remove as $field) { + $new_query->remove_filter($field['#name'], $field['#value']); + } + + $unclick_queries[$unclick_query_key]['query'] = $new_query->get_url_queryvalues(); + } + + return theme('apachesolr_unclick_link', $facet_text, $query->get_path(), $unclick_queries[$unclick_query_key]); +} + +function _apachesolr_search_get_unclick_query_key($res, $val) { + return $res.$val['#name'].$val['#value']; +} + +/** * Callback function for the 'Filter by book' facet block. */ function apachesolr_search_get_book($facet, &$options) { @@ -689,6 +742,27 @@ } /** + * Return nested array of nested facet values for use with theme_item_list() + */ +function _apachesolr_search_nested_facet_items($input, $parent = '', $level = 0) { + if ($level == 0) { + ksort($input); + } + $result = array(); + foreach ($input as $key => $item) { + // For items on current level, and their $key is son of $parent + if (substr_count($key, "|") == $level && ($parent == '' || strpos($key, $parent) === 0)) { + // Add this item and all items on next level + $result[$key] = array( + 'data' => $item, + 'children' => _apachesolr_search_nested_facet_items($input, $key, $level+1) + ); + } + } + return $result; +} + +/** * Callback function for the 'Filter by name' facet block. */ function apachesolr_search_get_username($facet) { @@ -1023,3 +1097,75 @@ return implode(' ... ', $snippets) .' ...'; } +/** + * Return the contents of the "Current search" block. + */ +function apachesolr_search_currentsearch_block($response, $query) { + $content = ''; + $reflect_hierarchy = variable_get('apachesolr_facet_reflect_hierarchy', array()); + $fields = $query->get_filters(); + $links = array(); + $active_facets = array(); + $active_facets_unclick = array(); + #$links[] = apachesolr_l($query->get_query_basic(), $query->get_path(), array('attributes' => array('class' => 'active'))); + // If current search has keys, offer current search without them + if ($keys = $query->get_query_basic()) { + $new_query = clone $query; + $new_query->remove_keys(); + $links[] = theme('apachesolr_unclick_link', $keys, $new_query->get_path(), array('query' => $new_query->get_url_queryvalues())); + } + foreach($fields as $field) { + if ($field['#name']) { + $processed = FALSE; + + if (module_exists('taxonomy') && 'tid' == $field['#name']) { + $term = taxonomy_get_term($field['#value']); + $facet_fieldname = 'im_vid_'.$term->vid; + if ($reflect_hierarchy['apachesolr_search'][$facet_fieldname]) { + + // Build data to theme this active facet hierarchically (using this term's parents) + $key = ''; + $parents = taxonomy_get_parents_all($term->tid); + $parents = array_reverse($parents); + foreach ($parents as $parent) { + + // Build field values to remove (the term's children and itself) + $children = taxonomy_get_tree($parent->vid, $parent->tid); + $children[] = $parent; + $remove = array(); + foreach ($children as $child) { + $remove[] = array('#name' => 'tid', '#value' => $child->tid); + } + // Key is a string denoting hierarchy, and includes the term name + // so it can be sorted for UX clarity. + $key .= ($key != '' ? '|' : '') . $parent->name . ':tid:' . $parent->tid; + + // Generate unclick link + if (! $fielddisplay = theme('apachesolr_breadcrumb_tid', $parent->tid)) { + $fielddisplay = $parent->tid; + } + $active_facets_unclick[$facet_fieldname][$key] = apachesolr_search_get_unclick_link($query, $fielddisplay, $remove); + } + $processed = TRUE; + } + } + + // If not a hierarchic taxonomy term, or not a taxonomy term, add to $links[] now + if (! $processed) { + $remove = array($field); + if (! $fielddisplay = theme('apachesolr_breadcrumb_'. $field['#name'], $field['#value'])) { + $fielddisplay = $field['#value']; + } + $links[] = apachesolr_search_get_unclick_link($query, $fielddisplay, $remove); + } + } + } + + // Process each $active_facets_unclick[vid] into $links[] + foreach ($active_facets_unclick as $facet_unclick) { + $links += _apachesolr_search_nested_facet_items($facet_unclick); + } + + $content = theme('apachesolr_currentsearch', $response->response->numFound, $links); + return array('subject' => t('Current search'), 'content' => $content); +}