search_index_backend_spec.rb 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  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('', 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. end
  116. describe '.append_wildcard_to_simple_query' do
  117. context 'with "simple" queries' do
  118. let(:queries) { <<~QUERIES.lines.map { |x| x.split('#')[0] }.map(&:strip) }
  119. M
  120. Max
  121. Max. # dot and underscore are acceptable characters in simple queries
  122. A_
  123. A_B
  124. äöü
  125. 123
  126. *ax # wildcards are allowed in simple queries
  127. Max*
  128. M*x
  129. M?x
  130. test@example.com
  131. test@example.
  132. test@example
  133. test@
  134. QUERIES
  135. it 'appends a * to the original query' do
  136. expect(queries.map { |query| described_class.append_wildcard_to_simple_query(query) })
  137. .to eq(queries.map { |q| "#{q}*" })
  138. end
  139. end
  140. context 'with "complex" queries (using search operators)' do
  141. let(:queries) { <<~QUERIES.lines.map { |x| x.split('#')[0] }.map(&:strip) }
  142. 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)
  143. title:"some wor*" # exact phrase beginning with "some wor*" will be searched
  144. created_at:[2017-01-01 TO 2017-12-31] # a time range
  145. created_at:>now-1h # created within last hour
  146. state:new OR state:open
  147. (state:new OR state:open) OR priority:"3 normal"
  148. (state:new OR state:open) AND customer.lastname:smith
  149. state:(new OR open) AND title:(full text search) # state: new OR open & title: full OR text OR search
  150. tags: "some tag"
  151. owner.email: "bod@example.com" AND state: (new OR open OR pending*) # show all open tickets of a certain agent
  152. state:closed AND _missing_:tag # all closed objects without tags
  153. article_count: [1 TO 5] # tickets with 1 to 5 articles
  154. article_count: [10 TO *] # tickets with 10 or more articles
  155. article.from: bob # also article.from can be used
  156. article.body: heat~ # using the fuzzy operator will also find terms that are similar, in this case also "head"
  157. article.body: /joh?n(ath[oa]n)/ # using regular expressions
  158. user:M
  159. user:Max
  160. user:Max.
  161. user:Max*
  162. organization:A_B
  163. organization:A_B*
  164. user: M
  165. user: Max
  166. user: Max.
  167. user: Max*
  168. organization: A_B
  169. organization: A_B*
  170. id:123
  171. number:123
  172. id:"123"
  173. number:"123"
  174. QUERIES
  175. it 'returns the original query verbatim' do
  176. expect(queries.map { |query| described_class.append_wildcard_to_simple_query(query) })
  177. .to eq(queries)
  178. end
  179. end
  180. end
  181. describe '.remove', searchindex: true do
  182. context 'record gets deleted' do
  183. let(:record_type) { 'Ticket'.freeze }
  184. let(:deleted_record) { create(:ticket) }
  185. before do
  186. described_class.add(record_type, deleted_record)
  187. described_class.refresh
  188. end
  189. it 'removes record from search index' do
  190. described_class.remove(record_type, deleted_record.id)
  191. described_class.refresh
  192. result = described_class.search(deleted_record.number, record_type, sort_by: ['updated_at'], order_by: ['desc'])
  193. expect(result).to eq([])
  194. end
  195. context 'other records present' do
  196. let(:other_record) { create(:ticket) }
  197. before do
  198. described_class.add(record_type, other_record)
  199. described_class.refresh
  200. end
  201. it "doesn't remove other records" do
  202. described_class.remove(record_type, deleted_record.id)
  203. described_class.refresh
  204. result = described_class.search(other_record.number, record_type, sort_by: ['updated_at'], order_by: ['desc'])
  205. expect(result).to eq([{ id: other_record.id.to_s, type: record_type }])
  206. end
  207. end
  208. end
  209. end
  210. describe '.selectors', searchindex: true do
  211. let(:group1) { create(:group) }
  212. let(:organization1) { create(:organization, note: 'hihi') }
  213. let(:agent1) { create(:agent, organization: organization1, groups: [group1]) }
  214. let(:customer1) { create(:customer, organization: organization1, firstname: 'special-first-name') }
  215. let(:ticket1) do
  216. ticket = create(:ticket, title: 'some-title1', state_id: 1, created_by: agent1, group: group1)
  217. ticket.tag_add('t1', 1)
  218. ticket
  219. end
  220. let(:ticket2) do
  221. ticket = create(:ticket, title: 'some_title2', state_id: 4)
  222. ticket.tag_add('t2', 1)
  223. ticket
  224. end
  225. let(:ticket3) do
  226. ticket = create(:ticket, title: 'some::title3', state_id: 1)
  227. ticket.tag_add('t1', 1)
  228. ticket.tag_add('t2', 1)
  229. ticket
  230. end
  231. let(:ticket4) { create(:ticket, title: 'phrase some-title4', state_id: 1) }
  232. let(:ticket5) { create(:ticket, title: 'phrase some_title5', state_id: 1) }
  233. let(:ticket6) { create(:ticket, title: 'phrase some::title6', state_id: 1) }
  234. let(:ticket7) { create(:ticket, title: 'some title7', state_id: 1) }
  235. let(:ticket8) { create(:ticket, title: 'sometitle', group: group1, state_id: 1, owner: agent1, customer: customer1, organization: organization1) }
  236. let(:article8) { create(:ticket_article, ticket: ticket8, subject: 'lorem ipsum') }
  237. before do
  238. Ticket.destroy_all # needed to remove not created tickets
  239. travel(-1.hour)
  240. create(:mention, mentionable: ticket1, user: agent1)
  241. ticket1.search_index_update_backend
  242. travel 1.hour
  243. ticket2.search_index_update_backend
  244. travel 1.second
  245. ticket3.search_index_update_backend
  246. travel 1.second
  247. ticket4.search_index_update_backend
  248. travel 1.second
  249. ticket5.search_index_update_backend
  250. travel 1.second
  251. ticket6.search_index_update_backend
  252. travel 1.second
  253. ticket7.search_index_update_backend
  254. travel 1.hour
  255. article8.ticket.search_index_update_backend
  256. described_class.refresh
  257. end
  258. context 'when limit is used' do
  259. it 'finds 1 record' do
  260. result = described_class.selectors('Ticket', { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } }, { limit: 1 }, { field: 'created_at' })
  261. expect(result[:ticket_ids].count).to eq(1)
  262. end
  263. it 'finds 3 records' do
  264. result = described_class.selectors('Ticket', { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } }, { limit: 3 }, { field: 'created_at' })
  265. expect(result[:ticket_ids].count).to eq(3)
  266. end
  267. end
  268. context 'query with contains' do
  269. it 'finds records with till (relative)' do
  270. result = described_class.selectors('Ticket',
  271. { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } },
  272. {},
  273. {
  274. field: 'created_at', # sort to verify result
  275. })
  276. 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] })
  277. end
  278. it 'finds records with from (relative)' do
  279. result = described_class.selectors('Ticket',
  280. { 'ticket.created_at'=>{ 'operator' => 'from (relative)', 'value' => '30', 'range' => 'minute' } },
  281. {},
  282. {
  283. field: 'created_at', # sort to verify result
  284. })
  285. 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] })
  286. end
  287. it 'finds records with till (relative) including +1 hour ticket' do
  288. result = described_class.selectors('Ticket',
  289. { 'ticket.created_at'=>{ 'operator' => 'till (relative)', 'value' => '120', 'range' => 'minute' } },
  290. {},
  291. {
  292. field: 'created_at', # sort to verify result
  293. })
  294. 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] })
  295. end
  296. it 'finds records with from (relative) including -1 hour ticket' do
  297. result = described_class.selectors('Ticket',
  298. { 'ticket.created_at'=>{ 'operator' => 'from (relative)', 'value' => '120', 'range' => 'minute' } },
  299. {},
  300. {
  301. field: 'created_at', # sort to verify result
  302. })
  303. 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] })
  304. end
  305. it 'finds records with tags which contains all' do
  306. result = described_class.selectors('Ticket',
  307. { 'ticket.tags'=>{ 'operator' => 'contains all', 'value' => 't1, t2' } },
  308. {},
  309. {
  310. field: 'created_at', # sort to verify result
  311. })
  312. expect(result).to eq({ count: 1, ticket_ids: [ticket3.id.to_s] })
  313. end
  314. it 'finds records with tags which contains one' do
  315. result = described_class.selectors('Ticket',
  316. { 'ticket.tags'=>{ 'operator' => 'contains one', 'value' => 't1, t2' } },
  317. {},
  318. {
  319. field: 'created_at', # sort to verify result
  320. })
  321. expect(result).to eq({ count: 3, ticket_ids: [ticket3.id.to_s, ticket2.id.to_s, ticket1.id.to_s] })
  322. end
  323. it 'finds records with tags which contains all not' do
  324. result = described_class.selectors('Ticket',
  325. { 'ticket.tags'=>{ 'operator' => 'contains all not', 'value' => 't2' } },
  326. {},
  327. {
  328. field: 'created_at', # sort to verify result
  329. })
  330. 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] })
  331. end
  332. it 'finds records with tags which contains one not' do
  333. result = described_class.selectors('Ticket',
  334. { 'ticket.tags'=>{ 'operator' => 'contains one not', 'value' => 't1' } },
  335. {},
  336. {
  337. field: 'created_at', # sort to verify result
  338. })
  339. 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] })
  340. end
  341. it 'finds records with organization note' do
  342. result = described_class.selectors('Ticket',
  343. {
  344. 'organization.note' => {
  345. 'operator' => 'contains',
  346. 'value' => 'hihi',
  347. },
  348. },
  349. {},
  350. {
  351. field: 'created_at', # sort to verify result
  352. })
  353. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  354. end
  355. it 'finds records with customer firstname' do
  356. result = described_class.selectors('Ticket',
  357. {
  358. 'customer.firstname' => {
  359. 'operator' => 'contains',
  360. 'value' => 'special',
  361. },
  362. },
  363. {},
  364. {
  365. field: 'created_at', # sort to verify result
  366. })
  367. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  368. end
  369. it 'finds records with article subject' do
  370. result = described_class.selectors('Ticket',
  371. {
  372. 'article.subject' => {
  373. 'operator' => 'contains',
  374. 'value' => 'ipsum',
  375. },
  376. },
  377. {},
  378. {
  379. field: 'created_at', # sort to verify result
  380. })
  381. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  382. end
  383. it 'finds records with pre_condition not_set' do
  384. result = described_class.selectors('Ticket',
  385. {
  386. 'created_by_id' => {
  387. 'pre_condition' => 'not_set',
  388. 'operator' => 'is',
  389. },
  390. },
  391. {},
  392. {
  393. field: 'created_at', # sort to verify result
  394. })
  395. 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] })
  396. end
  397. it 'finds records with pre_condition current_user.id' do
  398. result = described_class.selectors('Ticket',
  399. {
  400. 'owner_id' => {
  401. 'pre_condition' => 'current_user.id',
  402. 'operator' => 'is',
  403. },
  404. },
  405. { current_user: agent1 },
  406. {
  407. field: 'created_at', # sort to verify result
  408. })
  409. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  410. end
  411. it 'finds records with pre_condition current_user.organization_id' do
  412. result = described_class.selectors('Ticket',
  413. {
  414. 'organization_id' => {
  415. 'pre_condition' => 'current_user.organization_id',
  416. 'operator' => 'is',
  417. },
  418. },
  419. { current_user: agent1 },
  420. {
  421. field: 'created_at', # sort to verify result
  422. })
  423. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  424. end
  425. it 'finds records with containing phrase' do
  426. result = described_class.selectors('Ticket',
  427. {
  428. 'title' => {
  429. 'operator' => 'contains',
  430. 'value' => 'phrase',
  431. },
  432. },
  433. {},
  434. {
  435. field: 'created_at', # sort to verify result
  436. })
  437. expect(result).to eq({ count: 3, ticket_ids: [ticket6.id.to_s, ticket5.id.to_s, ticket4.id.to_s] })
  438. end
  439. it 'finds records with containing some title7' do
  440. result = described_class.selectors('Ticket',
  441. 'title' => {
  442. 'operator' => 'contains',
  443. 'value' => 'some title7',
  444. })
  445. expect(result).to eq({ count: 1, ticket_ids: [ticket7.id.to_s] })
  446. end
  447. it 'finds records with containing -' do
  448. result = described_class.selectors('Ticket',
  449. 'title' => {
  450. 'operator' => 'contains',
  451. 'value' => 'some-title1',
  452. })
  453. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  454. end
  455. it 'finds records with containing _' do
  456. result = described_class.selectors('Ticket',
  457. 'title' => {
  458. 'operator' => 'contains',
  459. 'value' => 'some_title2',
  460. })
  461. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  462. end
  463. it 'finds records with containing ::' do
  464. result = described_class.selectors('Ticket',
  465. 'title' => {
  466. 'operator' => 'contains',
  467. 'value' => 'some::title3',
  468. })
  469. expect(result).to eq({ count: 1, ticket_ids: [ticket3.id.to_s] })
  470. end
  471. it 'finds records with containing 4' do
  472. result = described_class.selectors('Ticket',
  473. 'state_id' => {
  474. 'operator' => 'contains',
  475. 'value' => 4,
  476. })
  477. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  478. end
  479. it 'finds records with containing "4"' do
  480. result = described_class.selectors('Ticket',
  481. 'state_id' => {
  482. 'operator' => 'contains',
  483. 'value' => '4',
  484. })
  485. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  486. end
  487. end
  488. context 'query with contains not' do
  489. it 'finds records with containing not phrase' do
  490. result = described_class.selectors('Ticket',
  491. {
  492. 'title' => {
  493. 'operator' => 'contains not',
  494. 'value' => 'phrase',
  495. },
  496. },
  497. {},
  498. {
  499. field: 'created_at', # sort to verify result
  500. })
  501. 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] })
  502. end
  503. it 'finds records with containing not some title7' do
  504. result = described_class.selectors('Ticket',
  505. 'title' => {
  506. 'operator' => 'contains not',
  507. 'value' => 'some title7',
  508. })
  509. 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] })
  510. end
  511. it 'finds records with containing not -' do
  512. result = described_class.selectors('Ticket',
  513. 'title' => {
  514. 'operator' => 'contains not',
  515. 'value' => 'some-title1',
  516. })
  517. 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] })
  518. end
  519. it 'finds records with containing not _' do
  520. result = described_class.selectors('Ticket',
  521. 'title' => {
  522. 'operator' => 'contains not',
  523. 'value' => 'some_title2',
  524. })
  525. 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] })
  526. end
  527. it 'finds records with containing not ::' do
  528. result = described_class.selectors('Ticket',
  529. 'title' => {
  530. 'operator' => 'contains not',
  531. 'value' => 'some::title3',
  532. })
  533. 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] })
  534. end
  535. it 'finds records with containing not 4' do
  536. result = described_class.selectors('Ticket',
  537. 'state_id' => {
  538. 'operator' => 'contains not',
  539. 'value' => 4,
  540. })
  541. 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] })
  542. end
  543. it 'finds records with containing not "4"' do
  544. result = described_class.selectors('Ticket',
  545. 'state_id' => {
  546. 'operator' => 'contains not',
  547. 'value' => '4',
  548. })
  549. 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] })
  550. end
  551. end
  552. context 'query with is' do
  553. it 'finds records with is phrase' do
  554. result = described_class.selectors('Ticket',
  555. 'title' => {
  556. 'operator' => 'is',
  557. 'value' => 'phrase',
  558. })
  559. expect(result).to eq({ count: 0, ticket_ids: [] })
  560. end
  561. it 'finds records with is some title7' do
  562. result = described_class.selectors('Ticket',
  563. 'title' => {
  564. 'operator' => 'is',
  565. 'value' => 'some title7',
  566. })
  567. expect(result).to eq({ count: 1, ticket_ids: [ticket7.id.to_s] })
  568. end
  569. it 'finds records with is -' do
  570. result = described_class.selectors('Ticket',
  571. 'title' => {
  572. 'operator' => 'is',
  573. 'value' => 'some-title1',
  574. })
  575. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  576. end
  577. it 'finds records with is _' do
  578. result = described_class.selectors('Ticket',
  579. 'title' => {
  580. 'operator' => 'is',
  581. 'value' => 'some_title2',
  582. })
  583. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  584. end
  585. it 'finds records with is ::' do
  586. result = described_class.selectors('Ticket',
  587. 'title' => {
  588. 'operator' => 'is',
  589. 'value' => 'some::title3',
  590. })
  591. expect(result).to eq({ count: 1, ticket_ids: [ticket3.id.to_s] })
  592. end
  593. it 'finds records with is 4' do
  594. result = described_class.selectors('Ticket',
  595. 'state_id' => {
  596. 'operator' => 'is',
  597. 'value' => 4,
  598. })
  599. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  600. end
  601. it 'finds records with is "4"' do
  602. result = described_class.selectors('Ticket',
  603. 'state_id' => {
  604. 'operator' => 'is',
  605. 'value' => '4',
  606. })
  607. expect(result).to eq({ count: 1, ticket_ids: [ticket2.id.to_s] })
  608. end
  609. end
  610. context 'query with is not' do
  611. it 'finds records with is not phrase' do
  612. result = described_class.selectors('Ticket',
  613. 'title' => {
  614. 'operator' => 'is not',
  615. 'value' => 'phrase',
  616. })
  617. 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] })
  618. end
  619. it 'finds records with is not some title7' do
  620. result = described_class.selectors('Ticket',
  621. 'title' => {
  622. 'operator' => 'is not',
  623. 'value' => 'some title7',
  624. })
  625. 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] })
  626. end
  627. it 'finds records with is not -' do
  628. result = described_class.selectors('Ticket',
  629. 'title' => {
  630. 'operator' => 'is not',
  631. 'value' => 'some-title1',
  632. })
  633. 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] })
  634. end
  635. it 'finds records with is not _' do
  636. result = described_class.selectors('Ticket',
  637. 'title' => {
  638. 'operator' => 'is not',
  639. 'value' => 'some_title2',
  640. })
  641. 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] })
  642. end
  643. it 'finds records with is not ::' do
  644. result = described_class.selectors('Ticket',
  645. 'title' => {
  646. 'operator' => 'is not',
  647. 'value' => 'some::title3',
  648. })
  649. 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] })
  650. end
  651. it 'finds records with is not 4' do
  652. result = described_class.selectors('Ticket',
  653. 'state_id' => {
  654. 'operator' => 'is not',
  655. 'value' => 4,
  656. })
  657. 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] })
  658. end
  659. it 'finds records with is not "4"' do
  660. result = described_class.selectors('Ticket',
  661. 'state_id' => {
  662. 'operator' => 'is not',
  663. 'value' => '4',
  664. })
  665. 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] })
  666. end
  667. it 'finds records with is not state_id ["4"] and title ["sometitle"]' do
  668. result = described_class.selectors('Ticket',
  669. 'state_id' => {
  670. 'operator' => 'is not',
  671. 'value' => ['4'],
  672. },
  673. 'title' => {
  674. 'operator' => 'is',
  675. 'value' => ['sometitle'],
  676. })
  677. expect(result).to eq({ count: 1, ticket_ids: [ticket8.id.to_s] })
  678. end
  679. end
  680. context 'mentions' do
  681. it 'finds records with pre_condition is not_set' do
  682. result = described_class.selectors('Ticket',
  683. {
  684. 'ticket.mention_user_ids' => {
  685. 'pre_condition' => 'not_set',
  686. 'operator' => 'is',
  687. },
  688. },
  689. { current_user: agent1 },
  690. {
  691. field: 'created_at', # sort to verify result
  692. })
  693. 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] })
  694. end
  695. it 'finds records with pre_condition is not not_set' do
  696. result = described_class.selectors('Ticket',
  697. {
  698. 'ticket.mention_user_ids' => {
  699. 'pre_condition' => 'not_set',
  700. 'operator' => 'is not',
  701. },
  702. },
  703. { current_user: agent1 },
  704. {
  705. field: 'created_at', # sort to verify result
  706. })
  707. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  708. end
  709. it 'finds records with pre_condition is current_user.id' do
  710. result = described_class.selectors('Ticket',
  711. {
  712. 'ticket.mention_user_ids' => {
  713. 'pre_condition' => 'current_user.id',
  714. 'operator' => 'is',
  715. },
  716. },
  717. { current_user: agent1 },
  718. {
  719. field: 'created_at', # sort to verify result
  720. })
  721. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  722. end
  723. it 'finds records with pre_condition is not current_user.id' do
  724. result = described_class.selectors('Ticket',
  725. {
  726. 'ticket.mention_user_ids' => {
  727. 'pre_condition' => 'current_user.id',
  728. 'operator' => 'is not',
  729. },
  730. },
  731. { current_user: agent1 },
  732. {
  733. field: 'created_at', # sort to verify result
  734. })
  735. 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] })
  736. end
  737. it 'finds records with pre_condition is specific' do
  738. result = described_class.selectors('Ticket',
  739. {
  740. 'ticket.mention_user_ids' => {
  741. 'pre_condition' => 'specific',
  742. 'operator' => 'is',
  743. 'value' => agent1.id,
  744. },
  745. },
  746. {},
  747. {
  748. field: 'created_at', # sort to verify result
  749. })
  750. expect(result).to eq({ count: 1, ticket_ids: [ticket1.id.to_s] })
  751. end
  752. it 'finds records with pre_condition is not specific' do
  753. result = described_class.selectors('Ticket',
  754. {
  755. 'ticket.mention_user_ids' => {
  756. 'pre_condition' => 'specific',
  757. 'operator' => 'is not',
  758. 'value' => agent1.id,
  759. },
  760. },
  761. {},
  762. {
  763. field: 'created_at', # sort to verify result
  764. })
  765. 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] })
  766. end
  767. end
  768. end
  769. describe '.search_by_index_sort' do
  770. it 'does return a sort value' do
  771. expect(described_class.search_by_index_sort(sort_by: ['title'], order_by: 'desc')).to eq([{ 'title.keyword'=>{ order: 'd' } }, '_score'])
  772. end
  773. it 'does return a sort value for fulltext searches' do
  774. expect(described_class.search_by_index_sort(sort_by: ['title'], order_by: 'desc', fulltext: true)).to eq([{ 'title.keyword'=>{ order: 'd' } }, '_score'])
  775. end
  776. it 'does return a default sort value' do
  777. expect(described_class.search_by_index_sort).to eq([{ updated_at: { order: 'desc' } }, '_score'])
  778. end
  779. it 'does return a default sort value for fulltext searches' do
  780. expect(described_class.search_by_index_sort(fulltext: true)).to eq(['_score'])
  781. end
  782. end
  783. end