diff --git a/handlers/islandora_solr_views_handler_argument_by_schema.inc b/handlers/islandora_solr_views_handler_argument_by_schema.inc new file mode 100644 index 0000000..b263c9a --- /dev/null +++ b/handlers/islandora_solr_views_handler_argument_by_schema.inc @@ -0,0 +1,63 @@ +argument) && !empty($this->options['solr_field'])) { + $value = islandora_solr_lesser_escape($this->argument); + $solr_field = $this->options['solr_field']; + module_load_include('inc', 'islandora_solr', 'includes/utilities'); + $group = isset($this->options['group']) ? $this->options['group'] : FALSE; + if (is_array($value)) { + $values = array_filter($value); + // Ensure that some values have been selected. + if (!empty($values)) { + $this->query->add_filter($solr_field, '(' . implode('OR', $values) . ')', $group); + } + return; + } + if (!empty($value)) { + $this->query->add_filter($solr_field, $value, $group); + } + } + } + + /** + * Define custom option for our solr field. + */ + function option_definition() { + $options = parent::option_definition(); + $options['solr_field'] = array('default' => 'PID'); + return $options; + } + + /** + * Define form element for real solr field. + */ + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + $form['solr_field'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => t('Solr field to use'), + '#description' => t("Select the Solr field to use"), + '#size' => 45, + '#type' => 'textfield', + '#autocomplete_path' => 'islandora_solr_views/autocomplete_luke/all', + '#default_value' => !empty($this->options['solr_field']) ? $this->options['solr_field'] : 'PID', + '#required' => TRUE, + '#weight' => -10, + ); + } +} +// @codingStandardsIgnoreEnd diff --git a/handlers/islandora_solr_views_handler_field_by_schema.inc b/handlers/islandora_solr_views_handler_field_by_schema.inc new file mode 100644 index 0000000..d7fec1b --- /dev/null +++ b/handlers/islandora_solr_views_handler_field_by_schema.inc @@ -0,0 +1,48 @@ + 'PID'); + return $options; + } + + /** + * Define form element for 'link to object' option and 'solr_field'. + */ + function options_form(&$form, &$form_state) { + + $form['solr_field'] = array( + '#title' => t('Solr field to use'), + '#description' => t("Select the Solr field to use"), + '#size' => 45, + '#type' => 'textfield', + '#autocomplete_path' => 'islandora_solr_views/autocomplete_luke/displayable', + '#default_value' => !empty($this->options['solr_field']) ? $this->options['solr_field'] : 'PID', + '#required' => TRUE, + ); + + parent::options_form($form, $form_state); + } + + function ui_name($short = FALSE) { + return $this->get_field(parent::ui_name($short)) . ' ' . $this->options['solr_field']; + } + + +} +// @codingStandardsIgnoreEnd diff --git a/handlers/islandora_solr_views_handler_field_date.inc b/handlers/islandora_solr_views_handler_field_date.inc index fb72521..1066315 100644 --- a/handlers/islandora_solr_views_handler_field_date.inc +++ b/handlers/islandora_solr_views_handler_field_date.inc @@ -12,6 +12,29 @@ // being called all over the place. TODO bring up to coding standards class islandora_solr_views_handler_field_date extends islandora_solr_views_handler_field { + + /** + * Get value. + * + * @param type $values + * @param type $field + * + * @return type string + */ + function get_value($values, $field = NULL) { + + $alias = isset($field) ? $this->aliases[$field] : $this->field_alias; + + if (isset($values->{$alias})) { + if (is_array($values->{$alias})) { + $values->{$alias} = array_filter($values->{$alias}, 'trim'); + return implode(", ", $values->{$alias}); + } + else { + return $values->{$alias}; + } + } + } /** * Define new option. */ @@ -20,6 +43,7 @@ class islandora_solr_views_handler_field_date extends islandora_solr_views_handl $options = parent::option_definition(); // Set defaults. + $options['solr_field'] = array('default' => 'fgs_createdDate_dt'); $options['date_format'] = array('default' => 'small'); $options['custom_date_format'] = array('default' => ''); @@ -39,7 +63,15 @@ class islandora_solr_views_handler_field_date extends islandora_solr_views_handl '@date' => format_date(REQUEST_TIME, $value['type']), )); } - + $form['solr_field'] = array( + '#title' => t('Solr field to use'), + '#description' => t("Select the Solr date field to use"), + '#size' => 45, + '#type' => 'textfield', + '#autocomplete_path' => 'islandora_solr_views/autocomplete_luke/date', + '#default_value' => !empty($this->options['solr_field']) ? $this->options['solr_field'] : 'fgs_createdDate_dt', + '#required' => TRUE, + ); $form['date_format'] = array( '#type' => 'select', '#title' => t('Date format'), @@ -76,7 +108,9 @@ class islandora_solr_views_handler_field_date extends islandora_solr_views_handl parent::options_form($form, $form_state); } - + function ui_name($short = FALSE) { + return $this->get_field(parent::ui_name($short)) . ' ' . $this->options['solr_field']; + } /** * Render field. */ diff --git a/handlers/islandora_solr_views_handler_filter.inc b/handlers/islandora_solr_views_handler_filter.inc index 39bb7c5..efcd8f6 100644 --- a/handlers/islandora_solr_views_handler_filter.inc +++ b/handlers/islandora_solr_views_handler_filter.inc @@ -69,6 +69,7 @@ class islandora_solr_views_handler_filter extends views_handler_filter { * Provide default options for exposed filters. */ function expose_options() { + parent::expose_options(); $this->options['expose']['identifier'] = drupal_strtolower(preg_replace('/[^A-Za-z0-9]/', '_', $this->options['id'])); } diff --git a/handlers/islandora_solr_views_handler_filter_by_schema.inc b/handlers/islandora_solr_views_handler_filter_by_schema.inc new file mode 100644 index 0000000..ead9795 --- /dev/null +++ b/handlers/islandora_solr_views_handler_filter_by_schema.inc @@ -0,0 +1,139 @@ +value) && !empty($this->options['solr_field'])) { + $value = $this->value; + module_load_include('inc', 'islandora_solr', 'includes/utilities'); + // Only escape if 'value_type' is disabled to allow range queries + // and other non string ones. + if (!$this->options['value_type']) { + $value = islandora_solr_lesser_escape($value); + } + $exclude = isset($this->operator) && $this->operator === '!='; + if (is_array($value)) { + $values = array_filter($value); + // Ensure that some values have been selected. + if (!empty($values)) { + $this->query->add_filter($this->options['solr_field'], '(' . implode('OR', $values) . ')', $this->options['group'], $exclude); + } + return; + } + $this->query->add_filter($this->options['solr_field'], $value, $this->options['group'], $exclude); + } + } + /** + * Define new custom options. + */ + function option_definition() { + $options = parent::option_definition(); + $options['solr_field'] = array('default' => 'PID'); + $options['value_type'] = array('default' => 0); + // There is a bug in Views that makes this option required, + // even when not exposed. + $options['expose']['identifier'] = array('default' => 'solrfilter'); + + return $options; + } + + function admin_summary() { + return check_plain((string) $this->options['solr_field']) . check_plain((string) $this->operator) . ' ' . check_plain((string) $this->value); + } + + /** + * Define custom form elements to match options. + */ + function options_form(&$form, &$form_state) { + // Add an option to allow non string filters like [* TO NOW] + $form['value_type'] = array( + '#type' => 'checkbox', + '#title' => t("Don't escape filter value for Solr"), + '#description' => t('Enable filter value to be passed without escaping. Useful for e.g [* TO NOW]'), + '#default_value' => !empty($this->options['value_type']) ? $this->options['value_type'] : 0, + '#weight' => 11, + ); + $form['solr_field'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => t('Solr field to use'), + '#description' => t("Select the Solr field to use"), + '#size' => 45, + '#type' => 'textfield', + '#autocomplete_path' => 'islandora_solr_views/autocomplete_luke/all', + '#default_value' => !empty($this->options['solr_field']) ? $this->options['solr_field'] : 'PID', + '#required' => TRUE, + '#weight' => -10, + ); + parent::options_form($form, $form_state); + // Modify the title for 'value' + $form['value']['#title'] = t("Filter value for Solr field"); + $form['value']['#weigth'] = 10; + } + + /** + * Don't allow exposure for not filtered values. + */ + function can_expose() { + return ($this->options['value_type'] == 0); + } + + /** + * Validation handler, the parent:: fails on disabling 'expose'. + */ + function options_validate(&$form, &$form_state) { + $this->operator_validate($form, $form_state); + $this->value_validate($form, $form_state); + if (isset($form_state['values']['expose_button']['checkbox']['checkbox']) && ($form_state['values']['expose_button']['checkbox']['checkbox'] == 1) && !$this->is_a_group()) { + $this->expose_validate($form, $form_state); + } + if ($this->is_a_group()) { + $this->build_group_validate($form, $form_state); + } + } + + /** + * Build strings from the operators() for 'select' options. + */ + function operator_options($which = 'title') { + $options = array(); + foreach ($this->operators() as $id => $info) { + $options[$id] = $info[$which]; + } + + return $options; + } + + function operator_values($values = 1) { + $options = array(); + foreach ($this->operators() as $id => $info) { + if (isset($info['values']) && $info['values'] == $values) { + $options[] = $id; + } + } + + return $options; + } + + public function expose_form(&$form, &$form_state) { + parent::expose_form($form, $form_state); + if (empty($form['expose']['identifier']['#default_value'])) { + $form['expose']['identifier']['#default_value'] = $this->options['field']; + } + if (empty($form['expose']['label']['#default_value'])) { + $form['expose']['label']['#default_value'] = $this->definition['title']; + } + if (empty($form['ui_name']['#default_value'])) { + $form['ui_name']['#default_value'] = $this->definition['title']; + } + } +} +// @codingStandardsIgnoreEnd \ No newline at end of file diff --git a/handlers/islandora_solr_views_handler_sort_by_schema.inc b/handlers/islandora_solr_views_handler_sort_by_schema.inc new file mode 100644 index 0000000..b4bab10 --- /dev/null +++ b/handlers/islandora_solr_views_handler_sort_by_schema.inc @@ -0,0 +1,58 @@ +options['order']); + $this->query->add_sort($this->options['solr_field'], $order); + } + /** + * Define new custom option. + */ + function option_definition() { + $options = parent::option_definition(); + $options['solr_field'] = array('default' => 'PID'); + return $options; + } + + /** + * Custom admin summary for field. + */ + function admin_summary() { + $internal_summary = parent::admin_summary(); + return check_plain((string) $this->options['solr_field']) . ' ' . check_plain(drupal_strtolower((string) $this->options['order'])) . ' ' . $internal_summary; + } + + /** + * Define form element for 'solr_field'. + */ + function options_form(&$form, &$form_state) { + $form['solr_field'] = array( + '#title' => t('Solr field to use for sorting'), + '#description' => t("Select the Solr field to use for sorting"), + '#size' => 45, + '#type' => 'textfield', + '#autocomplete_path' => 'islandora_solr_views/autocomplete_luke/sortable', + '#default_value' => !empty($this->options['solr_field']) ? $this->options['solr_field'] : 'PID', + '#required' => TRUE, + ); + + parent::options_form($form, $form_state); + } +} +// @codingStandardsIgnoreEnd diff --git a/includes/blocks.inc b/includes/blocks.inc new file mode 100644 index 0000000..02c78f9 --- /dev/null +++ b/includes/blocks.inc @@ -0,0 +1,27 @@ + array( + 'name' => t('Islandora facets for Solr Views'), + 'module' => 'islandora_solr_views', + 'file' => 'includes/blocks.inc', + 'class' => 'IslandoraSolrResultsView', + 'function' => 'displayFacets', + 'form' => NULL, + ), + ); +} + +class IslandoraSolrResultsView extends IslandoraSolrResults {}; diff --git a/includes/callbacks.inc b/includes/callbacks.inc new file mode 100644 index 0000000..ed50cec --- /dev/null +++ b/includes/callbacks.inc @@ -0,0 +1,59 @@ + $value) { + if (stripos($term, $string) !== FALSE) { + // Search case insensitive, but keep the case on replace. + $term_str = preg_replace("/$string/i", "\$0", $term); + + // Add strong elements to highlight the found string. + $result[$term] = $term_str . '(' . $value['type'] . ')'; + } + } + // Sort alphabetically. + ksort($result); + + drupal_json_output($result); + exit(); +} diff --git a/islandora_solr_views.info b/islandora_solr_views.info index 14d5c68..3e2818e 100644 --- a/islandora_solr_views.info +++ b/islandora_solr_views.info @@ -10,11 +10,15 @@ dependencies[] = views files[] = islandora_solr_views_query.inc files[] = handlers/islandora_solr_views_handler_field.inc +files[] = handlers/islandora_solr_views_handler_field_by_schema.inc files[] = handlers/islandora_solr_views_handler_field_date.inc files[] = handlers/islandora_solr_views_handler_field_manage.inc files[] = handlers/islandora_solr_views_handler_sort.inc +files[] = handlers/islandora_solr_views_handler_sort_by_schema.inc files[] = handlers/islandora_solr_views_handler_argument.inc +files[] = handlers/islandora_solr_views_handler_argument_by_schema.inc files[] = handlers/islandora_solr_views_handler_filter.inc +files[] = handlers/islandora_solr_views_handler_filter_by_schema.inc files[] = handlers/islandora_solr_views_handler_filter_query_lucene.inc files[] = handlers/islandora_solr_views_handler_filter_query_dismax.inc files[] = handlers/islandora_solr_views_handler_collection_count.inc diff --git a/islandora_solr_views.module b/islandora_solr_views.module index 95c415c..ccaaddf 100644 --- a/islandora_solr_views.module +++ b/islandora_solr_views.module @@ -5,6 +5,22 @@ * Provides Views Implementation for Islandora Solr */ +/** + * Implements hook_menu(). + */ +function islandora_solr_views_menu() { + $items['islandora_solr_views/autocomplete_luke/%'] = array( + 'title' => 'Islandora Solr Luke autocomplete', + 'description' => 'Autocomplete callback to populate solr text fields.', + 'page callback' => '_islandora_solr_views_autocomplete_luke', + 'page arguments' => array(2), + 'access arguments' => array('administer islandora solr'), + 'file' => 'includes/callbacks.inc', + 'type' => MENU_CALLBACK, + ); + return $items; +} + /** * Implements hook_views_api(). */ diff --git a/islandora_solr_views.views.inc b/islandora_solr_views.views.inc index d5b9ef8..72a9a4c 100644 --- a/islandora_solr_views.views.inc +++ b/islandora_solr_views.views.inc @@ -29,6 +29,7 @@ function islandora_solr_views_views_plugins() { */ function islandora_solr_views_views_data() { // Set base variables. + module_load_include('inc', 'islandora_solr', 'includes/luke'); $base_field = 'PID'; $base_table = 'islandora_solr'; $data[$base_table]['table']['group'] = t('Islandora Solr'); @@ -39,11 +40,6 @@ function islandora_solr_views_views_data() { 'help' => t('Searches the Islandora Solr index.'), 'field' => $base_field, ); - - // Get the list of the fields in index directly from Solr. - $luke = islandora_solr_get_luke(); - $solr_fields = $luke['fields']; - // Always add score handlers. $data[$base_table]['score'] = array( 'title' => t('Score'), @@ -90,52 +86,75 @@ function islandora_solr_views_views_data() { ), ); - // Loop over all solr fields. - foreach ($solr_fields as $solr_field_name => $solr_field) { + // Get the list of the fields in index directly from Solr. + $luke = islandora_solr_get_luke(); + $solr_fields = $luke['fields']; - // We do not allow to display 'sort_*' fields. - if (strpos($solr_field_name, 'sort_') === 0) { - continue; - } + // Create template array for different supported field + // types. + $solr_fields_types_to_create = array( + 'date' => array( + 'create' => FALSE, + 'title' => t('Date Solr field'), + 'help' => t('Any Solr field of type Date'), + ), + 'sortable' => array( + 'create' => FALSE, + 'title' => t('Sortable Solr field'), + 'help' => t('Any Solr field that can be used to sort'), + ), + 'general' => array( + 'create' => count($solr_fields) > 0 ? TRUE : FALSE, + 'title' => t('General Solr field'), + 'help' => t('Any Solr field that can be used to query, filter or as context argument'), + ), + ); + // Loop over all solr fields. We will group by types + // instead of creating individual fields. + foreach ($solr_fields as $solr_field_name => $solr_field) { // Set luke field variables. $field_type = $solr_field['type']; $field_schema = $solr_field['schema']; - $field_dynamicbase = isset($solr_field['dynamicBase']) ? $solr_field['dynamicBase'] : NULL; - - // Set field handlers. - $field = array(); - $field['title'] = $solr_field_name; - $field['help'] = t('Type') . ': ' . $field_type; - // Field handler. if ($field_type == 'date') { - $field['field']['handler'] = 'islandora_solr_views_handler_field_date'; - } - else { - $field['field']['handler'] = 'islandora_solr_views_handler_field'; + $solr_fields_types_to_create['date']['create'] = TRUE; } - // Check if sortable. if (strstr($field_schema, "I") != FALSE AND strstr($field_schema, "M") == FALSE) { - $field['field']['click sortable'] = TRUE; + $solr_fields_types_to_create['sortable']['create'] = TRUE; } - // Argument handler. - $field['argument'] = array( - 'handler' => 'islandora_solr_views_handler_argument', - ); - // Filter handler. - $field['filter'] = array( - 'handler' => 'islandora_solr_views_handler_filter', - ); - // Sortable handler. - // Check if sortable: must be indexed and can't be multivalued. - // http://wiki.apache.org/solr/CommonQueryParameters#sort - if (strstr($field_schema, "I") != FALSE AND strstr($field_schema, "M") == FALSE) { - $field['sort'] = array( - 'handler' => 'islandora_solr_views_handler_sort', + } + // Create actual fields grouped by schema type. + foreach ($solr_fields_types_to_create as $type => $field_data) { + if ($field_data['create']) { + $field = array(); + $field['title'] = $field_data['title']; + $field['help'] = $field_data['help']; + if ($type == 'date') { + $field['field'] = array( + 'handler' => 'islandora_solr_views_handler_field_date', + ); + } + else { + $field['field'] = array( + 'handler' => 'islandora_solr_views_handler_field_by_schema', + ); + } + $field['filter'] = array( + 'handler' => 'islandora_solr_views_handler_filter_by_schema', ); + if ($type == 'sortable') { + $field['sort'] = array( + 'handler' => 'islandora_solr_views_handler_sort_by_schema', + ); + } + if ($type == 'general') { + $field['argument'] = array( + 'handler' => 'islandora_solr_views_handler_argument_by_schema', + ); + } + $data[$base_table][$type] = $field; } - // Add array. - $data[$base_table][$solr_field_name] = $field; } + // Add our collection counting goodness. $data[$base_table]['solr_collection_count'] = array( 'title' => 'Collection count', diff --git a/islandora_solr_views_query.inc b/islandora_solr_views_query.inc index 6471d4e..a6e2331 100644 --- a/islandora_solr_views_query.inc +++ b/islandora_solr_views_query.inc @@ -58,18 +58,28 @@ class islandora_solr_views_query extends views_plugin_query { */ public function build(&$view) { $view->init_pager(); - // Let the pager modify the query to add limits. $this->pager->query(); - + + // Real Solr fields mapper + $solr_fields = array(); // Set aliases of the fields. + // Since we our real Solr field will not match always the $field_name, + // we will have to build our own naming structure. foreach ($view->field as $field_name => &$field) { - $field->field_alias = $field_name; + $solr_field_name_to_use = isset($field->options['solr_field']) ? $field->options['solr_field'] : $field->real_field; + if (isset($solr_fields[$solr_field_name_to_use])) { + $cardinality = array_push($solr_fields[$solr_field_name_to_use], $field_name); + } + else { + $solr_fields[$solr_field_name_to_use] = array($field_name); + $cardinality = 1; + } + $field->field_alias = $cardinality == 1 ? $solr_field_name_to_use : $solr_field_name_to_use . "_" . ($cardinality-1); $field->aliases['entity_type'] = 'entity_type'; } - // Add fields to the query so they will be shown in solr document. - $this->params['fl'] = array_keys($view->field); + $this->params['fl'] = array_keys($solr_fields); } /** @@ -78,13 +88,25 @@ class islandora_solr_views_query extends views_plugin_query { * Executes the query and fills the associated view object with according * values. * + * @global IslandoraSolrQueryProcessor $_islandora_solr_queryclass + * The IslandoraSolrQueryProcessor object which includes the current query + * settings and the raw Solr results. + * * Values to set: $view->result, $view->total_rows, $view->execute_time, * $view->pager['current_page']. */ public function execute(&$view) { try { + // New query processor class. + $islandora_solr_query = new IslandoraSolrQueryProcessor(); + // Only enable facets if needed. Makes queries faster + if ($this->options['enable_facets']) { + global $_islandora_solr_queryclass; + // Set our famous global so we can enable the facet block + $_islandora_solr_queryclass = $islandora_solr_query; + } + $start = microtime(TRUE); - // Include common.inc. module_load_include('inc', 'islandora_solr', 'includes/common'); @@ -96,7 +118,7 @@ class islandora_solr_views_query extends views_plugin_query { if ($params['rows'] == 0) { $params['rows'] = 1000000; if (!isset($this->offset)) { - $this->offset = 0; + $this->offset = 0; } } // Add fields. @@ -129,10 +151,10 @@ class islandora_solr_views_query extends views_plugin_query { // Add sorting. if (isset($this->orderby)) { module_load_include('inc', 'islandora_solr', 'includes/utilities'); - // Populate sorting parameters. - foreach ($this->orderby as $field => $order) { - $params['sort'][] = islandora_solr_lesser_escape($field) . ' ' . $order; - } + // Populate sorting parameter as one string. + $params['sort'] = array_map(function($item) { + return $item['field'] . ' ' . $item['direction']; + }, $this->orderby); } // Set query. @@ -150,23 +172,22 @@ class islandora_solr_views_query extends views_plugin_query { $query = '*:*'; } - // New query processor class. - $islandora_solr_query = new IslandoraSolrQueryProcessor(); // Check for dismax (not functional yet). if ($dismax != NULL) { $islandora_solr_query->solrDefType = $dismax; $params['defType'] = $dismax; } - // Add query (defaults to *:*). $islandora_solr_query->buildQuery($query, $params); - + // Add 'fl' to solrParams since the query builder does not use them. + $islandora_solr_query->solrParams['fl'] = $params['fl']; + // Disable or enable facets depending on user option. + $islandora_solr_query->solrParams['facet'] = $this->options['enable_facets'] ? 'true' : 'false'; // Add solr limit. $islandora_solr_query->solrLimit = $params['rows']; // Add solr start. $islandora_solr_query->solrStart = $this->offset; - // Excecute query. $islandora_solr_query->executeQuery(FALSE); // Solr results. @@ -205,18 +226,33 @@ class islandora_solr_views_query extends views_plugin_query { return; } } + function option_definition() { + $options = parent::option_definition(); + $options['enable_facets'] = array('default' => FALSE); + return $options; + } + + function options_form(&$form, &$form_state) { + $form['enable_facets'] = array( + '#type' => 'checkbox', + '#title' => t('Enable Facets'), + '#default_value' => $this->options['enable_facets'], + '#description' => t('This option enables facet processing and global Solr variable to display block'), + '#required' => FALSE, + ); + } /** - * Function add_filter. - */ + * Function add_filter. + */ public function add_filter($type, $value, $group, $exclude = FALSE) { module_load_include('inc', 'islandora_solr', 'includes/utilities'); $exclude_string = ($exclude) ? '-' : ''; if ($group) { - $this->params['filters'][$group][] = $exclude_string . islandora_solr_lesser_escape($type) . ':' . $value; + $this->params['filters'][$group][] = $exclude_string . $type . ':' . $value; } else { - $this->params['filters'][] = $exclude_string . islandora_solr_lesser_escape($type) . ':' . $value; + $this->params['filters'][] = $exclude_string . $type . ':' . $value; } } @@ -232,7 +268,11 @@ class islandora_solr_views_query extends views_plugin_query { * Function add_sort. */ public function add_sort($field, $order) { - $this->orderby[$field] = $order; + //$this->orderby[$field] = $order; + $this->orderby[] = array( + 'field' => $field, + 'direction' => drupal_strtolower($order) + ); } /**