2024-04-29 00:17:15 +02:00
# frozen_string_literal: true
2018-07-31 12:03:01 +02:00
class DossierSearchService
2021-05-19 14:52:54 +02:00
def self . matching_dossiers ( dossiers , search_terms , with_annotations = false )
if dossiers . nil?
[ ]
else
dossier_by_exact_id ( dossiers , search_terms )
. presence || dossier_by_full_text ( dossiers , search_terms , with_annotations )
end
2018-07-31 12:03:01 +02:00
end
2020-04-03 16:07:18 +02:00
def self . matching_dossiers_for_user ( search_terms , user )
dossier_by_exact_id_for_user ( search_terms , user )
2023-07-26 17:51:04 +02:00
. presence || dossier_by_full_text_for_user ( search_terms , Dossier . includes ( :procedure ) . where ( id : user . dossiers . ids + user . dossiers_invites . ids ) )
2020-04-03 16:07:18 +02:00
end
2018-07-31 12:03:01 +02:00
private
2021-05-19 14:52:54 +02:00
def self . dossier_by_exact_id ( dossiers , search_terms )
2018-07-31 14:29:37 +02:00
id = search_terms . to_i
2019-08-06 11:02:54 +02:00
if id != 0 && id_compatible? ( id ) # Sometimes instructeur is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this.
2022-03-09 10:27:43 +01:00
dossiers . visible_by_administration . where ( id : id ) . ids
2018-07-31 14:54:55 +02:00
else
2021-05-19 14:52:54 +02:00
[ ]
2018-07-31 14:29:37 +02:00
end
end
2021-05-19 14:52:54 +02:00
def self . dossier_by_full_text ( dossiers , search_terms , with_annotations )
2021-04-29 09:33:04 +02:00
ts_vector = " to_tsvector('french', #{ with_annotations ? 'dossiers.search_terms || dossiers.private_search_terms' : 'dossiers.search_terms' } ) "
ts_query = " to_tsquery('french', #{ Dossier . connection . quote ( to_tsquery ( search_terms ) ) } ) "
2021-05-19 14:52:54 +02:00
dossiers
2022-03-09 10:27:43 +01:00
. visible_by_administration
2021-04-29 09:33:04 +02:00
. where ( " #{ ts_vector } @@ #{ ts_query } " )
. order ( Arel . sql ( " COALESCE(ts_rank( #{ ts_vector } , #{ ts_query } ), 0) DESC " ) )
. pluck ( 'id' )
. uniq
2018-07-31 12:03:01 +02:00
end
2020-04-03 16:07:18 +02:00
def self . dossier_by_full_text_for_user ( search_terms , dossiers )
ts_vector = " to_tsvector('french', search_terms) "
2023-07-26 17:51:04 +02:00
ts_query = " to_tsquery('french', #{ Dossier . includes ( :procedure ) . connection . quote ( to_tsquery ( search_terms ) ) } ) "
2020-04-03 16:07:18 +02:00
dossiers
2022-03-09 10:27:43 +01:00
. visible_by_user
2020-04-03 16:07:18 +02:00
. where ( " #{ ts_vector } @@ #{ ts_query } " )
2021-02-17 18:25:41 +01:00
. order ( Arel . sql ( " COALESCE(ts_rank( #{ ts_vector } , #{ ts_query } ), 0) DESC " ) )
2020-04-03 16:07:18 +02:00
end
def self . dossier_by_exact_id_for_user ( search_terms , user )
id = search_terms . to_i
if id != 0 && id_compatible? ( id ) # Sometimes user is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this.
2023-07-26 17:51:04 +02:00
Dossier . includes ( :procedure ) . where ( id : user . dossiers . visible_by_user . where ( id : id ) + user . dossiers_invites . visible_by_user . where ( id : id ) ) . distinct
2020-04-03 16:07:18 +02:00
else
2023-07-26 17:51:04 +02:00
Dossier . includes ( :procedure ) . none
2020-04-03 16:07:18 +02:00
end
end
2021-05-06 01:14:57 +02:00
def self . id_compatible? ( number )
ActiveRecord :: Type :: Integer . new . serialize ( number )
true
rescue ActiveModel :: RangeError
false
2018-07-31 15:08:10 +02:00
end
def self . to_tsquery ( search_terms )
2019-05-22 14:34:46 +02:00
( search_terms || " " )
2021-02-16 15:05:04 +01:00
. gsub ( / ['? \\ :&|!<>()] / , " " ) # drop disallowed characters
2020-04-20 14:27:43 +02:00
. strip
2018-08-22 18:36:24 +02:00
. split ( / \ s+ / ) # split words
. map { | x | " #{ x } :* " } # enable prefix matching
2018-07-31 15:08:10 +02:00
. join ( " & " )
2018-07-31 14:32:17 +02:00
end
2018-07-31 12:03:01 +02:00
end