Как добавить настраиваемый фильтр фасета/солра набора данных в веб-интерфейс CKAN?

Вопрос:

Как я могу воспроизвести результат из приведенного ниже URL-адреса из package_search конечная точка API с веб-интерфейсом CKAN?

https://demo.ckan.org/api/3/action/package_search?fq=num_resources:[1%20TO%20*]

Я хочу, чтобы пользователь мог фильтровать пакеты с ресурсами или без (от 0 num_resources или от 1 до * num_resources).

Что я пробовал:

Я уже посмотрел и попытался добавить некоторые аспекты и сортировку.

Фасет позволяет фильтровать пакеты с количеством ресурсов X (например, 1). Сортировка позволяет сортировать все наборы данных по порядку количества ресурсов, например. Сначала пакеты с 10 ресурсами, затем с 9 ресурсами, затем с 8 и т. д.

Я попытался продублировать URI API с помощью приведенного ниже

https://demo.ckan.org/dataset?num_resources=[1%20TO%20*]

Если я добавлю часть fq, это тоже не сработает. Действие search() захватит num_resources=[1 TO *] и добавит его к параметру solr fq (можно увидеть здесь с операторами журнала).

Однако после устранения неполадок я обнаружил, что действие search() контроллера пакета CKAN позволяет вам добавлять значения в package.py#L217" rel="nofollow noreferrer">параметр фильтра solr fq как и в вызове API, НО сначала он преобразует параметры в строку num_resources:"[1 TO *]". Это отлично работает, чтобы получить одно значение, но не диапазон, который мне нужен. Если я использую этот точный параметр (с кавычками, а не как указанную выше конечную точку url/api) с API, я также получаю неверный результат.

Общая информация:

ОБНОВЛЕНИЯ:

20.12.2018:

С тех пор я обнаружил, что q=num_resources:[1%20TO%20*] в качестве строки запроса работает, поскольку это не экранируется в действии search(). Параметры q извлекаются перед кодированием.

Однако это не идеально, поскольку обновляет ввод поиска и переопределяет любые существующие строки запроса, если только вы не добавляете к строке запроса, а добавление этого в фильтры до сих пор является проблемой.

# I've switched spaces to + to help with readability.
https://demo.ckan.org/dataset?q=num_resources:[1+TO+*]+Test

2018-12-21:

Работа над реализацией IPackageController изнутри расширения. Это кажется правильным способом сделать это, учитывая обстоятельства. Добавлю реализацию после.

Тем не менее, я чувствую, что обновленную реализацию параметров можно было бы выполнить в файле ckan package_search

Получается индекс поиска и чтение организации реализованы несколько иначе, поэтому точно такая же реализация не будет работать. дополнительные параметры фактически включены как часть q параметров, а не fq, как поиск.


person user3366016    schedule 19.12.2018    source источник
comment
Даже не плюс? Посмотрите на красивую структуру вопросов, лол... в конце концов я заставлю это работать. Может потребоваться PR для search() плана действий / представлений ядра CKAN, хотя для поиска параметров fq.   -  person user3366016    schedule 21.12.2018


Ответы (1)


Согласно моему последнему обновлению, лучший способ приблизиться к этому — через IPackageController из расширения и используйте before_search() для изменения параметров поиска.

Однако это работает хорошо, было бы неплохо, если бы CKAN разрешил передавать дополнительные fq фильтры на своих основных страницах поиска (набор данных и организация ?fq=num_resources:[1 TO *] и добавление к fq). Кроме того, кажется, что наборы данных немного отличаются в назначении параметров для fq, чем организации. Вы можете увидеть это в этих двух строках их действий (поиск набора данных по сравнению с чтением организации). В моем случае я решил обрабатывать это только для поиска набора данных.

Основные части

# In plugin class implement IPackageController.
class CustomPlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm):
    ...
    plugins.implements(plugins.IPackageController)


    # Lower in plugin class add needed functions from IPackageController, 
    # I decided to add them all and leave them untouched to avoid various 
    # errors I was getting.
    def before_search(self, search_params):
        u'''Extensions will receive a dictionary with the query parameters,
        and should return a modified (or not) version of it.

        Basically go over all search_params and look for values that contain my 
        additional filter and remove the double quotes. All fq values are a 
        single string, so do exact match to not remove other escaping / quotes.

        In query string in URL if you do `?num_resources=0` you get datasets with no
        resources (the opposite of this query string).
        '''
        for (param, value) in search_params.items():
            if param == 'fq' and 'num_resources:"[' in value:
                v = value.replace('num_resources:"[1 TO *]"', 'num_resources:[1 TO *]')
                search_params[param] = v

        return search_params

    def after_search(self, search_results, search_params):
        return search_results

    def before_index(self, pkg_dict):
        return pkg_dict

    def before_view(self, pkg_dict):
        return pkg_dict

    def read(self, entity):
        return entity

    def create(self, entity):
        return entity

    def edit(self, entity):
        return entity

    def delete(self, entity):
        return entity

    def after_create(self, context, pkg_dict):
        return pkg_dict

    def after_update(self, context, pkg_dict):
        return pkg_dict

    def after_delete(self, context, pkg_dict):
        return pkg_dict

    def after_show(self, context, pkg_dict):
        return pkg_dict

Затем для пользовательского интерфейса я добавил пользовательский список фасетов в шаблон search.html.

<div>
  <section class="module module-narrow module-shallow">
    {% block facet_list_heading %}
      <h2 class="module-heading">
        <i class="fa fa-filter"></i>
        {% set title = 'Resources (data)' %}
        {{ title }}
      </h2>
    {% endblock %}
    {% block facet_list_items %}
      {% set title = 'Has Resources (data)' %}
      <nav>
        <ul class="{{ nav_class or 'list-unstyled nav nav-simple nav-facet' }}">
          {% set href = h.remove_url_param('num_resources',
                                           extras=extras,
                                           alternative_url=alternative_url) 
                        if c.fields_grouped['num_resources'] 
                        else h.add_url_param(new_params={'num_resources': '[1 TO *]' },
                                            alternative_url=alternative_url) %}
          <li class="{{ nav_item_class or 'nav-item' }}{% if c.fields_grouped['num_resources'] %} active{% endif %}">
            <a href="{{ href }}" title="{{ title }}">
              <span>{{ title }}</span>
            </a>
          </li>
        </ul>
      </nav>
    {% endblock %}
  </section>
</div>

Делая это таким образом, он не добавляет новый фасет с помощью IFacets, поскольку это добавляет список фасетов для num_resources, который дает параметры фильтра 0, 1, 2, 3, ... (или что-то другое, что имеет смысл для вашей установки, например, если набор данных имеет 15 ресурсов, он показывает это как вариант).

Я также внес некоторые изменения во фрагмент search_form.html, чтобы фасетный фильтр отображал то, что я хотел, но это было просто дополнительно.

person user3366016    schedule 16.01.2019