Merge pull request #4 from sgmap/postgresql-full-text-search-2
PostgreSQL full text search
This commit is contained in:
commit
9b9e2be8d8
13 changed files with 346 additions and 116 deletions
1
Gemfile
1
Gemfile
|
@ -62,6 +62,7 @@ gem 'fog'
|
||||||
gem 'fog-openstack'
|
gem 'fog-openstack'
|
||||||
|
|
||||||
gem 'pg'
|
gem 'pg'
|
||||||
|
gem 'scenic'
|
||||||
|
|
||||||
gem 'rgeo-geojson'
|
gem 'rgeo-geojson'
|
||||||
gem 'leaflet-rails'
|
gem 'leaflet-rails'
|
||||||
|
|
|
@ -482,6 +482,9 @@ GEM
|
||||||
sprockets (>= 2.8, < 4.0)
|
sprockets (>= 2.8, < 4.0)
|
||||||
sprockets-rails (>= 2.0, < 4.0)
|
sprockets-rails (>= 2.0, < 4.0)
|
||||||
tilt (>= 1.1, < 3)
|
tilt (>= 1.1, < 3)
|
||||||
|
scenic (1.3.0)
|
||||||
|
activerecord (>= 4.0.0)
|
||||||
|
railties (>= 4.0.0)
|
||||||
sdoc (0.4.1)
|
sdoc (0.4.1)
|
||||||
json (~> 1.7, >= 1.7.7)
|
json (~> 1.7, >= 1.7.7)
|
||||||
rdoc (~> 4.0)
|
rdoc (~> 4.0)
|
||||||
|
@ -635,6 +638,7 @@ DEPENDENCIES
|
||||||
rubocop-checkstyle_formatter
|
rubocop-checkstyle_formatter
|
||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
sass-rails (~> 5.0)
|
sass-rails (~> 5.0)
|
||||||
|
scenic
|
||||||
sdoc (~> 0.4.0)
|
sdoc (~> 0.4.0)
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
sentry-raven
|
sentry-raven
|
||||||
|
@ -654,4 +658,4 @@ DEPENDENCIES
|
||||||
will_paginate-bootstrap
|
will_paginate-bootstrap
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.12.5
|
1.13.2
|
||||||
|
|
|
@ -27,7 +27,12 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
|
||||||
|
|
||||||
def search
|
def search
|
||||||
@search_terms = params[:q]
|
@search_terms = params[:q]
|
||||||
@dossier = Dossier.search(current_gestionnaire, @search_terms)
|
|
||||||
|
@dossier = Search.new(
|
||||||
|
gestionnaire: current_gestionnaire,
|
||||||
|
query: @search_terms,
|
||||||
|
page: params[:page]
|
||||||
|
).results
|
||||||
|
|
||||||
smartlisting_dossier @dossier, 'search'
|
smartlisting_dossier @dossier, 'search'
|
||||||
|
|
||||||
|
|
|
@ -258,37 +258,6 @@ class Dossier < ActiveRecord::Base
|
||||||
where(state: TERMINE, archived: false).order("updated_at #{order}")
|
where(state: TERMINE, archived: false).order("updated_at #{order}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.search current_gestionnaire, terms
|
|
||||||
return [] if terms.blank?
|
|
||||||
|
|
||||||
dossiers = Dossier.arel_table
|
|
||||||
users = User.arel_table
|
|
||||||
etablissements = Etablissement.arel_table
|
|
||||||
entreprises = Entreprise.arel_table
|
|
||||||
|
|
||||||
composed_scope = self.joins('LEFT OUTER JOIN users ON users.id = dossiers.user_id')
|
|
||||||
.joins('LEFT OUTER JOIN entreprises ON entreprises.dossier_id = dossiers.id')
|
|
||||||
.joins('LEFT OUTER JOIN etablissements ON etablissements.dossier_id = dossiers.id')
|
|
||||||
|
|
||||||
terms.split.each do |word|
|
|
||||||
query_string = "%#{word}%"
|
|
||||||
query_string_start_with = "#{word}%"
|
|
||||||
|
|
||||||
composed_scope = composed_scope.where(
|
|
||||||
users[:email].matches(query_string).or\
|
|
||||||
etablissements[:siret].matches(query_string_start_with).or\
|
|
||||||
entreprises[:raison_sociale].matches(query_string).or\
|
|
||||||
dossiers[:id].eq(word_is_an_integer word))
|
|
||||||
end
|
|
||||||
|
|
||||||
composed_scope = composed_scope.where(
|
|
||||||
dossiers[:id].eq_any(current_gestionnaire.dossiers.ids).and\
|
|
||||||
dossiers[:state].does_not_match('draft').and\
|
|
||||||
dossiers[:archived].eq(false))
|
|
||||||
|
|
||||||
composed_scope
|
|
||||||
end
|
|
||||||
|
|
||||||
def cerfa_available?
|
def cerfa_available?
|
||||||
procedure.cerfa_flag? && cerfa.size != 0
|
procedure.cerfa_flag? && cerfa.size != 0
|
||||||
end
|
end
|
||||||
|
|
82
app/models/search.rb
Normal file
82
app/models/search.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# See:
|
||||||
|
# - https://robots.thoughtbot.com/implementing-multi-table-full-text-search-with-postgres
|
||||||
|
# - http://calebthompson.io/talks/search.html
|
||||||
|
class Search < ActiveRecord::Base
|
||||||
|
# :nodoc:
|
||||||
|
#
|
||||||
|
# Englobs a search result (actually a collection of Search objects) so it acts
|
||||||
|
# like a collection of regular Dossier objects, which can be decorated,
|
||||||
|
# paginated, ...
|
||||||
|
class Results
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize(results)
|
||||||
|
@results = results
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
@results.each do |search|
|
||||||
|
yield search.dossier
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(name, *args, &block)
|
||||||
|
@results.__send__(name, *args, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def decorate!
|
||||||
|
@results.each do |search|
|
||||||
|
search.dossier = search.dossier.decorate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :gestionnaire
|
||||||
|
attr_accessor :query
|
||||||
|
attr_accessor :page
|
||||||
|
|
||||||
|
belongs_to :dossier
|
||||||
|
|
||||||
|
def results
|
||||||
|
unless @query.present?
|
||||||
|
return Search.none
|
||||||
|
end
|
||||||
|
|
||||||
|
search_term = Search.connection.quote(to_tsquery)
|
||||||
|
|
||||||
|
dossier_ids = @gestionnaire.dossiers
|
||||||
|
.select(:id)
|
||||||
|
.where(archived: false)
|
||||||
|
.where.not(state: "draft")
|
||||||
|
|
||||||
|
q = Search
|
||||||
|
.select("DISTINCT(searches.dossier_id)")
|
||||||
|
.select("COALESCE(ts_rank(to_tsvector('french', searches.term::text), to_tsquery('french', #{search_term})), 0) AS rank")
|
||||||
|
.joins(:dossier)
|
||||||
|
.where(dossier_id: dossier_ids)
|
||||||
|
.where("to_tsvector('french', searches.term::text) @@ to_tsquery('french', #{search_term})")
|
||||||
|
.order("rank DESC")
|
||||||
|
.preload(:dossier)
|
||||||
|
|
||||||
|
if @page.present?
|
||||||
|
q = q.paginate(page: @page)
|
||||||
|
end
|
||||||
|
|
||||||
|
Results.new(q)
|
||||||
|
end
|
||||||
|
|
||||||
|
#def self.refresh
|
||||||
|
# # TODO: could be executed concurrently
|
||||||
|
# # See https://github.com/thoughtbot/scenic#what-about-materialized-views
|
||||||
|
# Scenic.database.refresh_materialized_view(table_name, concurrently: false)
|
||||||
|
#end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def to_tsquery
|
||||||
|
@query.gsub(/['?\\:&|!]/, "") # drop disallowed characters
|
||||||
|
.split(/\s+/) # split words
|
||||||
|
.map { |x| "#{x}:*" } # enable prefix matching
|
||||||
|
.join(" & ")
|
||||||
|
end
|
||||||
|
end
|
27
db/migrate/20161025150900_create_searches.rb
Normal file
27
db/migrate/20161025150900_create_searches.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
class CreateSearches < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
add_index :champs, :dossier_id
|
||||||
|
add_index :champs, :type_de_champ_id
|
||||||
|
add_index :drop_down_lists, :type_de_champ_id
|
||||||
|
add_index :etablissements, :dossier_id
|
||||||
|
add_index :entreprises, :dossier_id
|
||||||
|
add_index :france_connect_informations, :user_id
|
||||||
|
add_index :individuals, :dossier_id
|
||||||
|
add_index :pieces_justificatives, :dossier_id
|
||||||
|
add_index :rna_informations, :entreprise_id
|
||||||
|
create_view :searches #, materialized: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_index :champs, :dossier_id
|
||||||
|
remove_index :champs, :type_de_champ_id
|
||||||
|
remove_index :drop_down_lists, :type_de_champ_id
|
||||||
|
remove_index :etablissements, :dossier_id
|
||||||
|
remove_index :entreprises, :dossier_id
|
||||||
|
remove_index :france_connect_informations, :user_id
|
||||||
|
remove_index :individuals, :dossier_id
|
||||||
|
remove_index :pieces_justificatives, :dossier_id
|
||||||
|
remove_index :rna_informations, :entreprise_id
|
||||||
|
drop_view :searches #, materialized: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
class UpdateSearchesToVersion2 < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
replace_view :searches, version: 2
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
replace_view :searches, version: 1
|
||||||
|
end
|
||||||
|
end
|
35
db/schema.rb
35
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20161011125345) do
|
ActiveRecord::Schema.define(version: 20161102154835) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -102,6 +102,9 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.string "type"
|
t.string "type"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "champs", ["dossier_id"], name: "index_champs_on_dossier_id", using: :btree
|
||||||
|
add_index "champs", ["type_de_champ_id"], name: "index_champs_on_type_de_champ_id", using: :btree
|
||||||
|
|
||||||
create_table "commentaires", force: :cascade do |t|
|
create_table "commentaires", force: :cascade do |t|
|
||||||
t.string "email"
|
t.string "email"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
|
@ -135,6 +138,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.integer "type_de_champ_id"
|
t.integer "type_de_champ_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "drop_down_lists", ["type_de_champ_id"], name: "index_drop_down_lists_on_type_de_champ_id", using: :btree
|
||||||
|
|
||||||
create_table "entreprises", force: :cascade do |t|
|
create_table "entreprises", force: :cascade do |t|
|
||||||
t.string "siren"
|
t.string "siren"
|
||||||
t.integer "capital_social"
|
t.integer "capital_social"
|
||||||
|
@ -151,6 +156,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.integer "dossier_id"
|
t.integer "dossier_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "entreprises", ["dossier_id"], name: "index_entreprises_on_dossier_id", using: :btree
|
||||||
|
|
||||||
create_table "etablissements", force: :cascade do |t|
|
create_table "etablissements", force: :cascade do |t|
|
||||||
t.string "siret"
|
t.string "siret"
|
||||||
t.boolean "siege_social"
|
t.boolean "siege_social"
|
||||||
|
@ -168,6 +175,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.integer "entreprise_id"
|
t.integer "entreprise_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "etablissements", ["dossier_id"], name: "index_etablissements_on_dossier_id", using: :btree
|
||||||
|
|
||||||
create_table "exercices", force: :cascade do |t|
|
create_table "exercices", force: :cascade do |t|
|
||||||
t.string "ca"
|
t.string "ca"
|
||||||
t.datetime "dateFinExercice"
|
t.datetime "dateFinExercice"
|
||||||
|
@ -194,6 +203,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.string "email_france_connect"
|
t.string "email_france_connect"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "france_connect_informations", ["user_id"], name: "index_france_connect_informations_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "gestionnaires", force: :cascade do |t|
|
create_table "gestionnaires", force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.string "encrypted_password", default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
|
@ -221,6 +232,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.string "gender"
|
t.string "gender"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "individuals", ["dossier_id"], name: "index_individuals_on_dossier_id", using: :btree
|
||||||
|
|
||||||
create_table "invites", force: :cascade do |t|
|
create_table "invites", force: :cascade do |t|
|
||||||
t.string "email"
|
t.string "email"
|
||||||
t.string "email_sender"
|
t.string "email_sender"
|
||||||
|
@ -255,6 +268,7 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.string "content_secure_token"
|
t.string "content_secure_token"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "pieces_justificatives", ["dossier_id"], name: "index_pieces_justificatives_on_dossier_id", using: :btree
|
||||||
add_index "pieces_justificatives", ["type_de_piece_justificative_id"], name: "index_pieces_justificatives_on_type_de_piece_justificative_id", using: :btree
|
add_index "pieces_justificatives", ["type_de_piece_justificative_id"], name: "index_pieces_justificatives_on_type_de_piece_justificative_id", using: :btree
|
||||||
|
|
||||||
create_table "preference_list_dossiers", force: :cascade do |t|
|
create_table "preference_list_dossiers", force: :cascade do |t|
|
||||||
|
@ -324,6 +338,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
t.integer "entreprise_id"
|
t.integer "entreprise_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "rna_informations", ["entreprise_id"], name: "index_rna_informations_on_entreprise_id", using: :btree
|
||||||
|
|
||||||
create_table "types_de_champ", force: :cascade do |t|
|
create_table "types_de_champ", force: :cascade do |t|
|
||||||
t.string "libelle"
|
t.string "libelle"
|
||||||
t.string "type_champ"
|
t.string "type_champ"
|
||||||
|
@ -369,4 +385,21 @@ ActiveRecord::Schema.define(version: 20161011125345) do
|
||||||
add_foreign_key "dossiers", "users"
|
add_foreign_key "dossiers", "users"
|
||||||
add_foreign_key "procedure_paths", "administrateurs"
|
add_foreign_key "procedure_paths", "administrateurs"
|
||||||
add_foreign_key "procedure_paths", "procedures"
|
add_foreign_key "procedure_paths", "procedures"
|
||||||
|
|
||||||
|
create_view :searches, sql_definition: <<-SQL
|
||||||
|
SELECT dossiers.id AS dossier_id,
|
||||||
|
(((((((((((((((((((((((((((((((((((((((((((((((((((((((COALESCE(users.email, ''::character varying))::text || ' '::text) || (COALESCE(france_connect_informations.given_name, ''::character varying))::text) || ' '::text) || (COALESCE(france_connect_informations.family_name, ''::character varying))::text) || ' '::text) || (COALESCE(cerfas.content, ''::character varying))::text) || ' '::text) || (COALESCE(champs.value, ''::character varying))::text) || ' '::text) || (COALESCE(drop_down_lists.value, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.siren, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.numero_tva_intracommunautaire, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.forme_juridique, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.forme_juridique_code, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.nom_commercial, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.raison_sociale, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.siret_siege_social, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.nom, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.prenom, ''::character varying))::text) || ' '::text) || (COALESCE(rna_informations.association_id, ''::character varying))::text) || ' '::text) || (COALESCE(rna_informations.titre, ''::character varying))::text) || ' '::text) || COALESCE(rna_informations.objet, ''::text)) || ' '::text) || (COALESCE(etablissements.siret, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.naf, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.libelle_naf, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.adresse, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.code_postal, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.localite, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.code_insee_localite, ''::character varying))::text) || ' '::text) || (COALESCE(individuals.nom, ''::character varying))::text) || ' '::text) || (COALESCE(individuals.prenom, ''::character varying))::text) || ' '::text) || (COALESCE(pieces_justificatives.content, ''::character varying))::text) AS term
|
||||||
|
FROM ((((((((((dossiers
|
||||||
|
JOIN users ON ((users.id = dossiers.user_id)))
|
||||||
|
LEFT JOIN france_connect_informations ON ((france_connect_informations.user_id = dossiers.user_id)))
|
||||||
|
LEFT JOIN cerfas ON ((cerfas.dossier_id = dossiers.id)))
|
||||||
|
LEFT JOIN champs ON ((champs.dossier_id = dossiers.id)))
|
||||||
|
LEFT JOIN drop_down_lists ON ((drop_down_lists.type_de_champ_id = champs.type_de_champ_id)))
|
||||||
|
LEFT JOIN entreprises ON ((entreprises.dossier_id = dossiers.id)))
|
||||||
|
LEFT JOIN rna_informations ON ((rna_informations.entreprise_id = entreprises.id)))
|
||||||
|
LEFT JOIN etablissements ON ((etablissements.dossier_id = dossiers.id)))
|
||||||
|
LEFT JOIN individuals ON ((individuals.dossier_id = dossiers.id)))
|
||||||
|
LEFT JOIN pieces_justificatives ON ((pieces_justificatives.dossier_id = dossiers.id)));
|
||||||
|
SQL
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
59
db/views/searches_v01.sql
Normal file
59
db/views/searches_v01.sql
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
-- this version allows to search for a single term within many tables,
|
||||||
|
-- but behaves badly with multiple terms scattered in multiple tables.
|
||||||
|
|
||||||
|
SELECT dossiers.id AS dossier_id,
|
||||||
|
dossiers.id::text || ' ' ||
|
||||||
|
COALESCE(users.email, '') AS term
|
||||||
|
FROM dossiers
|
||||||
|
INNER JOIN users ON users.id = dossiers.user_id
|
||||||
|
|
||||||
|
UNION SELECT cerfas.dossier_id,
|
||||||
|
COALESCE(cerfas.content, '') AS term
|
||||||
|
FROM cerfas
|
||||||
|
|
||||||
|
UNION SELECT champs.dossier_id,
|
||||||
|
COALESCE(champs.value, '') || ' ' ||
|
||||||
|
COALESCE(drop_down_lists.value, '') AS term
|
||||||
|
FROM champs
|
||||||
|
INNER JOIN drop_down_lists ON drop_down_lists.type_de_champ_id = champs.type_de_champ_id
|
||||||
|
|
||||||
|
UNION SELECT entreprises.dossier_id,
|
||||||
|
COALESCE(entreprises.siren, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.numero_tva_intracommunautaire, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.forme_juridique, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.forme_juridique_code, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.nom_commercial, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.raison_sociale, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.siret_siege_social, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.nom, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.prenom, '') || ' ' ||
|
||||||
|
COALESCE(rna_informations.association_id, '') || ' ' ||
|
||||||
|
COALESCE(rna_informations.titre, '') || ' ' ||
|
||||||
|
COALESCE(rna_informations.objet, '') AS term
|
||||||
|
FROM entreprises
|
||||||
|
LEFT JOIN rna_informations ON rna_informations.entreprise_id = entreprises.id
|
||||||
|
|
||||||
|
UNION SELECT etablissements.dossier_id,
|
||||||
|
COALESCE(etablissements.siret, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.naf, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.libelle_naf, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.adresse, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.code_postal, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.localite, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.code_insee_localite, '') AS term
|
||||||
|
FROM etablissements
|
||||||
|
|
||||||
|
UNION SELECT individuals.dossier_id,
|
||||||
|
COALESCE(individuals.nom, '') || ' ' ||
|
||||||
|
COALESCE(individuals.prenom, '') AS term
|
||||||
|
FROM individuals
|
||||||
|
|
||||||
|
UNION SELECT pieces_justificatives.dossier_id,
|
||||||
|
COALESCE(pieces_justificatives.content, '') AS term
|
||||||
|
FROM pieces_justificatives
|
||||||
|
|
||||||
|
UNION SELECT dossiers.id,
|
||||||
|
COALESCE(france_connect_informations.given_name, '') || ' ' ||
|
||||||
|
COALESCE(france_connect_informations.family_name, '') AS term
|
||||||
|
FROM france_connect_informations
|
||||||
|
INNER JOIN dossiers ON dossiers.user_id = france_connect_informations.user_id
|
43
db/views/searches_v02.sql
Normal file
43
db/views/searches_v02.sql
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
-- this version merges all possible search terms together, complicating the
|
||||||
|
-- view, but enables searching for multiple terms from multiple tables at once.
|
||||||
|
|
||||||
|
SELECT dossiers.id AS dossier_id,
|
||||||
|
COALESCE(users.email, '') || ' ' ||
|
||||||
|
COALESCE(france_connect_informations.given_name, '') || ' ' ||
|
||||||
|
COALESCE(france_connect_informations.family_name, '') || ' ' ||
|
||||||
|
COALESCE(cerfas.content, '') || ' ' ||
|
||||||
|
COALESCE(champs.value, '') || ' ' ||
|
||||||
|
COALESCE(drop_down_lists.value, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.siren, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.numero_tva_intracommunautaire, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.forme_juridique, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.forme_juridique_code, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.nom_commercial, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.raison_sociale, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.siret_siege_social, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.nom, '') || ' ' ||
|
||||||
|
COALESCE(entreprises.prenom, '') || ' ' ||
|
||||||
|
COALESCE(rna_informations.association_id, '') || ' ' ||
|
||||||
|
COALESCE(rna_informations.titre, '') || ' ' ||
|
||||||
|
COALESCE(rna_informations.objet, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.siret, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.naf, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.libelle_naf, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.adresse, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.code_postal, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.localite, '') || ' ' ||
|
||||||
|
COALESCE(etablissements.code_insee_localite, '') || ' ' ||
|
||||||
|
COALESCE(individuals.nom, '') || ' ' ||
|
||||||
|
COALESCE(individuals.prenom, '') || ' ' ||
|
||||||
|
COALESCE(pieces_justificatives.content, '') AS term
|
||||||
|
FROM dossiers
|
||||||
|
INNER JOIN users ON users.id = dossiers.user_id
|
||||||
|
LEFT JOIN france_connect_informations ON france_connect_informations.user_id = dossiers.user_id
|
||||||
|
LEFT JOIN cerfas ON cerfas.dossier_id = dossiers.id
|
||||||
|
LEFT JOIN champs ON champs.dossier_id = dossiers.id
|
||||||
|
LEFT JOIN drop_down_lists ON drop_down_lists.type_de_champ_id = champs.type_de_champ_id
|
||||||
|
LEFT JOIN entreprises ON entreprises.dossier_id = dossiers.id
|
||||||
|
LEFT JOIN rna_informations ON rna_informations.entreprise_id = entreprises.id
|
||||||
|
LEFT JOIN etablissements ON etablissements.dossier_id = dossiers.id
|
||||||
|
LEFT JOIN individuals ON individuals.dossier_id = dossiers.id
|
||||||
|
LEFT JOIN pieces_justificatives ON pieces_justificatives.dossier_id = dossiers.id
|
|
@ -556,87 +556,6 @@ describe Dossier do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.search' do
|
|
||||||
subject { liste_dossiers }
|
|
||||||
|
|
||||||
let(:liste_dossiers) { described_class.search(gestionnaire_1, terms) }
|
|
||||||
# let(:dossier) { described_class.search(gestionnaire_1, terms)[1] }
|
|
||||||
|
|
||||||
let(:administrateur_1) { create(:administrateur) }
|
|
||||||
let(:administrateur_2) { create(:administrateur) }
|
|
||||||
|
|
||||||
let(:gestionnaire_1) { create(:gestionnaire, administrateurs: [administrateur_1]) }
|
|
||||||
let(:gestionnaire_2) { create(:gestionnaire, administrateurs: [administrateur_2]) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
create :assign_to, gestionnaire: gestionnaire_1, procedure: procedure_1
|
|
||||||
create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:procedure_1) { create(:procedure, administrateur: administrateur_1) }
|
|
||||||
let(:procedure_2) { create(:procedure, administrateur: administrateur_2) }
|
|
||||||
|
|
||||||
let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) }
|
|
||||||
let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) }
|
|
||||||
let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) }
|
|
||||||
let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) }
|
|
||||||
let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) }
|
|
||||||
|
|
||||||
let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') }
|
|
||||||
let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') }
|
|
||||||
let!(:etablissement_3) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Technology', dossier: dossier_3), dossier: dossier_3, siret: '41816609600051') }
|
|
||||||
|
|
||||||
describe 'search is empty' do
|
|
||||||
let(:terms) { '' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(0) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'search draft file' do
|
|
||||||
let(:terms) { 'brouillon' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(0) }
|
|
||||||
it { expect(subject.class).to eq Dossier::ActiveRecord_Relation }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'search on contact email' do
|
|
||||||
let(:terms) { 'clap' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(0) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'search on ID dossier' do
|
|
||||||
let(:terms) { "#{dossier_2.id}" }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(1) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'search on SIRET' do
|
|
||||||
context 'when is part of SIRET' do
|
|
||||||
let(:terms) { '4181' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(1) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when is a complet SIRET' do
|
|
||||||
let(:terms) { '41816602300012' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(1) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'search on raison social' do
|
|
||||||
let(:terms) { 'OCTO' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(2) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'search on multiple fields' do
|
|
||||||
let(:terms) { 'octo test' }
|
|
||||||
|
|
||||||
it { expect(subject.size).to eq(1) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#cerfa_available?' do
|
describe '#cerfa_available?' do
|
||||||
|
|
79
spec/models/search_spec.rb
Normal file
79
spec/models/search_spec.rb
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Search do
|
||||||
|
describe '.results' do
|
||||||
|
subject { liste_dossiers }
|
||||||
|
|
||||||
|
let(:liste_dossiers) do
|
||||||
|
described_class.new(gestionnaire: gestionnaire_1, query: terms).results
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:administrateur_1) { create(:administrateur) }
|
||||||
|
let(:administrateur_2) { create(:administrateur) }
|
||||||
|
|
||||||
|
let(:gestionnaire_1) { create(:gestionnaire, administrateurs: [administrateur_1]) }
|
||||||
|
let(:gestionnaire_2) { create(:gestionnaire, administrateurs: [administrateur_2]) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create :assign_to, gestionnaire: gestionnaire_1, procedure: procedure_1
|
||||||
|
create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:procedure_1) { create(:procedure, administrateur: administrateur_1) }
|
||||||
|
let(:procedure_2) { create(:procedure, administrateur: administrateur_2) }
|
||||||
|
|
||||||
|
let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) }
|
||||||
|
let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) }
|
||||||
|
let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) }
|
||||||
|
let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) }
|
||||||
|
let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) }
|
||||||
|
|
||||||
|
let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') }
|
||||||
|
let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') }
|
||||||
|
let!(:etablissement_3) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Technology', dossier: dossier_3), dossier: dossier_3, siret: '41816609600051') }
|
||||||
|
|
||||||
|
describe 'search is empty' do
|
||||||
|
let(:terms) { '' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(0) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'search draft file' do
|
||||||
|
let(:terms) { 'brouillon' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(0) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'search on contact email' do
|
||||||
|
let(:terms) { 'clap' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(0) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'search on SIRET' do
|
||||||
|
context 'when is part of SIRET' do
|
||||||
|
let(:terms) { '4181' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(1) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when is a complet SIRET' do
|
||||||
|
let(:terms) { '41816602300012' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(1) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'search on raison social' do
|
||||||
|
let(:terms) { 'OCTO' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(2) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'search on multiple fields' do
|
||||||
|
let(:terms) { 'octo plop' }
|
||||||
|
|
||||||
|
it { expect(subject.size).to eq(1) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue