search_index_backend_spec.rb 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe SearchIndexBackend do
  4. before do |example|
  5. next if !example.metadata[:searchindex]
  6. searchindex_model_reload([Ticket, User, Organization])
  7. end
  8. describe '.build_query' do
  9. subject(:query) { described_class.build_query('Ticket', '', query_extension: params) }
  10. let(:params) { { 'bool' => { 'filter' => { 'term' => { 'a' => 'b' } } } } }
  11. it 'coerces :query_extension hash keys to symbols' do
  12. expect(query.dig(:query, :bool, :filter, :term, :a)).to eq('b')
  13. end
  14. end
  15. describe '.search', searchindex: true do
  16. context 'query finds results' do
  17. let(:record_type) { 'Ticket'.freeze }
  18. let(:record) { create(:ticket) }
  19. before do
  20. record.search_index_update_backend
  21. described_class.refresh
  22. end
  23. it 'finds added records' do
  24. result = described_class.search(record.number, record_type, sort_by: ['updated_at'], order_by: ['desc'])
  25. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  26. end
  27. end
  28. context 'when search for user firstname + double lastname' do
  29. let(:record_type) { 'User'.freeze }
  30. let(:record) { create(:user, login: 'a', email: 'a@a.de', firstname: 'AnFirst', lastname: 'ASplit Lastname') }
  31. before do
  32. record.search_index_update_backend
  33. described_class.refresh
  34. end
  35. it 'finds user record' do
  36. result = described_class.search('AnFirst ASplit Lastname', record_type, sort_by: ['updated_at'], order_by: ['desc'])
  37. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  38. end
  39. end
  40. context 'for query with no results' do
  41. subject(:search) { described_class.search(query, index, limit: 3000) }
  42. let(:query) { 'preferences.notification_sound.enabled:*' }
  43. context 'on a single index' do
  44. let(:index) { 'User' }
  45. it { is_expected.to be_an(Array).and be_empty }
  46. end
  47. context 'on multiple indices' do
  48. let(:index) { %w[User Organization] }
  49. it { is_expected.to be_an(Array).and not_include(nil).and be_empty }
  50. end
  51. context 'when user has a signature detection' do
  52. let(:user) { create(:agent, preferences: { signature_detection: 'Hamburg' }) }
  53. let(:record_type) { 'Ticket'.freeze }
  54. let(:record) { create(:ticket, created_by: user) }
  55. before do
  56. record.search_index_update_backend
  57. described_class.refresh
  58. end
  59. it 'does not find the ticket record' do
  60. result = described_class.search('Hamburg', record_type, sort_by: ['updated_at'], order_by: ['desc'])
  61. expect(result).to eq([])
  62. end
  63. end
  64. end
  65. context 'search with date that requires time zone conversion', time_zone: 'Europe/Vilnius' do
  66. let(:record_type) { 'Ticket'.freeze }
  67. let(:record) { create(:ticket) }
  68. before do
  69. travel_to(Time.zone.parse('2019-01-02 00:33'))
  70. described_class.add(record_type, record)
  71. described_class.refresh
  72. end
  73. it 'finds record in a given timezone with a range' do
  74. Setting.set('timezone_default', 'UTC')
  75. result = described_class.search('created_at: [2019-01-01 TO 2019-01-01]', record_type)
  76. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  77. end
  78. it 'finds record in a far away timezone with a date' do
  79. Setting.set('timezone_default', 'Europe/Vilnius')
  80. result = described_class.search('created_at: 2019-01-02', record_type)
  81. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  82. end
  83. it 'finds record in UTC with date' do
  84. Setting.set('timezone_default', 'UTC')
  85. result = described_class.search('created_at: 2019-01-01', record_type)
  86. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  87. end
  88. end
  89. context 'does find integer values for ticket data', db_strategy: :reset do
  90. let(:record_type) { 'Ticket'.freeze }
  91. let(:record) { create(:ticket, inttest: '1021052349') }
  92. before do
  93. create(:object_manager_attribute_integer, name: 'inttest', data_option: {
  94. 'default' => 0,
  95. 'min' => 0,
  96. 'max' => 99_999_999,
  97. })
  98. ObjectManager::Attribute.migration_execute
  99. record.search_index_update_backend
  100. described_class.refresh
  101. end
  102. it 'finds added records by integer part' do
  103. result = described_class.search('102105', record_type, sort_by: ['updated_at'], order_by: ['desc'])
  104. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  105. end
  106. it 'finds added records by integer' do
  107. result = described_class.search('1021052349', record_type, sort_by: ['updated_at'], order_by: ['desc'])
  108. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  109. end
  110. it 'finds added records by quoted integer' do
  111. result = described_class.search('"1021052349"', record_type, sort_by: ['updated_at'], order_by: ['desc'])
  112. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  113. end
  114. end
  115. context 'can sort by datetime fields', db_strategy: :reset do
  116. let(:record_type) { 'Ticket'.freeze }
  117. let(:record) { create(:ticket) }
  118. let(:field_name) { SecureRandom.uuid }
  119. before do
  120. create(:object_manager_attribute_datetime, name: field_name)
  121. ObjectManager::Attribute.migration_execute
  122. record.search_index_update_backend
  123. described_class.refresh
  124. end
  125. it 'finds added records' do
  126. result = described_class.search(record.number, record_type, sort_by: [field_name], order_by: ['desc'])
  127. expect(result).to eq([{ id: record.id.to_s, type: record_type }])
  128. end
  129. end
  130. end
  131. describe '.append_wildcard_to_simple_query' do
  132. context 'with "simple" queries' do
  133. let(:queries) { <<~QUERIES.lines.map { |x| x.split('#')[0] }.map(&:strip) }
  134. M
  135. Max
  136. Max. # dot and underscore are acceptable characters in simple queries
  137. A_
  138. A_B
  139. äöü
  140. 123
  141. *ax # wildcards are allowed in simple queries
  142. Max*
  143. M*x
  144. M?x
  145. test@example.com
  146. test@example.
  147. test@example
  148. test@
  149. QUERIES
  150. it 'appends a * to the original query' do
  151. expect(queries.map { |query| described_class.append_wildcard_to_simple_query(query) })
  152. .to eq(queries.map { |q| "#{q}*" })
  153. end
  154. end
  155. context 'with "complex" queries (using search operators)' do
  156. let(:queries) { <<~QUERIES.lines.map { |x| x.split('#')[0] }.map(&:strip) }
  157. title:"some words with spaces" # exact phrase / without quotation marks " an AND search for the words will be performed (in Zammad 1.5 and lower an OR search will be performed)
  158. title:"some wor*" # exact phrase beginning with "some wor*" will be searched
  159. created_at:[2017-01-01 TO 2017-12-31] # a time range
  160. created_at:>now-1h # created within last hour
  161. state:new OR state:open
  162. (state:new OR state:open) OR priority:"3 normal"
  163. (state:new OR state:open) AND customer.lastname:smith
  164. state:(new OR open) AND title:(full text search) # state: new OR open & title: full OR text OR search
  165. tags: "some tag"
  166. owner.email: "bod@example.com" AND state: (new OR open OR pending*) # show all open tickets of a certain agent
  167. state:closed AND _missing_:tag # all closed objects without tags
  168. article_count: [1 TO 5] # tickets with 1 to 5 articles
  169. article_count: [10 TO *] # tickets with 10 or more articles
  170. article.from: bob # also article.from can be used
  171. article.body: heat~ # using the fuzzy operator will also find terms that are similar, in this case also "head"
  172. article.body: /joh?n(ath[oa]n)/ # using regular expressions
  173. user:M
  174. user:Max
  175. user:Max.
  176. user:Max*
  177. organization:A_B
  178. organization:A_B*
  179. user: M
  180. user: Max
  181. user: Max.
  182. user: Max*
  183. organization: A_B
  184. organization: A_B*
  185. id:123
  186. number:123
  187. id:"123"
  188. number:"123"
  189. QUERIES
  190. it 'returns the original query verbatim' do
  191. expect(queries.map { |query| described_class.append_wildcard_to_simple_query(query) })
  192. .to eq(queries)
  193. end
  194. end
  195. end
  196. describe '.remove', searchindex: true do
  197. context 'record gets deleted' do
  198. let(:record_type) { 'Ticket'.freeze }
  199. let(:deleted_record) { create(:ticket) }
  200. before do
  201. described_class.add(record_type, deleted_record)
  202. described_class.refresh
  203. end
  204. it 'removes record from search index' do
  205. described_class.remove(record_type, deleted_record.id)
  206. described_class.refresh
  207. result = described_class.search(deleted_record.number, record_type, sort_by: ['updated_at'], order_by: ['desc'])
  208. expect(result).to eq([])
  209. end
  210. context 'other records present' do
  211. let(:other_record) { create(:ticket) }
  212. before do
  213. described_class.add(record_type, other_record)
  214. described_class.refresh
  215. end
  216. it "doesn't remove other records" do
  217. described_class.remove(record_type, deleted_record.id)
  218. described_class.refresh
  219. result = described_class.search(other_record.number, record_type, sort_by: ['updated_at'], order_by: ['desc'])
  220. expect(result).to eq([{ id: other_record.id.to_s, type: record_type }])
  221. end
  222. end
  223. end
  224. end
  225. describe '.selectors', searchindex: true do
  226. let(:group1) { create(:group) }
  227. let(:organization1) { create(:organization, note: 'hihi') }
  228. let(:agent1) { create(:agent, organization: organization1, groups: [group1]) }
  229. let(:customer1) { create(:customer, organization: organization1, firstname: 'special-first-name') }
  230. let(:ticket1) do
  231. ticket = create(:ticket, title: 'some-title1', state_id: 1, created_by: agent1, group: group1)
  232. ticket.tag_add('t1', 1)
  233. ticket
  234. end
  235. let(:ticket2) do
  236. ticket = create(:ticket, title: 'some_title2', state_id: 4)
  237. ticket.tag_add('t2', 1)
  238. ticket
  239. end
  240. let(:ticket3) do
  241. ticket = create(:ticket, title: 'some::title3', state_id: 1)
  242. ticket.tag_add('t1', 1)
  243. ticket.tag_add('t2', 1)
  244. ticket
  245. end
  246. let(:ticket4) { create(:ticket, title: 'phrase some-title4', state_id: 1) }
  247. let(:ticket5) { create(:ticket, title: 'phrase some_title5', state_id: 1) }
  248. let(:ticket6) { create(:ticket, title: 'phrase some::title6', state_id: 1) }
  249. let(:ticket7) { create(:ticket, title: 'some title7', state_id: 1) }
  250. let(:ticket8) { create(:ticket, title: 'sometitle', group: group1, state_id: 1, owner: agent1, customer: customer1, organization: organization1) }
  251. let(:article8) { create(:ticket_article, ticket: ticket8, subject: 'lorem ipsum') }
  252. before do
  253. Ticket.destroy_all # needed to remove not created tickets
  254. travel(-1.hour)
  255. create(:mention, mentionable: ticket1, user: agent1)
  256. ticket1.search_index_update_backend
  257. travel 1.hour
  258. ticket2.search_index_update_backend
  259. travel 1.second
  260. ticket3.search_index_update_backend
  261. travel 1.second
  262. ticket4.search_index_update_backend
  263. travel 1.second
  264. ticket5.search_index_update_backend
  265. travel 1.second
  266. ticket6.search_index_update_backend
  267. travel 1.second
  268. ticket7.search_index_update_backend
  269. travel 1.hour
  270. article8.ticket.search_index_update_backend
  271. described_class.refresh
  272. end
  273. context 'when limit is used' do
  274. it 'finds 1 record' do
  275. result = described_class.selectors('Ticket', { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } }, { limit: 1 }, { field: 'created_at' })
  276. expect(result[:ticket_ids].count).to eq(1)
  277. end
  278. it 'finds 3 records' do
  279. result = described_class.selectors('Ticket', { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } }, { limit: 3 }, { field: 'created_at' })
  280. expect(result[:ticket_ids].count).to eq(3)
  281. end
  282. end
  283. context 'query with contains' do
  284. it 'finds records with till (relative)' do
  285. result = described_class.selectors('Ticket',
  286. { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } },
  287. {},
  288. {
  289. field: 'created_at', # sort to verify result
  290. })
  291. expect(result).to eq({ count: 7, ticket_ids: [ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  292. end
  293. it 'finds records with from (relative)' do
  294. result = described_class.selectors('Ticket',
  295. { 'ticket.created_at'=>{ 'operator' => 'from (relative)', 'value' => '30', 'range' => 'minute' } },
  296. {},
  297. {
  298. field: 'created_at', # sort to verify result
  299. })
  300. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  301. end
  302. it 'finds records with till (relative) including +1 hour ticket' do
  303. result = described_class.selectors('Ticket',
  304. { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '120', 'range' => 'minute' } },
  305. {},
  306. {
  307. field: 'created_at', # sort to verify result
  308. })
  309. expect(result).to eq({ count: 8, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  310. end
  311. it 'finds records with from (relative) including -1 hour ticket' do
  312. result = described_class.selectors('Ticket',
  313. { 'ticket.created_at'=>{ 'operator' => 'from (relative)', 'value' => '120', 'range' => 'minute' } },
  314. {},
  315. {
  316. field: 'created_at', # sort to verify result
  317. })
  318. expect(result).to eq({ count: 8, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  319. end
  320. it 'finds records with tags which contains all' do
  321. result = described_class.selectors('Ticket',
  322. { 'ticket.tags'=>{ 'operator' => 'contains all', 'value' => 't1, t2' } },
  323. {},
  324. {
  325. field: 'created_at', # sort to verify result
  326. })
  327. expect(result).to eq({ count: 1, ticket_ids: [ticket3.id.to_s] })
  328. end
  329. it 'finds records with tags which contains one' do
  330. result = described_class.selectors('Ticket',
  331. { 'ticket.tags'=>{ 'operator' => 'contains one', 'value' => 't1, t2' } },
  332. {},
  333. {
  334. field: 'created_at', # sort to verify result
  335. })
  336. expect(result).to eq({ count: 3, ticket_ids: [ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  337. end
  338. it 'finds records with tags which contains all not' do
  339. result = described_class.selectors('Ticket',
  340. { 'ticket.tags'=>{ 'operator' => 'contains all not', 'value' => 't2' } },
  341. {},
  342. {
  343. field: 'created_at', # sort to verify result
  344. })
  345. expect(result).to eq({ count: 6, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket1.id.to_s] })
  346. end
  347. it 'finds records with tags which contains one not' do
  348. result = described_class.selectors('Ticket',
  349. { 'ticket.tags'=>{ 'operator' => 'contains one not', 'value' => 't1' } },
  350. {},
  351. {
  352. field: 'created_at', # sort to verify result
  353. })
  354. expect(result).to eq({ count: 6, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket2.id.to_s] })
  355. end
  356. it 'finds records with organization note' do
  357. result = described_class.selectors('Ticket',
  358. {
  359. 'organization.note' => {
  360. 'operator' => 'contains',
  361. 'value' => 'hihi',
  362. },
  363. },
  364. {},
  365. {
  366. field: 'created_at', # sort to verify result
  367. })
  368. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  369. end
  370. it 'finds records with customer firstname' do
  371. result = described_class.selectors('Ticket',
  372. {
  373. 'customer.firstname' => {
  374. 'operator' => 'contains',
  375. 'value' => 'special',
  376. },
  377. },
  378. {},
  379. {
  380. field: 'created_at', # sort to verify result
  381. })
  382. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  383. end
  384. it 'finds records with article subject' do
  385. result = described_class.selectors('Ticket',
  386. {
  387. 'article.subject' => {
  388. 'operator' => 'contains',
  389. 'value' => 'ipsum',
  390. },
  391. },
  392. {},
  393. {
  394. field: 'created_at', # sort to verify result
  395. })
  396. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  397. end
  398. it 'finds records with pre_condition not_set' do
  399. result = described_class.selectors('Ticket',
  400. {
  401. 'created_by_id' => {
  402. 'pre_condition' => 'not_set',
  403. 'operator' => 'is',
  404. },
  405. },
  406. {},
  407. {
  408. field: 'created_at', # sort to verify result
  409. })
  410. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  411. end
  412. it 'finds records with pre_condition current_user.id' do
  413. result = described_class.selectors('Ticket',
  414. {
  415. 'owner_id' => {
  416. 'pre_condition' => 'current_user.id',
  417. 'operator' => 'is',
  418. },
  419. },
  420. { current_user: agent1 },
  421. {
  422. field: 'created_at', # sort to verify result
  423. })
  424. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  425. end
  426. it 'finds records with pre_condition current_user.organization_id' do
  427. result = described_class.selectors('Ticket',
  428. {
  429. 'organization_id' => {
  430. 'pre_condition' => 'current_user.organization_id',
  431. 'operator' => 'is',
  432. },
  433. },
  434. { current_user: agent1 },
  435. {
  436. field: 'created_at', # sort to verify result
  437. })
  438. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  439. end
  440. it 'finds records with containing phrase' do
  441. result = described_class.selectors('Ticket',
  442. {
  443. 'title' => {
  444. 'operator' => 'contains',
  445. 'value' => 'phrase',
  446. },
  447. },
  448. {},
  449. {
  450. field: 'created_at', # sort to verify result
  451. })
  452. expect(result).to eq({ count: 3, ticket_ids: [ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s] })
  453. end
  454. it 'finds records with containing some title7' do
  455. result = described_class.selectors('Ticket',
  456. 'title' => {
  457. 'operator' => 'contains',
  458. 'value' => 'some title7',
  459. })
  460. expect(result).to eq({ count: 1, ticket_ids: [ticket7.id.to_s] })
  461. end
  462. it 'finds records with containing -' do
  463. result = described_class.selectors('Ticket',
  464. 'title' => {
  465. 'operator' => 'contains',
  466. 'value' => 'some-title1',
  467. })
  468. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  469. end
  470. it 'finds records with containing _' do
  471. result = described_class.selectors('Ticket',
  472. 'title' => {
  473. 'operator' => 'contains',
  474. 'value' => 'some_title2',
  475. })
  476. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  477. end
  478. it 'finds records with containing ::' do
  479. result = described_class.selectors('Ticket',
  480. 'title' => {
  481. 'operator' => 'contains',
  482. 'value' => 'some::title3',
  483. })
  484. expect(result).to eq({ count: 1, ticket_ids: [ticket3.id.to_s] })
  485. end
  486. it 'finds records with containing 4' do
  487. result = described_class.selectors('Ticket',
  488. 'state_id' => {
  489. 'operator' => 'contains',
  490. 'value' => 4,
  491. })
  492. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  493. end
  494. it 'finds records with containing "4"' do
  495. result = described_class.selectors('Ticket',
  496. 'state_id' => {
  497. 'operator' => 'contains',
  498. 'value' => '4',
  499. })
  500. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  501. end
  502. end
  503. context 'query with contains not' do
  504. it 'finds records with containing not phrase' do
  505. result = described_class.selectors('Ticket',
  506. {
  507. 'title' => {
  508. 'operator' => 'contains not',
  509. 'value' => 'phrase',
  510. },
  511. },
  512. {},
  513. {
  514. field: 'created_at', # sort to verify result
  515. })
  516. expect(result).to eq({ count: 5, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  517. end
  518. it 'finds records with containing not some title7' do
  519. result = described_class.selectors('Ticket',
  520. 'title' => {
  521. 'operator' => 'contains not',
  522. 'value' => 'some title7',
  523. })
  524. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  525. end
  526. it 'finds records with containing not -' do
  527. result = described_class.selectors('Ticket',
  528. 'title' => {
  529. 'operator' => 'contains not',
  530. 'value' => 'some-title1',
  531. })
  532. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  533. end
  534. it 'finds records with containing not _' do
  535. result = described_class.selectors('Ticket',
  536. 'title' => {
  537. 'operator' => 'contains not',
  538. 'value' => 'some_title2',
  539. })
  540. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket1.id.to_s] })
  541. end
  542. it 'finds records with containing not ::' do
  543. result = described_class.selectors('Ticket',
  544. 'title' => {
  545. 'operator' => 'contains not',
  546. 'value' => 'some::title3',
  547. })
  548. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  549. end
  550. it 'finds records with containing not 4' do
  551. result = described_class.selectors('Ticket',
  552. 'state_id' => {
  553. 'operator' => 'contains not',
  554. 'value' => 4,
  555. })
  556. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket1.id.to_s] })
  557. end
  558. it 'finds records with containing not "4"' do
  559. result = described_class.selectors('Ticket',
  560. 'state_id' => {
  561. 'operator' => 'contains not',
  562. 'value' => '4',
  563. })
  564. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket1.id.to_s] })
  565. end
  566. end
  567. context 'query with is' do
  568. it 'finds records with is phrase' do
  569. result = described_class.selectors('Ticket',
  570. 'title' => {
  571. 'operator' => 'is',
  572. 'value' => 'phrase',
  573. })
  574. expect(result).to eq({ count: 0, ticket_ids: [] })
  575. end
  576. it 'finds records with is some title7' do
  577. result = described_class.selectors('Ticket',
  578. 'title' => {
  579. 'operator' => 'is',
  580. 'value' => 'some title7',
  581. })
  582. expect(result).to eq({ count: 1, ticket_ids: [ticket7.id.to_s] })
  583. end
  584. it 'finds records with is -' do
  585. result = described_class.selectors('Ticket',
  586. 'title' => {
  587. 'operator' => 'is',
  588. 'value' => 'some-title1',
  589. })
  590. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  591. end
  592. it 'finds records with is _' do
  593. result = described_class.selectors('Ticket',
  594. 'title' => {
  595. 'operator' => 'is',
  596. 'value' => 'some_title2',
  597. })
  598. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  599. end
  600. it 'finds records with is ::' do
  601. result = described_class.selectors('Ticket',
  602. 'title' => {
  603. 'operator' => 'is',
  604. 'value' => 'some::title3',
  605. })
  606. expect(result).to eq({ count: 1, ticket_ids: [ticket3.id.to_s] })
  607. end
  608. it 'finds records with is 4' do
  609. result = described_class.selectors('Ticket',
  610. 'state_id' => {
  611. 'operator' => 'is',
  612. 'value' => 4,
  613. })
  614. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  615. end
  616. it 'finds records with is "4"' do
  617. result = described_class.selectors('Ticket',
  618. 'state_id' => {
  619. 'operator' => 'is',
  620. 'value' => '4',
  621. })
  622. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  623. end
  624. end
  625. context 'query with is not' do
  626. it 'finds records with is not phrase' do
  627. result = described_class.selectors('Ticket',
  628. 'title' => {
  629. 'operator' => 'is not',
  630. 'value' => 'phrase',
  631. })
  632. expect(result).to eq({ count: 8, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  633. end
  634. it 'finds records with is not some title7' do
  635. result = described_class.selectors('Ticket',
  636. 'title' => {
  637. 'operator' => 'is not',
  638. 'value' => 'some title7',
  639. })
  640. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  641. end
  642. it 'finds records with is not -' do
  643. result = described_class.selectors('Ticket',
  644. 'title' => {
  645. 'operator' => 'is not',
  646. 'value' => 'some-title1',
  647. })
  648. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  649. end
  650. it 'finds records with is not _' do
  651. result = described_class.selectors('Ticket',
  652. 'title' => {
  653. 'operator' => 'is not',
  654. 'value' => 'some_title2',
  655. })
  656. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket1.id.to_s] })
  657. end
  658. it 'finds records with is not ::' do
  659. result = described_class.selectors('Ticket',
  660. 'title' => {
  661. 'operator' => 'is not',
  662. 'value' => 'some::title3',
  663. })
  664. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  665. end
  666. it 'finds records with is not 4' do
  667. result = described_class.selectors('Ticket',
  668. 'state_id' => {
  669. 'operator' => 'is not',
  670. 'value' => 4,
  671. })
  672. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket1.id.to_s] })
  673. end
  674. it 'finds records with is not "4"' do
  675. result = described_class.selectors('Ticket',
  676. 'state_id' => {
  677. 'operator' => 'is not',
  678. 'value' => '4',
  679. })
  680. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket1.id.to_s] })
  681. end
  682. it 'finds records with is not state_id ["4"] and title ["sometitle"]' do
  683. result = described_class.selectors('Ticket',
  684. 'state_id' => {
  685. 'operator' => 'is not',
  686. 'value' => ['4'],
  687. },
  688. 'title' => {
  689. 'operator' => 'is',
  690. 'value' => ['sometitle'],
  691. })
  692. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  693. end
  694. end
  695. context 'mentions' do
  696. it 'finds records with pre_condition is not_set' do
  697. result = described_class.selectors('Ticket',
  698. {
  699. 'ticket.mention_user_ids' => {
  700. 'pre_condition' => 'not_set',
  701. 'operator' => 'is',
  702. },
  703. },
  704. { current_user: agent1 },
  705. {
  706. field: 'created_at', # sort to verify result
  707. })
  708. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  709. end
  710. it 'finds records with pre_condition is not not_set' do
  711. result = described_class.selectors('Ticket',
  712. {
  713. 'ticket.mention_user_ids' => {
  714. 'pre_condition' => 'not_set',
  715. 'operator' => 'is not',
  716. },
  717. },
  718. { current_user: agent1 },
  719. {
  720. field: 'created_at', # sort to verify result
  721. })
  722. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  723. end
  724. it 'finds records with pre_condition is current_user.id' do
  725. result = described_class.selectors('Ticket',
  726. {
  727. 'ticket.mention_user_ids' => {
  728. 'pre_condition' => 'current_user.id',
  729. 'operator' => 'is',
  730. },
  731. },
  732. { current_user: agent1 },
  733. {
  734. field: 'created_at', # sort to verify result
  735. })
  736. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  737. end
  738. it 'finds records with pre_condition is not current_user.id' do
  739. result = described_class.selectors('Ticket',
  740. {
  741. 'ticket.mention_user_ids' => {
  742. 'pre_condition' => 'current_user.id',
  743. 'operator' => 'is not',
  744. },
  745. },
  746. { current_user: agent1 },
  747. {
  748. field: 'created_at', # sort to verify result
  749. })
  750. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  751. end
  752. it 'finds records with pre_condition is specific' do
  753. result = described_class.selectors('Ticket',
  754. {
  755. 'ticket.mention_user_ids' => {
  756. 'pre_condition' => 'specific',
  757. 'operator' => 'is',
  758. 'value' => agent1.id,
  759. },
  760. },
  761. {},
  762. {
  763. field: 'created_at', # sort to verify result
  764. })
  765. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  766. end
  767. it 'finds records with pre_condition is not specific' do
  768. result = described_class.selectors('Ticket',
  769. {
  770. 'ticket.mention_user_ids' => {
  771. 'pre_condition' => 'specific',
  772. 'operator' => 'is not',
  773. 'value' => agent1.id,
  774. },
  775. },
  776. {},
  777. {
  778. field: 'created_at', # sort to verify result
  779. })
  780. expect(result).to eq({ count: 7, ticket_ids: [ticket8.id.to_s, ticket7.id.to_s, ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s, ticket3.id.to_s, ticket2.id.to_s] })
  781. end
  782. end
  783. end
  784. describe '.search_by_index_sort' do
  785. it 'does return a sort value' do
  786. expect(described_class.search_by_index_sort(index: 'Ticket', sort_by: ['title'], order_by: 'desc')).to eq([{ 'title.keyword'=>{ order: 'd' } }, '_score'])
  787. end
  788. it 'does return a sort value for fulltext searches' do
  789. expect(described_class.search_by_index_sort(index: 'Ticket', sort_by: ['title'], order_by: 'desc', fulltext: true)).to eq([{ 'title.keyword'=>{ order: 'd' } }, '_score'])
  790. end
  791. it 'does return a default sort value' do
  792. expect(described_class.search_by_index_sort(index: 'Ticket')).to eq([{ updated_at: { order: 'desc' } }, '_score'])
  793. end
  794. it 'does return a default sort value for fulltext searches' do
  795. expect(described_class.search_by_index_sort(index: 'Ticket', fulltext: true)).to eq(['_score'])
  796. end
  797. end
  798. end