?_facet_size=100 parameter, closes #1332

This commit is contained in:
Simon Willison 2021-05-22 17:34:33 -07:00
commit 9789b94da4
5 changed files with 73 additions and 4 deletions

View file

@ -101,6 +101,14 @@ class Facet:
# [('_foo', 'bar'), ('_foo', '2'), ('empty', '')]
return urllib.parse.parse_qsl(self.request.query_string, keep_blank_values=True)
def get_facet_size(self):
facet_size = self.ds.setting("default_facet_size")
max_returned_rows = self.ds.setting("max_returned_rows")
custom_facet_size = self.request.args.get("_facet_size")
if custom_facet_size and custom_facet_size.isdigit():
facet_size = int(custom_facet_size)
return min(facet_size, max_returned_rows)
async def suggest(self):
return []
@ -136,7 +144,7 @@ class ColumnFacet(Facet):
async def suggest(self):
row_count = await self.get_row_count()
columns = await self.get_columns(self.sql, self.params)
facet_size = self.ds.setting("default_facet_size")
facet_size = self.get_facet_size()
suggested_facets = []
already_enabled = [c["config"]["simple"] for c in self.get_configs()]
for column in columns:
@ -186,7 +194,7 @@ class ColumnFacet(Facet):
qs_pairs = self.get_querystring_pairs()
facet_size = self.ds.setting("default_facet_size")
facet_size = self.get_facet_size()
for source_and_config in self.get_configs():
config = source_and_config["config"]
source = source_and_config["source"]
@ -338,7 +346,7 @@ class ArrayFacet(Facet):
facet_results = {}
facets_timed_out = []
facet_size = self.ds.setting("default_facet_size")
facet_size = self.get_facet_size()
for source_and_config in self.get_configs():
config = source_and_config["config"]
source = source_and_config["source"]
@ -449,7 +457,7 @@ class DateFacet(Facet):
facet_results = {}
facets_timed_out = []
args = dict(self.get_querystring_pairs())
facet_size = self.ds.setting("default_facet_size")
facet_size = self.get_facet_size()
for source_and_config in self.get_configs():
config = source_and_config["config"]
source = source_and_config["source"]

View file

@ -84,6 +84,8 @@ This works for both the HTML interface and the ``.json`` view. When enabled, fac
If Datasette detects that a column is a foreign key, the ``"label"`` property will be automatically derived from the detected label column on the referenced table.
The default number of facet results returned is 30, controlled by the :ref:`setting_default_facet_size` setting. You can increase this on an individual page by adding ``?_facet_size=100`` to the query string, up to a maximum of :ref:`setting_max_returned_rows` (which defaults to 1000).
Facets in metadata.json
-----------------------

View file

@ -371,6 +371,12 @@ Special table arguments
Pagination by continuation token - pass the token that was returned in the
``"next"`` property by the previous page.
``?_facet=column``
Facet by column. Can be applied multiple times, see :ref:`facets`. Only works on the default JSON output, not on any of the custom shapes.
``?_facet_size=100``
Increase the number of facet results returned for each facet.
``?_trace=1``
Turns on tracing for this page: SQL queries executed during the request will
be gathered and included in the response, either in a new ``"_traces"`` key

View file

@ -619,6 +619,7 @@ Each Facet subclass implements a new type of facet operation. The class should l
# using self.sql and self.params as the starting point
facet_results = {}
facets_timed_out = []
facet_size = self.get_facet_size()
# Do some calculations here...
for column in columns_selected_for_facet:
try:

View file

@ -347,3 +347,55 @@ async def test_json_array_with_blanks_and_nulls():
"toggle_url": "http://localhost/test_json_array/foo.json?_facet_array=json_column",
}
]
@pytest.mark.asyncio
async def test_facet_size():
ds = Datasette([], memory=True, config={"max_returned_rows": 50})
db = ds.add_database(Database(ds, memory_name="test_facet_size"))
await db.execute_write(
"create table neighbourhoods(city text, neighbourhood text)", block=True
)
for i in range(1, 51):
for j in range(1, 4):
await db.execute_write(
"insert into neighbourhoods (city, neighbourhood) values (?, ?)",
["City {}".format(i), "Neighbourhood {}".format(j)],
block=True,
)
response = await ds.client.get("/test_facet_size/neighbourhoods.json")
data = response.json()
assert data["suggested_facets"] == [
{
"name": "neighbourhood",
"toggle_url": "http://localhost/test_facet_size/neighbourhoods.json?_facet=neighbourhood",
}
]
# Bump up _facet_size= to suggest city too
response2 = await ds.client.get(
"/test_facet_size/neighbourhoods.json?_facet_size=50"
)
data2 = response2.json()
assert sorted(data2["suggested_facets"], key=lambda f: f["name"]) == [
{
"name": "city",
"toggle_url": "http://localhost/test_facet_size/neighbourhoods.json?_facet_size=50&_facet=city",
},
{
"name": "neighbourhood",
"toggle_url": "http://localhost/test_facet_size/neighbourhoods.json?_facet_size=50&_facet=neighbourhood",
},
]
# Facet by city should return expected number of results
response3 = await ds.client.get(
"/test_facet_size/neighbourhoods.json?_facet_size=50&_facet=city"
)
data3 = response3.json()
assert len(data3["facet_results"]["city"]["results"]) == 50
# Reduce max_returned_rows and check that it's respected
ds._settings["max_returned_rows"] = 20
response4 = await ds.client.get(
"/test_facet_size/neighbourhoods.json?_facet_size=50&_facet=city"
)
data4 = response4.json()
assert len(data4["facet_results"]["city"]["results"]) == 20