@@ -70,6 +70,63 @@ defmodule Plausible.Segments do
7070 { :ok , Repo . all ( query ) }
7171 end
7272
73+ def search_by_name ( % Plausible.Site { } = site , name , opts ) do
74+ type = Keyword . fetch! ( opts , :type )
75+ fields = Keyword . get ( opts , :fields , [ :id , :name ] )
76+
77+ name_empty? = is_nil ( name ) or ( is_binary ( name ) and String . trim ( name ) == "" )
78+
79+ base_query =
80+ from ( segment in Segment ,
81+ where: segment . site_id == ^ site . id ,
82+ where: segment . type == ^ type ,
83+ limit: 20
84+ )
85+
86+ query =
87+ if name_empty? do
88+ from ( [ segment ] in base_query ,
89+ select: ^ fields ,
90+ order_by: [ desc: segment . updated_at ]
91+ )
92+ else
93+ from ( [ segment ] in base_query ,
94+ select: % {
95+ id: segment . id ,
96+ name: segment . name ,
97+ match_rank:
98+ fragment (
99+ "CASE
100+ WHEN lower(?) = lower(?) THEN 0 -- exact match
101+ WHEN lower(?) LIKE lower(?) || '%' THEN 1 -- starts with
102+ WHEN lower(?) LIKE '% ' || lower(?) || '%' THEN 2 -- after a space
103+ WHEN lower(?) LIKE ? THEN 3 -- anywhere
104+ END AS match_rank" ,
105+ segment . name ,
106+ ^ name ,
107+ segment . name ,
108+ ^ name ,
109+ segment . name ,
110+ ^ name ,
111+ segment . name ,
112+ ^ "%name%"
113+ ) ,
114+ pos:
115+ fragment (
116+ "position(lower(?) IN lower(?)) AS pos" ,
117+ segment . name ,
118+ ^ name
119+ ) ,
120+ len_diff: fragment ( "abs(length(?) - length(?)) AS len_diff" , segment . name , ^ name )
121+ } ,
122+ where: fragment ( "? ilike ?" , segment . name , ^ "%#{ name } %" ) ,
123+ order_by: fragment ( "match_rank asc, pos asc, len_diff asc, updated_at desc" )
124+ )
125+ end
126+
127+ { :ok , Repo . all ( query ) }
128+ end
129+
73130 @ spec get_one ( pos_integer ( ) , Plausible.Site . t ( ) , atom ( ) , pos_integer ( ) | nil ) ::
74131 { :ok , Segment . t ( ) }
75132 | error_not_enough_permissions ( )
0 commit comments