如何在 CKAN 的 Web UI 中添加自定义数据集 facet / solr 过滤器?
How to add custom dataset facet / solr filter in CKAN's Web UI?
问题:
如何使用 CKAN 的网站 UI 从 package_search
API endpoint 中重现下面 url 的结果?
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 个,依此类推...
我尝试用下面的
复制 API URI
https://demo.ckan.org/dataset?num_resources=[1%20TO%20*]
如果我添加 fq
部分,它也不起作用。 search()
操作将获取 num_resources=[1 TO *]
并将其附加到 solr fq
参数 (can be seen here with log statements).
然而,在解决这个问题后,我发现 CKAN 包控制器的 search()
操作确实允许您向 solr filter option fq
添加值,就像在 API 调用中一样,但是它首先将参数转换为字符串 num_resources:"[1 TO *]"
。这可以很好地获取单个值,但不能获取我所追求的范围。如果我将这个确切的参数(带引号而不像上面的 url/api 端点)与 API 一起使用,我也会得到不正确的结果。
一般信息:
- 使用 ckan 2.8.1
- source install, deployed to production 遵循文档但用作我的开发箱
更新:
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
中完成参数的更新实现
事实证明 search index and organization read 的实现方式有很大不同,因此完全相同的实现方式无法工作。额外的参数实际上包含在 q
参数中,而不是像搜索那样 fq
。
根据我的上次更新,解决此问题的最佳方法似乎是通过 IPackageController from an extension and use before_search()
修改搜索参数。
然而,这很好用,如果 CKAN 允许在其主要搜索页面(数据集和组织 ?fq=num_resources:[1 TO *]
并附加到 fq)上传递额外的 fq
过滤器,那就太好了。此外,似乎数据集在将参数分配给 fq
时与组织略有不同。您可以从他们的操作 (dataset search vs organization read) 的这两行中看到这一点。在我的例子中,我决定只处理数据集搜索。
主要作品
# 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、...(或任何对你的设置有意义的东西,例如,如果一个数据集有 15 个资源,它会显示为一个选项)。
我还对 search_form.html
代码片段进行了一些修改,以使分面过滤器显示我想要的方式,但这只是额外的。
问题:
如何使用 CKAN 的网站 UI 从 package_search
API endpoint 中重现下面 url 的结果?
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 个,依此类推...
我尝试用下面的
复制 API URIhttps://demo.ckan.org/dataset?num_resources=[1%20TO%20*]
如果我添加 fq
部分,它也不起作用。 search()
操作将获取 num_resources=[1 TO *]
并将其附加到 solr fq
参数 (can be seen here with log statements).
然而,在解决这个问题后,我发现 CKAN 包控制器的 search()
操作确实允许您向 solr filter option fq
添加值,就像在 API 调用中一样,但是它首先将参数转换为字符串 num_resources:"[1 TO *]"
。这可以很好地获取单个值,但不能获取我所追求的范围。如果我将这个确切的参数(带引号而不像上面的 url/api 端点)与 API 一起使用,我也会得到不正确的结果。
一般信息:
- 使用 ckan 2.8.1
- source install, deployed to production 遵循文档但用作我的开发箱
更新:
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
中完成参数的更新实现事实证明 search index and organization read 的实现方式有很大不同,因此完全相同的实现方式无法工作。额外的参数实际上包含在 q
参数中,而不是像搜索那样 fq
。
根据我的上次更新,解决此问题的最佳方法似乎是通过 IPackageController from an extension and use before_search()
修改搜索参数。
然而,这很好用,如果 CKAN 允许在其主要搜索页面(数据集和组织 ?fq=num_resources:[1 TO *]
并附加到 fq)上传递额外的 fq
过滤器,那就太好了。此外,似乎数据集在将参数分配给 fq
时与组织略有不同。您可以从他们的操作 (dataset search vs organization read) 的这两行中看到这一点。在我的例子中,我决定只处理数据集搜索。
主要作品
# 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、...(或任何对你的设置有意义的东西,例如,如果一个数据集有 15 个资源,它会显示为一个选项)。
我还对 search_form.html
代码片段进行了一些修改,以使分面过滤器显示我想要的方式,但这只是额外的。