{"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-902375388", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 902375388, "node_id": "IC_kwDOBm6k_c41ySfc", "user": {"value": 9599, "label": "simonw"}, "created_at": "2021-08-20T02:07:53Z", "updated_at": "2021-08-20T02:07:53Z", "author_association": "OWNER", "body": "I could add these sorting links to the cog menu for any `TEXT` columns.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-902375088", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 902375088, "node_id": "IC_kwDOBm6k_c41ySaw", "user": {"value": 9599, "label": "simonw"}, "created_at": "2021-08-20T02:07:13Z", "updated_at": "2021-08-20T02:07:26Z", "author_association": "OWNER", "body": "Maybe `?_sort_numeric=col` and `?_sort_numeric_desc=col` would be better here.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709575818", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709575818, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTU3NTgxOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T20:35:03Z", "updated_at": "2020-10-15T20:35:03Z", "author_association": "OWNER", "body": "Prototype so far:\r\n```diff\r\ndiff --git a/datasette/views/table.py b/datasette/views/table.py\r\nindex ea11a51..d61f8bd 100644\r\n--- a/datasette/views/table.py\r\n+++ b/datasette/views/table.py\r\n@@ -497,17 +497,32 @@ class TableView(RowTableShared):\r\n if sort and sort_desc:\r\n raise DatasetteError(\"Cannot use _sort and _sort_desc at the same time\")\r\n \r\n+ def parse_sort(sort):\r\n+ if \"~\" in sort:\r\n+ if sort.endswith(\"~default\"):\r\n+ col = sort.rsplit(\"~\", 1)[0]\r\n+ return col, escape_sqlite(col)\r\n+ elif sort.endswith(\"~numeric\"):\r\n+ col = sort.rsplit(\"~\", 1)[0]\r\n+ return col, \"cast(nullif({}, '') as real)\".format(escape_sqlite(col))\r\n+ else:\r\n+ return sort, escape_sqlite(sort)\r\n+ else:\r\n+ return sort, escape_sqlite(sort)\r\n+\r\n if sort:\r\n- if sort not in sortable_columns:\r\n- raise DatasetteError(\"Cannot sort table by {}\".format(sort))\r\n+ sort_column, sort_clause = parse_sort(sort)\r\n+ if sort_column not in sortable_columns:\r\n+ raise DatasetteError(\"Cannot sort table by {}\".format(sort_column))\r\n \r\n- order_by = escape_sqlite(sort)\r\n+ order_by = sort_clause\r\n \r\n if sort_desc:\r\n- if sort_desc not in sortable_columns:\r\n- raise DatasetteError(\"Cannot sort table by {}\".format(sort_desc))\r\n+ sort_column, sort_clause = parse_sort(sort_desc)\r\n+ if sort_column not in sortable_columns:\r\n+ raise DatasetteError(\"Cannot sort table by {}\".format(sort_column))\r\n \r\n- order_by = \"{} desc\".format(escape_sqlite(sort_desc))\r\n+ order_by = \"{} desc\".format(sort_clause)\r\n \r\n from_sql = \"from {table_name} {where}\".format(\r\n table_name=escape_sqlite(table),\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709572425", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709572425, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTU3MjQyNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T20:28:18Z", "updated_at": "2020-10-15T20:28:18Z", "author_association": "OWNER", "body": "Also need to rethink this template logic that decides if to show a column as sorted or not:\r\nhttps://github.com/simonw/datasette/blob/4f7c0ebd85ccd8c1853d7aa0147628f7c1b749cc/datasette/templates/_table.html#L10-L14", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709571143", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709571143, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTU3MTE0Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T20:25:35Z", "updated_at": "2020-10-15T20:25:35Z", "author_association": "OWNER", "body": "`cast(nullif(colname, '') as real)` can fix this - it will treat `''` the same as `null`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709569951", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709569951, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTU2OTk1MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T20:23:02Z", "updated_at": "2020-10-15T20:23:02Z", "author_association": "OWNER", "body": "Something to watch out for: `\"\"` empty strings cast to `0.0`:\r\n\r\n`select cast(\"100\" as real), \"100\", cast(null as real), cast(\"\" as real)`\r\n\r\ncast(\"100\" as real) | \"100\" | cast(null as real) | cast(\"\" as real)\r\n-- | -- | -- | --\r\n100.0 | 100 | \u00a0 | 0.0\r\n\r\nhttps://latest.datasette.io/fixtures?sql=select+cast%28%22100%22+as+real%29%2C+%22100%22%2C+cast%28null+as+real%29%2C+cast%28%22%22+as+real%29", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709562940", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709562940, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTU2Mjk0MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T20:08:16Z", "updated_at": "2020-10-15T20:08:16Z", "author_association": "OWNER", "body": "Relevant code: https://github.com/simonw/datasette/blob/4f7c0ebd85ccd8c1853d7aa0147628f7c1b749cc/datasette/views/table.py#L485-L510", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709546976", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709546976, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTU0Njk3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T19:35:55Z", "updated_at": "2020-10-15T19:36:38Z", "author_association": "OWNER", "body": "Much easier solution: if the suffix is `~numeric` then treat it as the column name sorted numerically. If the suffix is missing OR the suffix is `~default`, sort without casting. Only add the `~default` suffix if the column name itself contains at least one `~` symbol.\r\n\r\nUsing `~` because it doesn't need to be URL-encoded.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709539257", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709539257, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTUzOTI1Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T19:19:29Z", "updated_at": "2020-10-15T19:34:07Z", "author_association": "OWNER", "body": "Urgh this isn't going to work. `%7E~%7E` gets decoded as `~~~` so I wouldn't be able to tell the difference.\r\n\r\nI could use double-percentage-encoding here instead. I feel like there's a simpler solution that I'm missing (and that may well be in use within Datasette already, I'm not doing great thinking this morning).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709534197", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709534197, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTUzNDE5Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T19:08:53Z", "updated_at": "2020-10-15T19:17:55Z", "author_association": "OWNER", "body": "Even better solution: use URL encoding in the parameter details. This is consistent with how `?_next=` tokens work, e.g. `?_next=0.291861560261786%2Ce%2Cj`.\r\n\r\nSo the format can be:\r\n\r\n- `mycolumn`\r\n- `urlencoded-mycolumn$castname`\r\n\r\nFor most columns this will look like: `?_sort=score$numeric`\r\n\r\nFor columns with a `$` in their name it will be `?_sort=score%24hasdollar$numeric`\r\n\r\nProblem: both `$` and `,` are usually URL encoded anyway. I need a character which isn't encoded by default, so that I can use its encoded form to show it is part of the column name and its un-encoded form to split the cast indicator.\r\n\r\n`_` is a candidate here - not encoded by default, but can be encoded as `%5F`.\r\n\r\nThe other unreserved non-alphanumeric characters are `-`, `.`, `_`, `~`.\r\n\r\nOf these, `~` is least likely to show up in a column name. So I'll use that.\r\n\r\n- `mycolumn`\r\n- `mycolumn~numeric`\r\n- `mycolumn%7Ewith%7Etildes~numeric`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709532369", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709532369, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTUzMjM2OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T19:05:07Z", "updated_at": "2020-10-15T19:07:35Z", "author_association": "OWNER", "body": "Simpler option: `?_sort=` column values look like this:\r\n\r\n- `mycolumn` - for sort by column\r\n- `mycolumn$numeric` - for sort by column after cast to float\r\n- `mycolumn$morename$default` - for the edge case where the column name itself contains a $ symbol", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 657572753, "label": "?sort=colname~numeric to sort by by column cast to real"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/894#issuecomment-709531343", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/894", "id": 709531343, "node_id": "MDEyOklzc3VlQ29tbWVudDcwOTUzMTM0Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-15T19:03:12Z", "updated_at": "2020-10-15T19:03:12Z", "author_association": "OWNER", "body": "The Sort by `