首页 > 解决方案 > 如何在 CKAN 的 Web UI 中添加自定义数据集 facet/solr 过滤器?

问题描述

问题:

如何使用 CKAN 的 Web UI从package_searchAPI 端点复制下面的 url 的结果?

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

我想让用户按有资源或没有资源的包(0num_resources或 1 到 * num_resources)进行过滤。

我试过的:

我已经查看并尝试添加一些方面并进行排序。

该构面允许按具有 X 个资源(例如 1)的包进行过滤。排序允许您按资源数量的顺序对所有数据集进行排序,例如首先具有 10 个资源的包,然后是 9 个资源,然后是 8 个,等等...

我尝试使用以下内容复制 API URI

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

如果我添加该fq部分,它也不起作用。该search()操作将抓取num_resources=[1 TO *]并将其附加到 solrfq参数(可以在此处使用日志语句查看)。

但是,在解决此问题后,我发现 CKAN 包控制器的search()操作确实允许您向solr 过滤器选项fq添加值,就像在 API 调用中一样,但它首先将 params 转换为 string num_resources:"[1 TO *]"。这可以很好地获得单个值,但不能获得我所追求的范围。如果我在 API 中使用这个确切的参数(带引号而不是上面的 url/api 端点),我也会得到不正确的结果。

基本信息:

更新:

2018-12-20:

从那以后,我发现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像搜索一样包含在内。

标签: pythonsolrckan

解决方案


根据我上次的更新,似乎解决此问题的最佳方法是通过扩展中的IPackageControllerbefore_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

然后对于 UI,我在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、... 的过滤器选项(或任何对您的设置有意义的内容,例如,如果dataset 有 15 个资源,它显示为一个选项)。

我还对search_form.html片段进行了一些修改,以使构面过滤器显示我想要的方式,但这只是额外的。


推荐阅读