index_alias_impl_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. // Copyright (c) 2014 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package bleve
  15. import (
  16. "context"
  17. "fmt"
  18. "reflect"
  19. "testing"
  20. "time"
  21. "github.com/blevesearch/bleve/document"
  22. "github.com/blevesearch/bleve/index"
  23. "github.com/blevesearch/bleve/index/store"
  24. "github.com/blevesearch/bleve/mapping"
  25. "github.com/blevesearch/bleve/numeric"
  26. "github.com/blevesearch/bleve/search"
  27. )
  28. func TestIndexAliasSingle(t *testing.T) {
  29. expectedError := fmt.Errorf("expected")
  30. ei1 := &stubIndex{
  31. err: expectedError,
  32. }
  33. alias := NewIndexAlias(ei1)
  34. err := alias.Index("a", "a")
  35. if err != expectedError {
  36. t.Errorf("expected %v, got %v", expectedError, err)
  37. }
  38. err = alias.Delete("a")
  39. if err != expectedError {
  40. t.Errorf("expected %v, got %v", expectedError, err)
  41. }
  42. batch := alias.NewBatch()
  43. err = alias.Batch(batch)
  44. if err != expectedError {
  45. t.Errorf("expected %v, got %v", expectedError, err)
  46. }
  47. _, err = alias.Document("a")
  48. if err != expectedError {
  49. t.Errorf("expected %v, got %v", expectedError, err)
  50. }
  51. _, err = alias.Fields()
  52. if err != expectedError {
  53. t.Errorf("expected %v, got %v", expectedError, err)
  54. }
  55. _, err = alias.GetInternal([]byte("a"))
  56. if err != expectedError {
  57. t.Errorf("expected %v, got %v", expectedError, err)
  58. }
  59. err = alias.SetInternal([]byte("a"), []byte("a"))
  60. if err != expectedError {
  61. t.Errorf("expected %v, got %v", expectedError, err)
  62. }
  63. err = alias.DeleteInternal([]byte("a"))
  64. if err != expectedError {
  65. t.Errorf("expected %v, got %v", expectedError, err)
  66. }
  67. mapping := alias.Mapping()
  68. if mapping != nil {
  69. t.Errorf("expected nil, got %v", mapping)
  70. }
  71. indexStat := alias.Stats()
  72. if indexStat != nil {
  73. t.Errorf("expected nil, got %v", indexStat)
  74. }
  75. // now a few things that should work
  76. sr := NewSearchRequest(NewTermQuery("test"))
  77. _, err = alias.Search(sr)
  78. if err != expectedError {
  79. t.Errorf("expected %v, got %v", expectedError, err)
  80. }
  81. count, err := alias.DocCount()
  82. if err != nil {
  83. t.Errorf("expected no error, got %v", err)
  84. }
  85. if count != 0 {
  86. t.Errorf("expected count 0, got %d", count)
  87. }
  88. // now change the def using add/remove
  89. expectedError2 := fmt.Errorf("expected2")
  90. ei2 := &stubIndex{
  91. err: expectedError2,
  92. }
  93. alias.Add(ei2)
  94. alias.Remove(ei1)
  95. err = alias.Index("a", "a")
  96. if err != expectedError2 {
  97. t.Errorf("expected %v, got %v", expectedError2, err)
  98. }
  99. err = alias.Delete("a")
  100. if err != expectedError2 {
  101. t.Errorf("expected %v, got %v", expectedError2, err)
  102. }
  103. err = alias.Batch(batch)
  104. if err != expectedError2 {
  105. t.Errorf("expected %v, got %v", expectedError2, err)
  106. }
  107. _, err = alias.Document("a")
  108. if err != expectedError2 {
  109. t.Errorf("expected %v, got %v", expectedError2, err)
  110. }
  111. _, err = alias.Fields()
  112. if err != expectedError2 {
  113. t.Errorf("expected %v, got %v", expectedError2, err)
  114. }
  115. _, err = alias.GetInternal([]byte("a"))
  116. if err != expectedError2 {
  117. t.Errorf("expected %v, got %v", expectedError2, err)
  118. }
  119. err = alias.SetInternal([]byte("a"), []byte("a"))
  120. if err != expectedError2 {
  121. t.Errorf("expected %v, got %v", expectedError2, err)
  122. }
  123. err = alias.DeleteInternal([]byte("a"))
  124. if err != expectedError2 {
  125. t.Errorf("expected %v, got %v", expectedError2, err)
  126. }
  127. mapping = alias.Mapping()
  128. if mapping != nil {
  129. t.Errorf("expected nil, got %v", mapping)
  130. }
  131. indexStat = alias.Stats()
  132. if indexStat != nil {
  133. t.Errorf("expected nil, got %v", indexStat)
  134. }
  135. // now a few things that should work
  136. _, err = alias.Search(sr)
  137. if err != expectedError2 {
  138. t.Errorf("expected %v, got %v", expectedError2, err)
  139. }
  140. count, err = alias.DocCount()
  141. if err != nil {
  142. t.Errorf("expected no error, got %v", err)
  143. }
  144. if count != 0 {
  145. t.Errorf("expected count 0, got %d", count)
  146. }
  147. // now change the def using swap
  148. expectedError3 := fmt.Errorf("expected3")
  149. ei3 := &stubIndex{
  150. err: expectedError3,
  151. }
  152. alias.Swap([]Index{ei3}, []Index{ei2})
  153. err = alias.Index("a", "a")
  154. if err != expectedError3 {
  155. t.Errorf("expected %v, got %v", expectedError3, err)
  156. }
  157. err = alias.Delete("a")
  158. if err != expectedError3 {
  159. t.Errorf("expected %v, got %v", expectedError3, err)
  160. }
  161. err = alias.Batch(batch)
  162. if err != expectedError3 {
  163. t.Errorf("expected %v, got %v", expectedError3, err)
  164. }
  165. _, err = alias.Document("a")
  166. if err != expectedError3 {
  167. t.Errorf("expected %v, got %v", expectedError3, err)
  168. }
  169. _, err = alias.Fields()
  170. if err != expectedError3 {
  171. t.Errorf("expected %v, got %v", expectedError3, err)
  172. }
  173. _, err = alias.GetInternal([]byte("a"))
  174. if err != expectedError3 {
  175. t.Errorf("expected %v, got %v", expectedError3, err)
  176. }
  177. err = alias.SetInternal([]byte("a"), []byte("a"))
  178. if err != expectedError3 {
  179. t.Errorf("expected %v, got %v", expectedError3, err)
  180. }
  181. err = alias.DeleteInternal([]byte("a"))
  182. if err != expectedError3 {
  183. t.Errorf("expected %v, got %v", expectedError3, err)
  184. }
  185. mapping = alias.Mapping()
  186. if mapping != nil {
  187. t.Errorf("expected nil, got %v", mapping)
  188. }
  189. indexStat = alias.Stats()
  190. if indexStat != nil {
  191. t.Errorf("expected nil, got %v", indexStat)
  192. }
  193. // now a few things that should work
  194. _, err = alias.Search(sr)
  195. if err != expectedError3 {
  196. t.Errorf("expected %v, got %v", expectedError3, err)
  197. }
  198. count, err = alias.DocCount()
  199. if err != nil {
  200. t.Errorf("expected no error, got %v", err)
  201. }
  202. if count != 0 {
  203. t.Errorf("expected count 0, got %d", count)
  204. }
  205. }
  206. func TestIndexAliasClosed(t *testing.T) {
  207. alias := NewIndexAlias()
  208. err := alias.Close()
  209. if err != nil {
  210. t.Fatal(err)
  211. }
  212. err = alias.Index("a", "a")
  213. if err != ErrorIndexClosed {
  214. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  215. }
  216. err = alias.Delete("a")
  217. if err != ErrorIndexClosed {
  218. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  219. }
  220. batch := alias.NewBatch()
  221. err = alias.Batch(batch)
  222. if err != ErrorIndexClosed {
  223. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  224. }
  225. _, err = alias.Document("a")
  226. if err != ErrorIndexClosed {
  227. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  228. }
  229. _, err = alias.Fields()
  230. if err != ErrorIndexClosed {
  231. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  232. }
  233. _, err = alias.GetInternal([]byte("a"))
  234. if err != ErrorIndexClosed {
  235. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  236. }
  237. err = alias.SetInternal([]byte("a"), []byte("a"))
  238. if err != ErrorIndexClosed {
  239. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  240. }
  241. err = alias.DeleteInternal([]byte("a"))
  242. if err != ErrorIndexClosed {
  243. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  244. }
  245. mapping := alias.Mapping()
  246. if mapping != nil {
  247. t.Errorf("expected nil, got %v", mapping)
  248. }
  249. indexStat := alias.Stats()
  250. if indexStat != nil {
  251. t.Errorf("expected nil, got %v", indexStat)
  252. }
  253. // now a few things that should work
  254. sr := NewSearchRequest(NewTermQuery("test"))
  255. _, err = alias.Search(sr)
  256. if err != ErrorIndexClosed {
  257. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  258. }
  259. _, err = alias.DocCount()
  260. if err != ErrorIndexClosed {
  261. t.Errorf("expected %v, got %v", ErrorIndexClosed, err)
  262. }
  263. }
  264. func TestIndexAliasEmpty(t *testing.T) {
  265. alias := NewIndexAlias()
  266. err := alias.Index("a", "a")
  267. if err != ErrorAliasEmpty {
  268. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  269. }
  270. err = alias.Delete("a")
  271. if err != ErrorAliasEmpty {
  272. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  273. }
  274. batch := alias.NewBatch()
  275. err = alias.Batch(batch)
  276. if err != ErrorAliasEmpty {
  277. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  278. }
  279. _, err = alias.Document("a")
  280. if err != ErrorAliasEmpty {
  281. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  282. }
  283. _, err = alias.Fields()
  284. if err != ErrorAliasEmpty {
  285. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  286. }
  287. _, err = alias.GetInternal([]byte("a"))
  288. if err != ErrorAliasEmpty {
  289. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  290. }
  291. err = alias.SetInternal([]byte("a"), []byte("a"))
  292. if err != ErrorAliasEmpty {
  293. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  294. }
  295. err = alias.DeleteInternal([]byte("a"))
  296. if err != ErrorAliasEmpty {
  297. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  298. }
  299. mapping := alias.Mapping()
  300. if mapping != nil {
  301. t.Errorf("expected nil, got %v", mapping)
  302. }
  303. indexStat := alias.Stats()
  304. if indexStat != nil {
  305. t.Errorf("expected nil, got %v", indexStat)
  306. }
  307. // now a few things that should work
  308. sr := NewSearchRequest(NewTermQuery("test"))
  309. _, err = alias.Search(sr)
  310. if err != ErrorAliasEmpty {
  311. t.Errorf("expected %v, got %v", ErrorAliasEmpty, err)
  312. }
  313. count, err := alias.DocCount()
  314. if err != nil {
  315. t.Errorf("error getting alias doc count: %v", err)
  316. }
  317. if count != 0 {
  318. t.Errorf("expected %d, got %d", 0, count)
  319. }
  320. }
  321. func TestIndexAliasMulti(t *testing.T) {
  322. score1, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(1.0), 0)
  323. score2, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(2.0), 0)
  324. ei1Count := uint64(7)
  325. ei1 := &stubIndex{
  326. err: nil,
  327. docCountResult: &ei1Count,
  328. searchResult: &SearchResult{
  329. Status: &SearchStatus{
  330. Total: 1,
  331. Successful: 1,
  332. Errors: make(map[string]error),
  333. },
  334. Total: 1,
  335. Hits: search.DocumentMatchCollection{
  336. {
  337. ID: "a",
  338. Score: 1.0,
  339. Sort: []string{string(score1)},
  340. },
  341. },
  342. MaxScore: 1.0,
  343. }}
  344. ei2Count := uint64(8)
  345. ei2 := &stubIndex{
  346. err: nil,
  347. docCountResult: &ei2Count,
  348. searchResult: &SearchResult{
  349. Status: &SearchStatus{
  350. Total: 1,
  351. Successful: 1,
  352. Errors: make(map[string]error),
  353. },
  354. Total: 1,
  355. Hits: search.DocumentMatchCollection{
  356. {
  357. ID: "b",
  358. Score: 2.0,
  359. Sort: []string{string(score2)},
  360. },
  361. },
  362. MaxScore: 2.0,
  363. }}
  364. alias := NewIndexAlias(ei1, ei2)
  365. err := alias.Index("a", "a")
  366. if err != ErrorAliasMulti {
  367. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  368. }
  369. err = alias.Delete("a")
  370. if err != ErrorAliasMulti {
  371. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  372. }
  373. batch := alias.NewBatch()
  374. err = alias.Batch(batch)
  375. if err != ErrorAliasMulti {
  376. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  377. }
  378. _, err = alias.Document("a")
  379. if err != ErrorAliasMulti {
  380. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  381. }
  382. _, err = alias.Fields()
  383. if err != ErrorAliasMulti {
  384. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  385. }
  386. _, err = alias.GetInternal([]byte("a"))
  387. if err != ErrorAliasMulti {
  388. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  389. }
  390. err = alias.SetInternal([]byte("a"), []byte("a"))
  391. if err != ErrorAliasMulti {
  392. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  393. }
  394. err = alias.DeleteInternal([]byte("a"))
  395. if err != ErrorAliasMulti {
  396. t.Errorf("expected %v, got %v", ErrorAliasMulti, err)
  397. }
  398. mapping := alias.Mapping()
  399. if mapping != nil {
  400. t.Errorf("expected nil, got %v", mapping)
  401. }
  402. indexStat := alias.Stats()
  403. if indexStat != nil {
  404. t.Errorf("expected nil, got %v", indexStat)
  405. }
  406. // now a few things that should work
  407. sr := NewSearchRequest(NewTermQuery("test"))
  408. expected := &SearchResult{
  409. Status: &SearchStatus{
  410. Total: 2,
  411. Successful: 2,
  412. Errors: make(map[string]error),
  413. },
  414. Request: sr,
  415. Total: 2,
  416. Hits: search.DocumentMatchCollection{
  417. {
  418. ID: "b",
  419. Score: 2.0,
  420. Sort: []string{string(score2)},
  421. },
  422. {
  423. ID: "a",
  424. Score: 1.0,
  425. Sort: []string{string(score1)},
  426. },
  427. },
  428. MaxScore: 2.0,
  429. }
  430. results, err := alias.Search(sr)
  431. if err != nil {
  432. t.Error(err)
  433. }
  434. // cheat and ensure that Took field matches since it involves time
  435. expected.Took = results.Took
  436. if !reflect.DeepEqual(results, expected) {
  437. t.Errorf("expected %#v, got %#v", expected, results)
  438. }
  439. count, err := alias.DocCount()
  440. if err != nil {
  441. t.Errorf("error getting alias doc count: %v", err)
  442. }
  443. if count != (*ei1.docCountResult + *ei2.docCountResult) {
  444. t.Errorf("expected %d, got %d", (*ei1.docCountResult + *ei2.docCountResult), count)
  445. }
  446. }
  447. // TestMultiSearchNoError
  448. func TestMultiSearchNoError(t *testing.T) {
  449. score1, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(1.0), 0)
  450. score2, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(2.0), 0)
  451. ei1 := &stubIndex{err: nil, searchResult: &SearchResult{
  452. Status: &SearchStatus{
  453. Total: 1,
  454. Successful: 1,
  455. Errors: make(map[string]error),
  456. },
  457. Total: 1,
  458. Hits: search.DocumentMatchCollection{
  459. {
  460. Index: "1",
  461. ID: "a",
  462. Score: 1.0,
  463. Sort: []string{string(score1)},
  464. },
  465. },
  466. MaxScore: 1.0,
  467. }}
  468. ei2 := &stubIndex{err: nil, searchResult: &SearchResult{
  469. Status: &SearchStatus{
  470. Total: 1,
  471. Successful: 1,
  472. Errors: make(map[string]error),
  473. },
  474. Total: 1,
  475. Hits: search.DocumentMatchCollection{
  476. {
  477. Index: "2",
  478. ID: "b",
  479. Score: 2.0,
  480. Sort: []string{string(score2)},
  481. },
  482. },
  483. MaxScore: 2.0,
  484. }}
  485. sr := NewSearchRequest(NewTermQuery("test"))
  486. expected := &SearchResult{
  487. Status: &SearchStatus{
  488. Total: 2,
  489. Successful: 2,
  490. Errors: make(map[string]error),
  491. },
  492. Request: sr,
  493. Total: 2,
  494. Hits: search.DocumentMatchCollection{
  495. {
  496. Index: "2",
  497. ID: "b",
  498. Score: 2.0,
  499. Sort: []string{string(score2)},
  500. },
  501. {
  502. Index: "1",
  503. ID: "a",
  504. Score: 1.0,
  505. Sort: []string{string(score1)},
  506. },
  507. },
  508. MaxScore: 2.0,
  509. }
  510. results, err := MultiSearch(context.Background(), sr, ei1, ei2)
  511. if err != nil {
  512. t.Error(err)
  513. }
  514. // cheat and ensure that Took field matches since it involves time
  515. expected.Took = results.Took
  516. if !reflect.DeepEqual(results, expected) {
  517. t.Errorf("expected %#v, got %#v", expected, results)
  518. }
  519. }
  520. // TestMultiSearchSomeError
  521. func TestMultiSearchSomeError(t *testing.T) {
  522. ei1 := &stubIndex{name: "ei1", err: nil, searchResult: &SearchResult{
  523. Status: &SearchStatus{
  524. Total: 1,
  525. Successful: 1,
  526. Errors: make(map[string]error),
  527. },
  528. Total: 1,
  529. Hits: search.DocumentMatchCollection{
  530. {
  531. ID: "a",
  532. Score: 1.0,
  533. },
  534. },
  535. Took: 1 * time.Second,
  536. MaxScore: 1.0,
  537. }}
  538. ei2 := &stubIndex{name: "ei2", err: fmt.Errorf("deliberate error")}
  539. sr := NewSearchRequest(NewTermQuery("test"))
  540. res, err := MultiSearch(context.Background(), sr, ei1, ei2)
  541. if err != nil {
  542. t.Errorf("expected no error, got %v", err)
  543. }
  544. if res.Status.Total != 2 {
  545. t.Errorf("expected 2 indexes to be queried, got %d", res.Status.Total)
  546. }
  547. if res.Status.Failed != 1 {
  548. t.Errorf("expected 1 index to fail, got %d", res.Status.Failed)
  549. }
  550. if res.Status.Successful != 1 {
  551. t.Errorf("expected 1 index to be successful, got %d", res.Status.Successful)
  552. }
  553. if len(res.Status.Errors) != 1 {
  554. t.Fatalf("expected 1 status error message, got %d", len(res.Status.Errors))
  555. }
  556. if res.Status.Errors["ei2"].Error() != "deliberate error" {
  557. t.Errorf("expected ei2 index error message 'deliberate error', got '%s'", res.Status.Errors["ei2"])
  558. }
  559. }
  560. // TestMultiSearchAllError
  561. // reproduces https://github.com/blevesearch/bleve/issues/126
  562. func TestMultiSearchAllError(t *testing.T) {
  563. ei1 := &stubIndex{name: "ei1", err: fmt.Errorf("deliberate error")}
  564. ei2 := &stubIndex{name: "ei2", err: fmt.Errorf("deliberate error")}
  565. sr := NewSearchRequest(NewTermQuery("test"))
  566. res, err := MultiSearch(context.Background(), sr, ei1, ei2)
  567. if err != nil {
  568. t.Errorf("expected no error, got %v", err)
  569. }
  570. if res.Status.Total != 2 {
  571. t.Errorf("expected 2 indexes to be queried, got %d", res.Status.Total)
  572. }
  573. if res.Status.Failed != 2 {
  574. t.Errorf("expected 2 indexes to fail, got %d", res.Status.Failed)
  575. }
  576. if res.Status.Successful != 0 {
  577. t.Errorf("expected 0 indexes to be successful, got %d", res.Status.Successful)
  578. }
  579. if len(res.Status.Errors) != 2 {
  580. t.Fatalf("expected 2 status error messages, got %d", len(res.Status.Errors))
  581. }
  582. if res.Status.Errors["ei1"].Error() != "deliberate error" {
  583. t.Errorf("expected ei1 index error message 'deliberate error', got '%s'", res.Status.Errors["ei1"])
  584. }
  585. if res.Status.Errors["ei2"].Error() != "deliberate error" {
  586. t.Errorf("expected ei2 index error message 'deliberate error', got '%s'", res.Status.Errors["ei2"])
  587. }
  588. }
  589. func TestMultiSearchSecondPage(t *testing.T) {
  590. checkRequest := func(sr *SearchRequest) error {
  591. if sr.From != 0 {
  592. return fmt.Errorf("child request from should be 0")
  593. }
  594. if sr.Size != 20 {
  595. return fmt.Errorf("child request size should be 20")
  596. }
  597. return nil
  598. }
  599. ei1 := &stubIndex{
  600. searchResult: &SearchResult{
  601. Status: &SearchStatus{
  602. Total: 1,
  603. Successful: 1,
  604. Errors: make(map[string]error),
  605. },
  606. },
  607. checkRequest: checkRequest,
  608. }
  609. ei2 := &stubIndex{
  610. searchResult: &SearchResult{
  611. Status: &SearchStatus{
  612. Total: 1,
  613. Successful: 1,
  614. Errors: make(map[string]error),
  615. },
  616. },
  617. checkRequest: checkRequest,
  618. }
  619. sr := NewSearchRequestOptions(NewTermQuery("test"), 10, 10, false)
  620. _, err := MultiSearch(context.Background(), sr, ei1, ei2)
  621. if err != nil {
  622. t.Errorf("unexpected error %v", err)
  623. }
  624. }
  625. // TestMultiSearchTimeout tests simple timeout cases
  626. // 1. all searches finish successfully before timeout
  627. // 2. no searchers finish before the timeout
  628. // 3. no searches finish before cancellation
  629. func TestMultiSearchTimeout(t *testing.T) {
  630. score1, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(1.0), 0)
  631. score2, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(2.0), 0)
  632. var ctx context.Context
  633. ei1 := &stubIndex{
  634. name: "ei1",
  635. checkRequest: func(req *SearchRequest) error {
  636. select {
  637. case <-ctx.Done():
  638. return ctx.Err()
  639. case <-time.After(50 * time.Millisecond):
  640. return nil
  641. }
  642. },
  643. err: nil,
  644. searchResult: &SearchResult{
  645. Status: &SearchStatus{
  646. Total: 1,
  647. Successful: 1,
  648. Errors: make(map[string]error),
  649. },
  650. Total: 1,
  651. Hits: []*search.DocumentMatch{
  652. {
  653. Index: "1",
  654. ID: "a",
  655. Score: 1.0,
  656. Sort: []string{string(score1)},
  657. },
  658. },
  659. MaxScore: 1.0,
  660. }}
  661. ei2 := &stubIndex{
  662. name: "ei2",
  663. checkRequest: func(req *SearchRequest) error {
  664. select {
  665. case <-ctx.Done():
  666. return ctx.Err()
  667. case <-time.After(50 * time.Millisecond):
  668. return nil
  669. }
  670. },
  671. err: nil,
  672. searchResult: &SearchResult{
  673. Status: &SearchStatus{
  674. Total: 1,
  675. Successful: 1,
  676. Errors: make(map[string]error),
  677. },
  678. Total: 1,
  679. Hits: []*search.DocumentMatch{
  680. {
  681. Index: "2",
  682. ID: "b",
  683. Score: 2.0,
  684. Sort: []string{string(score2)},
  685. },
  686. },
  687. MaxScore: 2.0,
  688. }}
  689. // first run with absurdly long time out, should succeed
  690. var cancel context.CancelFunc
  691. ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
  692. defer cancel()
  693. query := NewTermQuery("test")
  694. sr := NewSearchRequest(query)
  695. res, err := MultiSearch(ctx, sr, ei1, ei2)
  696. if err != nil {
  697. t.Errorf("expected no error, got %v", err)
  698. }
  699. if res.Status.Total != 2 {
  700. t.Errorf("expected 2 total, got %d", res.Status.Failed)
  701. }
  702. if res.Status.Successful != 2 {
  703. t.Errorf("expected 0 success, got %d", res.Status.Successful)
  704. }
  705. if res.Status.Failed != 0 {
  706. t.Errorf("expected 2 failed, got %d", res.Status.Failed)
  707. }
  708. if len(res.Status.Errors) != 0 {
  709. t.Errorf("expected 0 errors, got %v", res.Status.Errors)
  710. }
  711. // now run a search again with an absurdly low timeout (should timeout)
  712. ctx, cancel = context.WithTimeout(context.Background(), 1*time.Microsecond)
  713. defer cancel()
  714. res, err = MultiSearch(ctx, sr, ei1, ei2)
  715. if err != nil {
  716. t.Errorf("expected no error, got %v", err)
  717. }
  718. if res.Status.Total != 2 {
  719. t.Errorf("expected 2 failed, got %d", res.Status.Failed)
  720. }
  721. if res.Status.Successful != 0 {
  722. t.Errorf("expected 0 success, got %d", res.Status.Successful)
  723. }
  724. if res.Status.Failed != 2 {
  725. t.Errorf("expected 2 failed, got %d", res.Status.Failed)
  726. }
  727. if len(res.Status.Errors) != 2 {
  728. t.Errorf("expected 2 errors, got %v", res.Status.Errors)
  729. } else {
  730. if res.Status.Errors["ei1"].Error() != context.DeadlineExceeded.Error() {
  731. t.Errorf("expected err for 'ei1' to be '%s' got '%s'", context.DeadlineExceeded.Error(), res.Status.Errors["ei1"])
  732. }
  733. if res.Status.Errors["ei2"].Error() != context.DeadlineExceeded.Error() {
  734. t.Errorf("expected err for 'ei2' to be '%s' got '%s'", context.DeadlineExceeded.Error(), res.Status.Errors["ei2"])
  735. }
  736. }
  737. // now run a search again with a normal timeout, but cancel it first
  738. ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
  739. cancel()
  740. res, err = MultiSearch(ctx, sr, ei1, ei2)
  741. if err != nil {
  742. t.Errorf("expected no error, got %v", err)
  743. }
  744. if res.Status.Total != 2 {
  745. t.Errorf("expected 2 failed, got %d", res.Status.Failed)
  746. }
  747. if res.Status.Successful != 0 {
  748. t.Errorf("expected 0 success, got %d", res.Status.Successful)
  749. }
  750. if res.Status.Failed != 2 {
  751. t.Errorf("expected 2 failed, got %d", res.Status.Failed)
  752. }
  753. if len(res.Status.Errors) != 2 {
  754. t.Errorf("expected 2 errors, got %v", res.Status.Errors)
  755. } else {
  756. if res.Status.Errors["ei1"].Error() != context.Canceled.Error() {
  757. t.Errorf("expected err for 'ei1' to be '%s' got '%s'", context.Canceled.Error(), res.Status.Errors["ei1"])
  758. }
  759. if res.Status.Errors["ei2"].Error() != context.Canceled.Error() {
  760. t.Errorf("expected err for 'ei2' to be '%s' got '%s'", context.Canceled.Error(), res.Status.Errors["ei2"])
  761. }
  762. }
  763. }
  764. // TestMultiSearchTimeoutPartial tests the case where some indexes exceed
  765. // the timeout, while others complete successfully
  766. func TestMultiSearchTimeoutPartial(t *testing.T) {
  767. score1, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(1.0), 0)
  768. score2, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(2.0), 0)
  769. score3, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(3.0), 0)
  770. var ctx context.Context
  771. ei1 := &stubIndex{
  772. name: "ei1",
  773. err: nil,
  774. searchResult: &SearchResult{
  775. Status: &SearchStatus{
  776. Total: 1,
  777. Successful: 1,
  778. Errors: make(map[string]error),
  779. },
  780. Total: 1,
  781. Hits: []*search.DocumentMatch{
  782. {
  783. Index: "1",
  784. ID: "a",
  785. Score: 1.0,
  786. Sort: []string{string(score1)},
  787. },
  788. },
  789. MaxScore: 1.0,
  790. }}
  791. ei2 := &stubIndex{
  792. name: "ei2",
  793. err: nil,
  794. searchResult: &SearchResult{
  795. Status: &SearchStatus{
  796. Total: 1,
  797. Successful: 1,
  798. Errors: make(map[string]error),
  799. },
  800. Total: 1,
  801. Hits: []*search.DocumentMatch{
  802. {
  803. Index: "2",
  804. ID: "b",
  805. Score: 2.0,
  806. Sort: []string{string(score2)},
  807. },
  808. },
  809. MaxScore: 2.0,
  810. }}
  811. ei3 := &stubIndex{
  812. name: "ei3",
  813. checkRequest: func(req *SearchRequest) error {
  814. select {
  815. case <-ctx.Done():
  816. return ctx.Err()
  817. case <-time.After(50 * time.Millisecond):
  818. return nil
  819. }
  820. },
  821. err: nil,
  822. searchResult: &SearchResult{
  823. Status: &SearchStatus{
  824. Total: 1,
  825. Successful: 1,
  826. Errors: make(map[string]error),
  827. },
  828. Total: 1,
  829. Hits: []*search.DocumentMatch{
  830. {
  831. Index: "3",
  832. ID: "c",
  833. Score: 3.0,
  834. Sort: []string{string(score3)},
  835. },
  836. },
  837. MaxScore: 3.0,
  838. }}
  839. // ei3 is set to take >50ms, so run search with timeout less than
  840. // this, this should return partial results
  841. var cancel context.CancelFunc
  842. ctx, cancel = context.WithTimeout(context.Background(), 25*time.Millisecond)
  843. defer cancel()
  844. query := NewTermQuery("test")
  845. sr := NewSearchRequest(query)
  846. expected := &SearchResult{
  847. Status: &SearchStatus{
  848. Total: 3,
  849. Successful: 2,
  850. Failed: 1,
  851. Errors: map[string]error{
  852. "ei3": context.DeadlineExceeded,
  853. },
  854. },
  855. Request: sr,
  856. Total: 2,
  857. Hits: search.DocumentMatchCollection{
  858. {
  859. Index: "2",
  860. ID: "b",
  861. Score: 2.0,
  862. Sort: []string{string(score2)},
  863. },
  864. {
  865. Index: "1",
  866. ID: "a",
  867. Score: 1.0,
  868. Sort: []string{string(score1)},
  869. },
  870. },
  871. MaxScore: 2.0,
  872. }
  873. res, err := MultiSearch(ctx, sr, ei1, ei2, ei3)
  874. if err != nil {
  875. t.Fatalf("expected no err, got %v", err)
  876. }
  877. expected.Took = res.Took
  878. if !reflect.DeepEqual(res, expected) {
  879. t.Errorf("expected %#v, got %#v", expected, res)
  880. }
  881. }
  882. func TestIndexAliasMultipleLayer(t *testing.T) {
  883. score1, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(1.0), 0)
  884. score2, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(2.0), 0)
  885. score3, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(3.0), 0)
  886. score4, _ := numeric.NewPrefixCodedInt64(numeric.Float64ToInt64(4.0), 0)
  887. var ctx context.Context
  888. ei1 := &stubIndex{
  889. name: "ei1",
  890. err: nil,
  891. searchResult: &SearchResult{
  892. Status: &SearchStatus{
  893. Total: 1,
  894. Successful: 1,
  895. Errors: make(map[string]error),
  896. },
  897. Total: 1,
  898. Hits: []*search.DocumentMatch{
  899. {
  900. Index: "1",
  901. ID: "a",
  902. Score: 1.0,
  903. Sort: []string{string(score1)},
  904. },
  905. },
  906. MaxScore: 1.0,
  907. }}
  908. ei2 := &stubIndex{
  909. name: "ei2",
  910. checkRequest: func(req *SearchRequest) error {
  911. select {
  912. case <-ctx.Done():
  913. return ctx.Err()
  914. case <-time.After(50 * time.Millisecond):
  915. return nil
  916. }
  917. },
  918. err: nil,
  919. searchResult: &SearchResult{
  920. Status: &SearchStatus{
  921. Total: 1,
  922. Successful: 1,
  923. Errors: make(map[string]error),
  924. },
  925. Total: 1,
  926. Hits: []*search.DocumentMatch{
  927. {
  928. Index: "2",
  929. ID: "b",
  930. Score: 2.0,
  931. Sort: []string{string(score2)},
  932. },
  933. },
  934. MaxScore: 2.0,
  935. }}
  936. ei3 := &stubIndex{
  937. name: "ei3",
  938. checkRequest: func(req *SearchRequest) error {
  939. select {
  940. case <-ctx.Done():
  941. return ctx.Err()
  942. case <-time.After(50 * time.Millisecond):
  943. return nil
  944. }
  945. },
  946. err: nil,
  947. searchResult: &SearchResult{
  948. Status: &SearchStatus{
  949. Total: 1,
  950. Successful: 1,
  951. Errors: make(map[string]error),
  952. },
  953. Total: 1,
  954. Hits: []*search.DocumentMatch{
  955. {
  956. Index: "3",
  957. ID: "c",
  958. Score: 3.0,
  959. Sort: []string{string(score3)},
  960. },
  961. },
  962. MaxScore: 3.0,
  963. }}
  964. ei4 := &stubIndex{
  965. name: "ei4",
  966. err: nil,
  967. searchResult: &SearchResult{
  968. Status: &SearchStatus{
  969. Total: 1,
  970. Successful: 1,
  971. Errors: make(map[string]error),
  972. },
  973. Total: 1,
  974. Hits: []*search.DocumentMatch{
  975. {
  976. Index: "4",
  977. ID: "d",
  978. Score: 4.0,
  979. Sort: []string{string(score4)},
  980. },
  981. },
  982. MaxScore: 4.0,
  983. }}
  984. alias1 := NewIndexAlias(ei1, ei2)
  985. alias2 := NewIndexAlias(ei3, ei4)
  986. aliasTop := NewIndexAlias(alias1, alias2)
  987. // ei2 and ei3 have 50ms delay
  988. // search across aliasTop should still get results from ei1 and ei4
  989. // total should still be 4
  990. var cancel context.CancelFunc
  991. ctx, cancel = context.WithTimeout(context.Background(), 25*time.Millisecond)
  992. defer cancel()
  993. query := NewTermQuery("test")
  994. sr := NewSearchRequest(query)
  995. expected := &SearchResult{
  996. Status: &SearchStatus{
  997. Total: 4,
  998. Successful: 2,
  999. Failed: 2,
  1000. Errors: map[string]error{
  1001. "ei2": context.DeadlineExceeded,
  1002. "ei3": context.DeadlineExceeded,
  1003. },
  1004. },
  1005. Request: sr,
  1006. Total: 2,
  1007. Hits: search.DocumentMatchCollection{
  1008. {
  1009. Index: "4",
  1010. ID: "d",
  1011. Score: 4.0,
  1012. Sort: []string{string(score4)},
  1013. },
  1014. {
  1015. Index: "1",
  1016. ID: "a",
  1017. Score: 1.0,
  1018. Sort: []string{string(score1)},
  1019. },
  1020. },
  1021. MaxScore: 4.0,
  1022. }
  1023. res, err := aliasTop.SearchInContext(ctx, sr)
  1024. if err != nil {
  1025. t.Fatalf("expected no err, got %v", err)
  1026. }
  1027. expected.Took = res.Took
  1028. if !reflect.DeepEqual(res, expected) {
  1029. t.Errorf("expected %#v, got %#v", expected, res)
  1030. }
  1031. }
  1032. // TestMultiSearchNoError
  1033. func TestMultiSearchCustomSort(t *testing.T) {
  1034. ei1 := &stubIndex{err: nil, searchResult: &SearchResult{
  1035. Status: &SearchStatus{
  1036. Total: 1,
  1037. Successful: 1,
  1038. Errors: make(map[string]error),
  1039. },
  1040. Total: 2,
  1041. Hits: search.DocumentMatchCollection{
  1042. {
  1043. Index: "1",
  1044. ID: "a",
  1045. Score: 1.0,
  1046. Sort: []string{"albert"},
  1047. },
  1048. {
  1049. Index: "1",
  1050. ID: "b",
  1051. Score: 2.0,
  1052. Sort: []string{"crown"},
  1053. },
  1054. },
  1055. MaxScore: 2.0,
  1056. }}
  1057. ei2 := &stubIndex{err: nil, searchResult: &SearchResult{
  1058. Status: &SearchStatus{
  1059. Total: 1,
  1060. Successful: 1,
  1061. Errors: make(map[string]error),
  1062. },
  1063. Total: 2,
  1064. Hits: search.DocumentMatchCollection{
  1065. {
  1066. Index: "2",
  1067. ID: "c",
  1068. Score: 2.5,
  1069. Sort: []string{"frank"},
  1070. },
  1071. {
  1072. Index: "2",
  1073. ID: "d",
  1074. Score: 3.0,
  1075. Sort: []string{"zombie"},
  1076. },
  1077. },
  1078. MaxScore: 3.0,
  1079. }}
  1080. sr := NewSearchRequest(NewTermQuery("test"))
  1081. sr.SortBy([]string{"name"})
  1082. expected := &SearchResult{
  1083. Status: &SearchStatus{
  1084. Total: 2,
  1085. Successful: 2,
  1086. Errors: make(map[string]error),
  1087. },
  1088. Request: sr,
  1089. Total: 4,
  1090. Hits: search.DocumentMatchCollection{
  1091. {
  1092. Index: "1",
  1093. ID: "a",
  1094. Score: 1.0,
  1095. Sort: []string{"albert"},
  1096. },
  1097. {
  1098. Index: "1",
  1099. ID: "b",
  1100. Score: 2.0,
  1101. Sort: []string{"crown"},
  1102. },
  1103. {
  1104. Index: "2",
  1105. ID: "c",
  1106. Score: 2.5,
  1107. Sort: []string{"frank"},
  1108. },
  1109. {
  1110. Index: "2",
  1111. ID: "d",
  1112. Score: 3.0,
  1113. Sort: []string{"zombie"},
  1114. },
  1115. },
  1116. MaxScore: 3.0,
  1117. }
  1118. results, err := MultiSearch(context.Background(), sr, ei1, ei2)
  1119. if err != nil {
  1120. t.Error(err)
  1121. }
  1122. // cheat and ensure that Took field matches since it involves time
  1123. expected.Took = results.Took
  1124. if !reflect.DeepEqual(results, expected) {
  1125. t.Errorf("expected %v, got %v", expected, results)
  1126. }
  1127. }
  1128. // stubIndex is an Index impl for which all operations
  1129. // return the configured error value, unless the
  1130. // corresponding operation result value has been
  1131. // set, in which case that is returned instead
  1132. type stubIndex struct {
  1133. name string
  1134. err error
  1135. searchResult *SearchResult
  1136. documentResult *document.Document
  1137. docCountResult *uint64
  1138. checkRequest func(*SearchRequest) error
  1139. }
  1140. func (i *stubIndex) Index(id string, data interface{}) error {
  1141. return i.err
  1142. }
  1143. func (i *stubIndex) Delete(id string) error {
  1144. return i.err
  1145. }
  1146. func (i *stubIndex) Batch(b *Batch) error {
  1147. return i.err
  1148. }
  1149. func (i *stubIndex) Document(id string) (*document.Document, error) {
  1150. if i.documentResult != nil {
  1151. return i.documentResult, nil
  1152. }
  1153. return nil, i.err
  1154. }
  1155. func (i *stubIndex) DocCount() (uint64, error) {
  1156. if i.docCountResult != nil {
  1157. return *i.docCountResult, nil
  1158. }
  1159. return 0, i.err
  1160. }
  1161. func (i *stubIndex) Search(req *SearchRequest) (*SearchResult, error) {
  1162. return i.SearchInContext(context.Background(), req)
  1163. }
  1164. func (i *stubIndex) SearchInContext(ctx context.Context, req *SearchRequest) (*SearchResult, error) {
  1165. if i.checkRequest != nil {
  1166. err := i.checkRequest(req)
  1167. if err != nil {
  1168. return nil, err
  1169. }
  1170. }
  1171. if i.searchResult != nil {
  1172. return i.searchResult, nil
  1173. }
  1174. return nil, i.err
  1175. }
  1176. func (i *stubIndex) Fields() ([]string, error) {
  1177. return nil, i.err
  1178. }
  1179. func (i *stubIndex) FieldDict(field string) (index.FieldDict, error) {
  1180. return nil, i.err
  1181. }
  1182. func (i *stubIndex) FieldDictRange(field string, startTerm []byte, endTerm []byte) (index.FieldDict, error) {
  1183. return nil, i.err
  1184. }
  1185. func (i *stubIndex) FieldDictPrefix(field string, termPrefix []byte) (index.FieldDict, error) {
  1186. return nil, i.err
  1187. }
  1188. func (i *stubIndex) Close() error {
  1189. return i.err
  1190. }
  1191. func (i *stubIndex) Mapping() mapping.IndexMapping {
  1192. return nil
  1193. }
  1194. func (i *stubIndex) Stats() *IndexStat {
  1195. return nil
  1196. }
  1197. func (i *stubIndex) StatsMap() map[string]interface{} {
  1198. return nil
  1199. }
  1200. func (i *stubIndex) GetInternal(key []byte) ([]byte, error) {
  1201. return nil, i.err
  1202. }
  1203. func (i *stubIndex) SetInternal(key, val []byte) error {
  1204. return i.err
  1205. }
  1206. func (i *stubIndex) DeleteInternal(key []byte) error {
  1207. return i.err
  1208. }
  1209. func (i *stubIndex) Advanced() (index.Index, store.KVStore, error) {
  1210. return nil, nil, nil
  1211. }
  1212. func (i *stubIndex) NewBatch() *Batch {
  1213. return &Batch{}
  1214. }
  1215. func (i *stubIndex) Name() string {
  1216. return i.name
  1217. }
  1218. func (i *stubIndex) SetName(name string) {
  1219. i.name = name
  1220. }