I'm trying to create a custom facet & widget that are restricted to only working with each other.

- register a custom facet type using hook_facetapi_facet_info() and set 'query types' to array('myquerytype')
- register a custom facet widget using hook_facetapi_widgets() and set 'query types' to array('myquerytype')

However, I are unable to edit the facet display in the admin area: [admin/config/search/facetapi/(realm)/block/(facetname)/edit]

facetapi_facet_display_form() contains:
$global_settings = $adapter->getFacet($facet)->getSettings()
FacetapiFacet::getSettings(): calls
FacetapiAdapter::getFacetSettingsGlobal(): sets a default 'query_type' of 'term'
$form['global']['query_type'] uses $global_settings->settings['query_type'] for the default option regardless of what the facet actually supports.

If the facet doesn't support 'term' then when you try to save the form you get a validation error to this effect.

FacetapiFacet::getSettings() doesn't seem to be intended to be overriden [at least not without creating a new FacetapiAdapter subclass -- I would expect that creating a new query type shouldn't involve overriding the whole backend].

Two workarounds when creating the Query Type dropdown:
[1] change $global_settings->settings['query_type'] to $facet_settings->settings['query_type']. Without fully understanding everything that facetapi is doing internally I'm not convinced that this is actually a proper solution -- what about all other attributes that are read from the global settings and not the facet settings (eg operator)?
[2] Set #access to true regardless of the number of options. In this case, #default is effectively ignored.

In short: it seems impossible to create a facet & widget that does not support 'term' query type.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

cpliakas’s picture

Category: bug » support

Hi levialliance.

Thanks for posting. I am changing this to a support request, because I can confirm that the conclusion in the bug report isn't correct. Facet API most definitely can have facets and widgets that don't support the "term" query type.

Did you create a query type plugin for "myquerytype" and register it with the adapter you are using? See the apachesolr_facetapi_query_types() function and the associated classes for an example.

