From 93e1fad88e939436037ed19e45b23f9f325944c5 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 4 May 2025 09:20:45 +0100 Subject: [PATCH] Make purchase order deterministic Avoids ordering issues when records have the same purchased_at datetime (which is what happens when they're scanned in batches). --- lib/music_library/collection.ex | 4 +++- lib/music_library/records.ex | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/music_library/collection.ex b/lib/music_library/collection.ex index 213cfa02..f3bdfd4f 100644 --- a/lib/music_library/collection.ex +++ b/lib/music_library/collection.ex @@ -5,6 +5,8 @@ defmodule MusicLibrary.Collection do alias MusicLibrary.Records.{Record, SearchIndex} alias MusicLibrary.Repo + import MusicLibrary.Records, only: [order_alphabetically: 0] + def search_records(query, opts \\ []) do limit = Keyword.get(opts, :limit, 20) offset = Keyword.get(opts, :offset, 0) @@ -43,7 +45,7 @@ defmodule MusicLibrary.Collection do q = from r in Record, where: not is_nil(r.purchased_at), - order_by: [desc: r.purchased_at], + order_by: [{:desc, r.purchased_at}, order_alphabetically()], limit: 1, select: ^Records.essential_fields() diff --git a/lib/music_library/records.ex b/lib/music_library/records.ex index 4e8a8a78..7105da37 100644 --- a/lib/music_library/records.ex +++ b/lib/music_library/records.ex @@ -47,6 +47,14 @@ defmodule MusicLibrary.Records do Repo.aggregate(search, :count) end + defmacro order_alphabetically do + quote do + fragment( + "unaccent(json_extract(artists, '$[0].sort_name')) COLLATE NOCASE ASC, unaccent(title) COLLATE NOCASE ASC" + ) + end + end + defp build_search(initial_search, query, order \\ :alphabetical) do {:ok, parsed_query} = SearchParser.parse(query) @@ -54,15 +62,14 @@ defmodule MusicLibrary.Records do case order do :alphabetical -> initial_search - |> order_by( - fragment( - "unaccent(json_extract(artists, '$[0].sort_name')) COLLATE NOCASE ASC, unaccent(title) COLLATE NOCASE ASC" - ) - ) + |> order_by(order_alphabetically()) :purchase -> initial_search - |> order_by([r], {:desc, r.purchased_at}) + |> order_by([r], [ + {:desc, r.purchased_at}, + order_alphabetically() + ]) end Enum.reduce(parsed_query, search_with_order, fn