Fixed bug: Organization search queries don't find case insensitive results.

This commit is contained in:
Thorsten Eckel 2018-06-05 09:15:48 +02:00
parent 61aa8fb84a
commit 809a698c03
4 changed files with 67 additions and 6 deletions

View file

@ -17,6 +17,7 @@ class ApplicationModel < ActiveRecord::Base
include ApplicationModel::HasExternalSync include ApplicationModel::HasExternalSync
include ApplicationModel::ChecksImport include ApplicationModel::ChecksImport
include ApplicationModel::CanTouchReferences include ApplicationModel::CanTouchReferences
include ApplicationModel::CanQueryCaseInsensitiveWhereOrSql
self.abstract_class = true self.abstract_class = true
end end

View file

@ -0,0 +1,43 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module ApplicationModel::CanQueryCaseInsensitiveWhereOrSql
extend ActiveSupport::Concern
included do
# Builds a case insenstive WHERE ... OR ... SQL query.
#
# @see .or_cis
#
# @example
# Organization.where_or_cis(%i[name note], "%zammad%").to_sql
# #=> "SELECT `organizations`.* FROM `organizations` WHERE (`organizations`.`name` LIKE '%zammad%' OR `organizations`.`note` LIKE '%zammad%')"
#
# @return [ActiveRecord::Relation] the ActiveRecord relation that can be combined or executed
scope :where_or_cis, ->(*args) { where(or_cis(*args)) }
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
# Builds a case insenstive OR SQL grouping. This comes in handy for join queries.
# For direct WHERE queries prefer .where_or_cis scope.
#
# @param [Array] attributes the attributes that should get queried case insensitive.
# @param [String] query the SQL query that should be used for each given attribute.
#
# @example
# Organization.joins(:users).where(User.or_cis(%i[firstname lastname email], "%#{query}%"))
#
# @return [Arel::Nodes::Grouping] can be passed to ActiveRecord queries
def or_cis(attributes, query)
# use Arel to create an Array of case insenstive
# LIKE queries based on the current DB adapter
cis_matches = attributes.map do |attribute|
arel_table[attribute].matches(query)
end
# return the by OR joined Arel queries
cis_matches.inject(:or)
end
end
end

View file

@ -77,9 +77,11 @@ returns
# fallback do sql query # fallback do sql query
# - stip out * we already search for *query* - # - stip out * we already search for *query* -
query.delete! '*' query.delete! '*'
organizations = Organization.where( organizations = Organization.where_or_cis(%i[name note], "%#{query}%")
'name LIKE ? OR note LIKE ?', "%#{query}%", "%#{query}%" .order('name')
).order('name').offset(offset).limit(limit).to_a .offset(offset)
.limit(limit)
.to_a
# use result independent of size if an explicit offset is given # use result independent of size if an explicit offset is given
# this is the case for e.g. paginated searches # this is the case for e.g. paginated searches
@ -87,9 +89,11 @@ returns
return organizations if organizations.length > 3 return organizations if organizations.length > 3
# if only a few organizations are found, search for names of users # if only a few organizations are found, search for names of users
organizations_by_user = Organization.select('DISTINCT(organizations.id), organizations.name').joins('LEFT OUTER JOIN users ON users.organization_id = organizations.id').where( organizations_by_user = Organization.select('DISTINCT(organizations.id), organizations.name')
'users.firstname LIKE ? or users.lastname LIKE ? or users.email LIKE ?', "%#{query}%", "%#{query}%", "%#{query}%" .joins('LEFT OUTER JOIN users ON users.organization_id = organizations.id')
).order('organizations.name').limit(limit) .where(User.or_cis(%i[firstname lastname email], "%#{query}%"))
.order('organizations.name')
.limit(limit)
organizations_by_user.each do |organization_by_user| organizations_by_user.each do |organization_by_user|

View file

@ -0,0 +1,13 @@
require 'rails_helper'
RSpec.describe Organization do
context '.where_or_cis' do
it 'finds instance by querying multiple attributes case insensitive' do
# search for Zammad Foundation
organizations = described_class.where_or_cis(%i[name note], '%zammad%')
expect(organizations).not_to be_blank
end
end
end