Also, are you sure you want to implement a new query type? I'm not sure I understand the desire to have a facet that is only tied to one widget. I can see the flip use case, where a widget is only tied to one facet. I just discovered that this isn't documented in facetapi.api.php :-(, but there is a "requirements" key that you can use in the widget definition for this purpose.

/**
 * Implements hook_facetapi_widgets().
 */
function mymodule_facetapi_widgets() {
  return array(
    'mywidget' => array(
      'handler' => array(
        'label' => t('My Widget'),
        'class' => 'MymoduleFacetapiMywidget',
        'query types' => array('term'),
        'requirements' => array(
          'facetapi_requirement_facet_property' => array('name' => 'myfacetname')
        ),
      ),
    ),
  );
}

What this says is that the widget can only be applied to the facet named "myfacetname".

Hope this helps,
Chris

levialliance’s picture

Category: support » bug
FileSize
2.29 KB
946 bytes

Hi Chris,

The reason for the custom query type is that I am querying an external (very dynamic) data source and then turning those results into solr filters -- given that the widget is so tightly coupled with the query type it didn't make any sense for the query type/widget to be used with anything other than each other. (although thanks for mentioning the 'requirements' attribute of hook_facetapi_widgets(), there is another facet that this will help immensely with).

I've looked at this a bit more now and the issue is unrelated to custom query types: trying to configure any unconfigured facet that supports exactly 1 non-term query type with a non-term widget will always fail even with a built-in query type.

I've attached a basic module that contains the bare minimum necessary code (50 lines) to demonstrate the behaviour: it only defines a custom widget and facet. If you install facetapi_slider (which only supports numeric_range) you can demonstrate the behaviour simply with the 15 lines in demo_facetapi_facet_info().

Steps to reproduce:
- Clean drupal 7.12 install with minimal profile.
- Activate Demo module (requires apachesolr, ctools, facetapi, features)
- Configure demo facet: /admin/config/search/facetapi/apachesolr%40solr/block/demo_facet/edit
- Ensure Demo Widget is selected, click save

Preconditions
Note the 2 preconditions required for the bug to show:
[1] There are no existing settings in the {facetapi} table for that facet [else FacetapiAdapter::getFacetSettingsGlobal() returns those settings instead]
[2] hook_facetapi_facet_info() returns a facet with exactly one non-term query type [any more and facetapi_facet_display_form() will display the facet type dropdown and then the browser will pick the first option so you don't notice that the #default_value for the dropdown is invalid]

Analysis

facetapi_facet_display_form():
$global_settings = $adapter->getFacet($facet)->getSettings();

FacetapiFacet::getSettings():

  $method = ($realm) ? 'getFacetSettings' : 'getFacetSettingsGlobal';
  return $this->adapter->$method($this->facet, $realm);

FacetapiAdapter::getFacetSettingsGlobal():

     $settings->settings = array(
        ...
        'query_type' => 'term',
      );

The root problem is that in FacetapiAdapter::getFacetSettingsGlobal(), the default query type is hardcoded to 'term' irrespective of what the facet actually supports. facetapi_facet_display_form() then hides the query type dropdown because there is only 1 option available, but #default_value is still hardcoded to 'term' so it is impossible to change because the dropown is hidden.

Note that the problem of incorrect defaults applies to 'allowed operators', but there is no validation [so even though the demo facet defines itself as only supporting FACETAPI_OPERATOR_OR, if you look at the serialised facet config data in {facetapi}, it will save with 'and'].

http://drupal.org/node/1347348 may in fact be the same problem (although they didn't give enough information to know)

Fix
Attached is a patch against 7.x-2.x that sets the facet global default query type and operator to the first query type/operator present in the facet definition. (applies without changes to 7.x-1.x too)

Edit: had swapped AND and OR around in operator description

ohthehugemanatee’s picture

Version: 7.x-1.0-rc2 » 7.x-1.0-rc4
Status: Active » Needs review
FileSize
1.07 KB

Bumping this, because I'm experiencing it too. A good example of a widget that doesn't support "term" is the Slider Widget referenced on FacetAPI's module page. This is a correctly defined widget (as far as I can tell!).

Then I try to create a new facet for it, for example:

  $facets['is_age'] = array(
    'name' => 'is_age',
    'label' => t('Minimum Age'),
    'description' => t('Minimum age requirement'),
    'field' => 'is_age',
    'field api name' => 'field_job_min_age',
    'query types' => array('numeric_range'),
    'query type' => 'numeric_range',
    'index_type' => 'integer',
  );

(note I have both the array and singular definition in there, to be doubly sure)

This is an integer data type, numeric_range in Facet parlance. But try and select the Slider widget, and you'll get a form validation error that this widget doesn't support the "Term" query type. that error comes from facetapi.admin.inc, function facetapi_facet_display_form_validate().

function facetapi_facet_display_form_validate($form, &$form_state) {
  // Gets the selected query type and widget.
  $query_type = $form_state['values']['global']['query_type'];
  $widget_name = $form_state['values']['widget'];

  // Loads the widget plugin, makes sure it supports the selected type.
  if ($widget = ctools_get_plugins('facetapi', 'widgets', $widget_name)) {
    $widget_types = drupal_map_assoc($widget['handler']['query types']);
    if (!isset($widget_types[$query_type])) {
      $error = t('The widget does not support the %type query type', array('%type' => $query_type));
      form_set_error('widget', $error);
    }
  }
  else {
    $error = t('Widget %name not valid.', array('%name' => $widget_name));
    form_set_error('widget', $error);
  }
}

It's throwing the error because the widget does not support the query type defined in $form_state['values']['global']['query_type']; , which comes from a hidden field on the form... which doesn't seem to care about the facet definition.

If it is intended to check against a hardcoded value, can someone explain why?

Otherwise, thank you levialliance for a good patch. Attaching a re-roll against the current version (rc4)

levialliance’s picture

Title: It is not possible to create a facet & widget that does not support 'term' query type » Default widget type is hardcoded to 'term'

(Changed title to reflect actual problem)

tahiticlic’s picture

Status: Needs review » Needs work

Doesn't work with latest version of Facetapi (rc4) and facetapi slider on numeric field.

gnucifer’s picture

FileSize
842 bytes

I have the exact same problem implementing a widget for a new query-type. This problem only appers in widgets supporting only one query type (not being term) and/or one operator (not beeing AND). Was going to start a new issue, but will submit my patch, (very similar to other patches in this tread).

Nick_vh’s picture

Add this in your custom module

function hook_ctools_plugin_post_alter(&$widget, &$plugin_type_info) {
  if (!empty($plugin_type_info['hook']) && $plugin_type_info['hook'] == "facetapi_widgets") {
    $widget['handler']['query types'][] = 'your_query_term';
  }
}
gnucifer’s picture

Status: Needs work » Needs review

@Nick_vh I'm not sure if you have misunderstood the problem. The issue occurs when defining new facets supporting one query_type (not term).

In this piece of code in facetapi.admin.inc:

  // Loop through the query types so the key is the same as the value
  $query_types = drupal_map_assoc($facet['query types'], 'check_plain');
  $form['global']['query_type'] = array(
    '#type' => 'select',
    '#access' => count($facet['query types']) > 1,
    '#title' => t('Query type'),
    '#prefix' => '<div class="facetapi-global-setting">',
    '#suffix' => '</div>',
    '#default_value' => $global_settings->settings['query_type'],
    '#options' => $query_types,
    '#description' => t('Select the query type this facet will use.'),
  );

if $query_types has only one element, the form element will be hidden with the default_value hard-coded to "term". So if $query_types contains the single element "my_query_type", it will not be saved, but overriden by "term", $options being ignored by FAPI when not a visible select element.

cpliakas’s picture

I am trying to wrap my head around this. I do agree there is definitely a usability WTF here. There should at least be some JavaScript that doesn't present options that aren't valid.

An issue I see with the Facet API Slider module is that it doesn't implement requirements, so it presents itself as an option for facets that do not support the query type plugin. It is in these cases where I see the phenomenon mentioned above. For cases like number fields which do allow numeric range queries, the user is presented with an option to select the correct query type, although again JavaScript should be changing the values so it doesn't default to something that won't validate.

Thanks for all of the discussions and research around this issue. Look forward to more discussion and a better understanding of the bug.
Chris

cpliakas’s picture

Status: Needs review » Needs work

Marking as needs work, more so in helping me understand the issue fully.

levialliance’s picture

again JavaScript should be changing the values so it doesn't default to something that won't validate

The UI is actually doing the right thing; if there is only one query type available then it is entirely sensible to hide from the user the select box for the query type since they can only choose one value. The problem is that the forms API is being told that certain values are allowed when in fact they are not. Note that the issue is not limited to just the query_type; if you create a facet that only supports FACETAPI_OPERATOR_OR and not FACETAPI_OPERATOR_AND then you will not see an error, but the underlying facet settings in the database will be invalid.

The fundamental problem is that if a facet does not support the FACETAPI_OPERATOR_AND operator, then the default settings should not specify FACETAPI_OPERATOR_AND as the default operator as this is an illegal value. Similarly, if a facet only supports 'numeric_range' as the query type, then 'term' should not be the default query type in the facet settings as this is an illegal value.

The FacetapiAdapter::getFacetSettingsGlobal() function completely ignores the available operators and query types returned from hook_facetapi_facet_info() and insists that FACETAPI_OPERATOR_AND and 'term' will be your defaults even if these were not listed in the facet info hook. If FacetapiAdapter::getFacetSettingsGlobal() was to respect the values returned by hook_facetapi_facet_info() then there would be no UI issues.

The patch attached (recreated against 7.x-2.x git branch) simply changes the hardcoded defaults in FacetapiAdapter::getFacetSettingsGlobal() to choose the first available query type and operator.

Run the demo module at http://drupal.org/node/1443340#comment-5635674 - it demonstrates the problem in 50 lines. Note that demo_facetapi_facet_info() indicates that this facet only supports a query type of 'numeric_range' and an allowed operator of FACETAPI_OPERATOR_OR.

Activate the demo module and reset your DB settings:

DELETE FROM {facetapi} WHERE name LIKE '%demo%';

now edit the facet in the admin UI, saving the facet with the default settings.

SELECT * FROM {facetapi} WHERE name LIKE '%demo%';

The operator is set to 'and' and the query type is 'term' even though these were not among the valid choices according to hook_facetapi_facet_info(). [For some reason the latest version of drupal + facetapi 2.x no longer throws an error, it now silently saves illegal values to the database instead]

cpliakas’s picture

Status: Needs work » Needs review

Thanks for the very detailed explanation! I understand much better what the problem is and have wrapped my head around it. Marking #1443340-11: The default query type setting is hard coded to "term" which causes issues for widgets that don't support "term" queries as needs review.

cpliakas’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
1.1 KB

I totally get it now and can replicate the bug. Patch works as expected. I am refactoring a bit just to highlight exactly why to code is written as such, but marking as RTBC. Thanks for sticking with me on this one.

Chris

cpliakas’s picture

Title: Default widget type is hardcoded to 'term' » The default query type setting is hard coded to "term" which causes issues for widgets that don't support "term" queries
Status: Reviewed & tested by the community » Fixed
gnucifer’s picture

If being really anal about it, shouldn't $facet['allowed operators'] also first be filtered like:

$allowed_operators = array_filter($facet['allowed operators']);

Since a valid value for $facet['allowed operators'] could be:

array(FACETAPI_OPERATOR_AND => FALSE, FACETAPI_OPERATOR_OR => TRUE)

and that would cause the same problem?
Either that or refactor the code so that $facet['allowed operators'] stores the allowed operators as values instead.

cpliakas’s picture

gnucifer,

You are absolutely right, and great catch! If we take $allowed_operators = array_filter($facet['allowed operators']); approach that you mentioned, we can actually replace the reset($facet['allowed operators']); statement with your snippet and read the key() of the captured variable. We don't need reset() because the internal pointer isn't transferred to the array returned by array_filter(). The attached follow-up patch takes this approach.

Thanks for the contribution,
Chris

cpliakas’s picture

This is a small change, so I committed it to all major versions of Facet API giving gnucifer credit.

gnucifer, congrats on becoming the 29th code contributor to Facet API!

cpliakas’s picture

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

cappadona’s picture

Status: Closed (fixed) » Needs review

I know this has been closed for several months now, but I'm still running into this error with facetapi-7.x-1.2 and facetapi_slider-7.x-1.x-dev:

The widget does not support the term query type.

Status: Needs review » Needs work

The last submitted patch, facetapi-global-defaults-followup-1443340-16.patch, failed testing.

cpliakas’s picture

Status: Needs work » Closed (fixed)

Re-closing as fixed. You have to make sure that the correct query type is used, because sliders should work with term query types. They should only work with ranges. If the problem persists, I suggest opening up a new bu report with detailed steps to reproduce.

Thanks,
Chris

fehin’s picture

Status: Closed (fixed) » Active

Hi cpliakas, you said

You have to make sure that the correct query type is used

where do you set the query type? My field is ubercart Sell Price field and it was set by default to decimal in search_api field type.

cpliakas’s picture

Status: Active » Fixed

Let's open a separate issue for this as opposed to re-opening an old one. It helps to make sure we aren't trouble shooting multiple things in one thread.

fehin’s picture

No problem. I have opened another issue.

xeeshangulzar’s picture

Status: Fixed » Needs review
francois.soulard’s picture

Dear,

I still have this error while using facet api and slider widget...

Does anybody have a solution ?

Best regards,

Sebastien M.’s picture

Status: Needs review » Needs work

The last submitted patch, 3: 1443340-3facet-global-defaults-1.patch, failed testing.

surfgatinho’s picture

Any news on this? I still get this error even with the latest dev version of Facet API.
Am using 7.x-1.x-dev of facet slider and the latest dev version of Facet API.

Thanks

serpforge’s picture

May be you can fix it already? This problem has existed for two years.

Thanks

f0ns’s picture

Hi,

I also get the error:

Error message: The widget does not support the term query type

I use Facet API 1.4 with With Facet API Slider 1.0. I tried to apply widget Slider to my field price (Integer) when I get this error. Anyone has a way to quickfix this?

lpeabody’s picture

Version: 7.x-1.0-rc4 » 7.x-1.5

Any update on this? @cpliakas you say this was committed (back in 2012), but I still don't see this reflected in the current codebase, 1.5. Needless to say I'm running into this error. What's the status?

EDIT: Looks like the the settings array was changed to remove the operator and query_type keys. Still, this problem appears to have resurfaced and is causing issues.