search.rb 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. module Ticket::Search
  3. extend ActiveSupport::Concern
  4. # methods defined here are going to extend the class, not the instance of it
  5. class_methods do
  6. =begin
  7. search tickets preferences
  8. result = Ticket.search_preferences(user_model)
  9. returns if user has permissions to search
  10. result = {
  11. prio: 3000,
  12. direct_search_index: false
  13. }
  14. returns if user has no permissions to search
  15. result = false
  16. =end
  17. def search_preferences(_current_user)
  18. {
  19. prio: 3000,
  20. direct_search_index: false,
  21. }
  22. end
  23. =begin
  24. search tickets via search index
  25. result = Ticket.search(
  26. current_user: User.find(123),
  27. query: 'search something',
  28. limit: 15,
  29. offset: 100,
  30. )
  31. returns
  32. result = [ticket_model1, ticket_model2]
  33. search tickets via search index
  34. result = Ticket.search(
  35. current_user: User.find(123),
  36. query: 'search something',
  37. limit: 15,
  38. offset: 100,
  39. full: false,
  40. )
  41. returns
  42. result = [1,3,5,6,7]
  43. search tickets via database
  44. result = Ticket.search(
  45. current_user: User.find(123),
  46. query: 'some query', # query or condition is required
  47. condition: {
  48. 'tickets.owner_id' => {
  49. operator: 'is',
  50. value: user.id,
  51. },
  52. 'tickets.state_id' => {
  53. operator: 'is',
  54. value: Ticket::State.where(
  55. state_type_id: Ticket::StateType.where(
  56. name: [
  57. 'pending reminder',
  58. 'pending action',
  59. ],
  60. ).map(&:id),
  61. },
  62. ),
  63. },
  64. limit: 15,
  65. offset: 100,
  66. # sort single column
  67. sort_by: 'created_at',
  68. order_by: 'asc',
  69. # sort multiple columns
  70. sort_by: [ 'created_at', 'updated_at' ],
  71. order_by: [ 'asc', 'desc' ],
  72. full: false,
  73. )
  74. returns
  75. result = [1,3,5,6,7]
  76. =end
  77. def search(params)
  78. # get params
  79. query = params[:query]
  80. condition = params[:condition]
  81. limit = params[:limit] || 12
  82. offset = params[:offset] || 0
  83. current_user = params[:current_user]
  84. full = false
  85. if params[:full] == true || params[:full] == 'true' || !params.key?(:full)
  86. full = true
  87. end
  88. sql_helper = ::SqlHelper.new(object: self)
  89. # check sort
  90. sort_by = sql_helper.get_sort_by(params, 'updated_at')
  91. # check order
  92. order_by = sql_helper.get_order_by(params, 'desc')
  93. # try search index backend
  94. if condition.blank? && SearchIndexBackend.enabled?
  95. query_or = []
  96. if current_user.permissions?('ticket.agent')
  97. group_ids = current_user.group_ids_access('read')
  98. if group_ids.present?
  99. access_condition = {
  100. 'query_string' => { 'default_field' => 'group_id', 'query' => "\"#{group_ids.join('" OR "')}\"" }
  101. }
  102. query_or.push(access_condition)
  103. end
  104. end
  105. if current_user.permissions?('ticket.customer')
  106. access_condition = if !current_user.organization || (!current_user.organization.shared || current_user.organization.shared == false)
  107. {
  108. 'query_string' => { 'default_field' => 'customer_id', 'query' => current_user.id }
  109. }
  110. # customer_id: XXX
  111. # conditions = [ 'customer_id = ?', current_user.id ]
  112. else
  113. {
  114. 'query_string' => { 'query' => "customer_id:#{current_user.id} OR organization_id:#{current_user.organization.id}" }
  115. }
  116. # customer_id: XXX OR organization_id: XXX
  117. # conditions = [ '( customer_id = ? OR organization_id = ? )', current_user.id, current_user.organization.id ]
  118. end
  119. query_or.push(access_condition)
  120. end
  121. return [] if query_or.blank?
  122. query_extension = {
  123. bool: {
  124. must: [
  125. {
  126. bool: {
  127. should: query_or,
  128. },
  129. },
  130. ],
  131. }
  132. }
  133. items = SearchIndexBackend.search(query, 'Ticket', limit: limit,
  134. query_extension: query_extension,
  135. from: offset,
  136. sort_by: sort_by,
  137. order_by: order_by)
  138. if !full
  139. ids = []
  140. items.each do |item|
  141. ids.push item[:id]
  142. end
  143. return ids
  144. end
  145. tickets = []
  146. items.each do |item|
  147. ticket = Ticket.lookup(id: item[:id])
  148. next if !ticket
  149. tickets.push ticket
  150. end
  151. return tickets
  152. end
  153. order_sql = sql_helper.get_order(sort_by, order_by, 'tickets.updated_at DESC')
  154. tickets_all = TicketPolicy::ReadScope.new(current_user).resolve
  155. .order(Arel.sql(order_sql))
  156. .offset(offset)
  157. .limit(limit)
  158. ticket_ids = if query
  159. tickets_all.joins(:articles)
  160. .where(<<~SQL.squish, query: "%#{query.delete('*')}%")
  161. tickets.title LIKE :query
  162. OR tickets.number LIKE :query
  163. OR ticket_articles.body LIKE :query
  164. OR ticket_articles.from LIKE :query
  165. OR ticket_articles.to LIKE :query
  166. OR ticket_articles.subject LIKE :query
  167. SQL
  168. else
  169. query_condition, bind_condition, tables = selector2sql(condition)
  170. tickets_all.joins(tables)
  171. .where(query_condition, *bind_condition)
  172. end.group(:id).pluck(:id)
  173. if full
  174. ticket_ids.map { |id| Ticket.lookup(id: id) }
  175. else
  176. ticket_ids
  177. end
  178. end
  179. end
  180. end