diff --git a/datasette/templates/table.html b/datasette/templates/table.html
index 65b336b6..1ba61f87 100644
--- a/datasette/templates/table.html
+++ b/datasette/templates/table.html
@@ -104,7 +104,12 @@
{% for facet_info in sorted_facet_results %}
-
{{ facet_info.name }} ✖
+
+ {{ facet_info.name }}
+ {% if facet_hideable(facet_info.name) %}
+ ✖
+ {% endif %}
+
{% for facet_value in facet_info.results %}
- {{ facet_value.label }} {{ "{:,}".format(facet_value.count) }}
diff --git a/datasette/views/table.py b/datasette/views/table.py
index cb5a44f7..ab66b880 100644
--- a/datasette/views/table.py
+++ b/datasette/views/table.py
@@ -537,10 +537,12 @@ class TableView(RowTableShared):
# facets support
FACET_SIZE = 20
+ metadata_facets = table_metadata.get("facets", [])
+ facets = metadata_facets[:]
try:
- facets = request.args["_facet"]
+ facets.extend(request.args["_facet"])
except KeyError:
- facets = table_metadata.get("facets", [])
+ pass
facet_results = {}
for column in facets:
facet_sql = """
@@ -719,6 +721,7 @@ class TableView(RowTableShared):
key=lambda f: (len(f["results"]), f["name"]),
reverse=True
),
+ "facet_hideable": lambda facet: facet not in metadata_facets,
"is_sortable": any(c["sortable"] for c in display_columns),
"path_with_replaced_args": path_with_replaced_args,
"path_with_removed_args": path_with_removed_args,
diff --git a/docs/facets.rst b/docs/facets.rst
index 02ac8d55..51bfc0f5 100644
--- a/docs/facets.rst
+++ b/docs/facets.rst
@@ -3,8 +3,6 @@
Facets
======
-This feature is currently under development, see `#255 `_
-
Datasette facets can be used to add a faceted browse interface to any Datasette table. With facets, tables are displayed along with a summary showing the most common values in specified columns. These values can be selected to further filter the table.
Facets can be specified in two ways: using queryset parameters, or in ``metadata.json`` configuration for the table.
@@ -18,43 +16,72 @@ To turn on faceting for specific columns on a Datasette table view, add one or m
This works for both the HTML interface and the ``.json`` view. When enabled, facets will cause a ``facet_results`` block to be added to the JSON output, looking something like this::
- "facet_results": {
- "state": [
- {
- "value": "CA",
- "count": 10,
- "selected": false,
- "toggle_url": "http://...&state=CA"
- },
- {
- "value": "MI",
- "count": 4,
- "selected": false,
- "toggle_url": "http://...&state=MI"
- }
- ],
- "city": [
- {
- "value": "San Francisco",
- "count": 6,
- "selected": false,
- "toggle_url": "http://...=San+Francisco"
- },
- {
- "value": "Detroit",
- "count": 4,
- "selected": false,
- "toggle_url": "http://...&city=Detroit"
- },
- {
- "value": "Los Angeles",
- "count": 4,
- "selected": false,
- "toggle_url": "http://...=Los+Angeles"
- }
- ]
+ {
+ "state": {
+ "name": "state",
+ "results": [
+ {
+ "value": "CA",
+ "label": "CA",
+ "count": 10,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&state=CA",
+ "selected": false
+ },
+ {
+ "value": "MI",
+ "label": "MI",
+ "count": 4,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&state=MI",
+ "selected": false
+ },
+ {
+ "value": "MC",
+ "label": "MC",
+ "count": 1,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&state=MC",
+ "selected": false
+ }
+ ],
+ "truncated": false
+ }
+ "city_id": {
+ "name": "city_id",
+ "results": [
+ {
+ "value": 1,
+ "label": "San Francisco",
+ "count": 6,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&city_id=1",
+ "selected": false
+ },
+ {
+ "value": 2,
+ "label": "Los Angeles",
+ "count": 4,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&city_id=2",
+ "selected": false
+ },
+ {
+ "value": 3,
+ "label": "Detroit",
+ "count": 4,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&city_id=3",
+ "selected": false
+ },
+ {
+ "value": 4,
+ "label": "Memnonia",
+ "count": 1,
+ "toggle_url": "http://...?_facet=city_id&_facet=state&city_id=4",
+ "selected": false
+ }
+ ],
+ "truncated": false
+ }
}
+If Datasette detects that a column is a foreign key, the ``"label"`` proper will be automatically derived from the detected label column on the referenced table.
+
Facets in metadata.json
-----------------------
@@ -73,3 +100,19 @@ Here's an example that turns on faceting by default for the ``qLegalStatus`` col
}
}
}
+
+Facets defined in this way will always be shown in the interface and returned in the API, regardless of the ``_facet`` arguments passed to the view.
+
+Suggested facets
+----------------
+
+Datasette's table UI will suggest facets for the user to apply, based on the following criteria:
+
+For the currently filtered data are there any columns which, if applied as a facet...
+
+* Will return 20 or less unique options
+* Will return more than one unique option
+* Will return less unique options than the total number of filtered rows
+* And the query used to evaluate this criteria can be completed in under 20ms
+
+That last point is particularly important: Datasette runs a query for every column that is displayed on a page, which could get expensive - so to avoid slow load times it sets a time limit of just 20ms for each of those queries. This means suggested facets are unlikely to appear for tables with millions of records in them.