@@ -9,11 +9,8 @@ use entity::signature_metadata_mapping;
99use entity:: signature_metadata_mapping:: Model ;
1010use sea_orm:: ActiveValue :: Set ;
1111use sea_orm:: prelude:: Uuid ;
12- use sea_orm:: sea_query:: Expr ;
13- use sea_orm:: {
14- ActiveModelTrait , ColumnTrait , DbConn , DbErr , EntityTrait , IntoActiveModel , QueryFilter ,
15- TryIntoModel ,
16- } ;
12+ use sea_orm:: sea_query:: { Expr , OnConflict } ;
13+ use sea_orm:: { ColumnTrait , DbConn , DbErr , EntityTrait , QueryFilter } ;
1714
1815/// Builder input for [`create_or_update_signature_metadata_mapping`]. Exactly one of
1916/// `company_id`, `game_id`, `platform_id` should be set.
@@ -119,50 +116,70 @@ pub async fn find_signature_metadata_mapping_by_platform_game_company_and_provid
119116 . await
120117}
121118
122- /// Upsert a signature metadata mapping. Looks up the existing row by its identifying tuple,
123- /// updates it in place if found, or inserts a new row otherwise. Touches `updated_at` on every call.
119+ /// `matched_name` and `matched_year` use COALESCE on conflict so a follow-up
120+ /// call that doesn't carry those fields (e.g. a hash-only retry) preserves
121+ /// values written by an earlier hit. Every other column is overwritten.
124122pub async fn create_or_update_signature_metadata_mapping (
125123 input : SignatureMetadataMappingInput ,
126124 db_conn : & DbConn ,
127125) -> Result < Model , DbErr > {
128- let signature_metadata_mapping =
129- find_signature_metadata_mapping_by_platform_game_company_and_provider (
130- input. platform_id ,
131- input. game_id ,
132- input. company_id ,
133- input. provider ,
134- db_conn,
135- )
136- . await ?;
137-
138- let mut active_model = if let Some ( signature_metadata_mapping) = signature_metadata_mapping {
139- signature_metadata_mapping. into_active_model ( )
140- } else {
141- signature_metadata_mapping:: ActiveModel {
142- ..Default :: default ( )
126+ let target_col = match ( input. game_id , input. platform_id , input. company_id ) {
127+ ( Some ( _) , None , None ) => signature_metadata_mapping:: Column :: GameId ,
128+ ( None , Some ( _) , None ) => signature_metadata_mapping:: Column :: PlatformId ,
129+ ( None , None , Some ( _) ) => signature_metadata_mapping:: Column :: CompanyId ,
130+ _ => {
131+ return Err ( DbErr :: Custom (
132+ "SignatureMetadataMappingInput requires exactly one of game_id, platform_id, or company_id" . to_string ( ) ,
133+ ) ) ;
143134 }
144135 } ;
145136
146- active_model. platform_id = Set ( input. platform_id ) ;
147- active_model. game_id = Set ( input. game_id ) ;
148- active_model. company_id = Set ( input. company_id ) ;
149- active_model. provider = Set ( input. provider ) ;
150- active_model. provider_id = Set ( input. provider_id ) ;
151- active_model. match_type = Set ( input. match_type ) ;
152- active_model. manual_match_type = Set ( input. manual_match_type ) ;
153- active_model. failed_match_reason = Set ( input. failed_match_reason ) ;
154- active_model. comment = Set ( input. comment ) ;
155- active_model. automatic_match_reason = Set ( input. automatic_match_reason ) ;
156- active_model. manually_matched_by = Set ( input. manually_matched_by ) ;
157- if input. matched_name . is_some ( ) {
158- active_model. matched_name = Set ( input. matched_name ) ;
159- }
160- if input. matched_year . is_some ( ) {
161- active_model. matched_year = Set ( input. matched_year ) ;
162- }
163- active_model. updated_at = Set ( Utc :: now ( ) . fixed_offset ( ) ) ;
137+ let active_model = signature_metadata_mapping:: ActiveModel {
138+ platform_id : Set ( input. platform_id ) ,
139+ game_id : Set ( input. game_id ) ,
140+ company_id : Set ( input. company_id ) ,
141+ provider : Set ( input. provider ) ,
142+ provider_id : Set ( input. provider_id ) ,
143+ match_type : Set ( input. match_type ) ,
144+ manual_match_type : Set ( input. manual_match_type ) ,
145+ failed_match_reason : Set ( input. failed_match_reason ) ,
146+ comment : Set ( input. comment ) ,
147+ automatic_match_reason : Set ( input. automatic_match_reason ) ,
148+ manually_matched_by : Set ( input. manually_matched_by ) ,
149+ matched_name : Set ( input. matched_name ) ,
150+ matched_year : Set ( input. matched_year ) ,
151+ updated_at : Set ( Utc :: now ( ) . fixed_offset ( ) ) ,
152+ ..Default :: default ( )
153+ } ;
164154
165- active_model = active_model. save ( db_conn) . await ?;
155+ let on_conflict =
156+ OnConflict :: columns ( [ target_col, signature_metadata_mapping:: Column :: Provider ] )
157+ . update_columns ( [
158+ signature_metadata_mapping:: Column :: ProviderId ,
159+ signature_metadata_mapping:: Column :: MatchType ,
160+ signature_metadata_mapping:: Column :: ManualMatchType ,
161+ signature_metadata_mapping:: Column :: FailedMatchReason ,
162+ signature_metadata_mapping:: Column :: Comment ,
163+ signature_metadata_mapping:: Column :: AutomaticMatchReason ,
164+ signature_metadata_mapping:: Column :: ManuallyMatchedBy ,
165+ signature_metadata_mapping:: Column :: UpdatedAt ,
166+ ] )
167+ . value (
168+ signature_metadata_mapping:: Column :: MatchedName ,
169+ Expr :: cust (
170+ "COALESCE(EXCLUDED.matched_name, signature_metadata_mapping.matched_name)" ,
171+ ) ,
172+ )
173+ . value (
174+ signature_metadata_mapping:: Column :: MatchedYear ,
175+ Expr :: cust (
176+ "COALESCE(EXCLUDED.matched_year, signature_metadata_mapping.matched_year)" ,
177+ ) ,
178+ )
179+ . to_owned ( ) ;
166180
167- active_model. try_into_model ( )
181+ signature_metadata_mapping:: Entity :: insert ( active_model)
182+ . on_conflict ( on_conflict)
183+ . exec_with_returning ( db_conn)
184+ . await
168185}
0 commit comments