33# SPDX-License-Identifier: MIT
44
55defmodule AshPostgres.CustomIndex do
6- @ moduledoc "Represents a custom index on the table backing a resource"
6+ @ moduledoc """
7+ Represents a custom index on the table backing a resource.
8+
9+ Each entry in `fields` can be an atom, string, or a tuple with an order and a field.
10+ """
711 @ fields [
812 :table ,
913 :fields ,
@@ -26,8 +30,19 @@ defmodule AshPostgres.CustomIndex do
2630
2731 @ schema [
2832 fields: [
29- type: { :wrap_list , { :or , [ :atom , :string ] } } ,
30- doc: "The fields to include in the index."
33+ type:
34+ { :wrap_list ,
35+ { :or ,
36+ [
37+ :atom ,
38+ :string ,
39+ { :tuple , [ { :one_of , [ :asc , :desc ] } , { :or , [ :atom , :string ] } ] }
40+ ] } } ,
41+ doc: """
42+ The fields to include in the index.
43+
44+ Each entry can be an atom, string, or a tuple with an order using :desc or :asc and a field.
45+ """
3146 ] ,
3247 error_fields: [
3348 type: { :list , :atom } ,
@@ -83,6 +98,41 @@ defmodule AshPostgres.CustomIndex do
8398
8499 def schema , do: @ schema
85100
101+ def column_name ( field ) when is_atom ( field ) or is_binary ( field ) , do: field
102+
103+ def column_name ( { order , field } )
104+ when order in [ :asc , :desc ] and ( is_atom ( field ) or is_binary ( field ) ) ,
105+ do: field
106+
107+ def field_to_snapshot ( field ) when is_atom ( field ) , do: % { type: "atom" , value: field }
108+ def field_to_snapshot ( field ) when is_binary ( field ) , do: % { type: "string" , value: field }
109+
110+ def field_to_snapshot ( { order , field } )
111+ when order in [ :asc , :desc ] and ( is_atom ( field ) or is_binary ( field ) ) do
112+ % {
113+ type: "directed" ,
114+ order: to_string ( order ) ,
115+ value: if ( is_atom ( field ) , do: to_string ( field ) , else: field )
116+ }
117+ end
118+
119+ def field_comparison_key ( field ) when is_atom ( field ) , do: to_string ( field )
120+ def field_comparison_key ( field ) when is_binary ( field ) , do: field
121+
122+ def field_comparison_key ( { order , field } )
123+ when order in [ :asc , :desc ] and ( is_atom ( field ) or is_binary ( field ) ) do
124+ "#{ order } :#{ field } "
125+ end
126+
127+ @ doc false
128+ def field_for_migration ( field ) when is_atom ( field ) , do: inspect ( field )
129+ def field_for_migration ( field ) when is_binary ( field ) , do: inspect ( field )
130+
131+ def field_for_migration ( { order , field } )
132+ when order in [ :asc , :desc ] and ( is_atom ( field ) or is_binary ( field ) ) do
133+ "#{ order } : #{ inspect ( field ) } "
134+ end
135+
86136 def transform ( index ) do
87137 with { :ok , index } <- set_name ( index ) do
88138 set_error_fields ( index )
@@ -99,11 +149,13 @@ defmodule AshPostgres.CustomIndex do
99149 index
100150 | error_fields:
101151 Enum . flat_map ( index . fields , fn field ->
102- if Regex . match? ( ~r/ ^[0-9a-zA-Z_]+$/ , to_string ( field ) ) do
103- if is_binary ( field ) do
104- [ String . to_atom ( field ) ]
152+ column = column_name ( field )
153+
154+ if Regex . match? ( ~r/ ^[0-9a-zA-Z_]+$/ , to_string ( column ) ) do
155+ if is_binary ( column ) do
156+ [ String . to_atom ( column ) ]
105157 else
106- [ field ]
158+ [ column ]
107159 end
108160 else
109161 [ ]
@@ -125,7 +177,8 @@ defmodule AshPostgres.CustomIndex do
125177
126178 mismatched_field =
127179 Enum . find ( index . fields , fn field ->
128- ! Regex . match? ( ~r/ ^[0-9a-zA-Z_]+$/ , to_string ( field ) )
180+ column = column_name ( field )
181+ ! Regex . match? ( ~r/ ^[0-9a-zA-Z_]+$/ , to_string ( column ) )
129182 end ) ->
130183 { :error ,
131184 """
@@ -149,7 +202,7 @@ defmodule AshPostgres.CustomIndex do
149202
150203 # sobelow_skip ["DOS.StringToAtom"]
151204 def name ( table , % { fields: fields } ) do
152- [ table , fields , "index" ]
205+ [ table , Enum . map ( fields , & column_name / 1 ) , "index" ]
153206 |> List . flatten ( )
154207 |> Enum . map ( & to_string ( & 1 ) )
155208 |> Enum . map ( & String . replace ( & 1 , ~r" [^\w _]" , "_" ) )
0 commit comments