indexer_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. package core
  2. import (
  3. "testing"
  4. "github.com/huichen/wukong/engine"
  5. "github.com/huichen/wukong/types"
  6. "github.com/huichen/wukong/utils"
  7. )
  8. func TestAddKeywords(t *testing.T) {
  9. var indexer Indexer
  10. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  11. indexer.AddDocumentToCache(&types.DocumentIndex{
  12. DocId: 1,
  13. Keywords: []types.KeywordIndex{{"token1", 0, []int{}}},
  14. }, false)
  15. indexer.AddDocumentToCache(&types.DocumentIndex{
  16. DocId: 2,
  17. Keywords: []types.KeywordIndex{{"token2", 0, []int{}}},
  18. }, false)
  19. indexer.AddDocumentToCache(&types.DocumentIndex{
  20. DocId: 3,
  21. Keywords: []types.KeywordIndex{{"token3", 0, []int{}}},
  22. }, false)
  23. indexer.AddDocumentToCache(&types.DocumentIndex{
  24. DocId: 7,
  25. Keywords: []types.KeywordIndex{{"token7", 0, []int{}}},
  26. }, false)
  27. indexer.AddDocumentToCache(&types.DocumentIndex{
  28. DocId: 1,
  29. Keywords: []types.KeywordIndex{{"token2", 0, []int{}}},
  30. }, false)
  31. indexer.AddDocumentToCache(&types.DocumentIndex{
  32. DocId: 7,
  33. Keywords: []types.KeywordIndex{{"token77", 0, []int{}}},
  34. }, false)
  35. indexer.AddDocumentToCache(nil, true)
  36. utils.Expect(t, "", indicesToString(&indexer, "token1"))
  37. utils.Expect(t, "1 2 ", indicesToString(&indexer, "token2"))
  38. utils.Expect(t, "3 ", indicesToString(&indexer, "token3"))
  39. utils.Expect(t, "7 ", indicesToString(&indexer, "token77"))
  40. }
  41. func TestRemoveDocument(t *testing.T) {
  42. var indexer Indexer
  43. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  44. // doc1 = "token2 token3"
  45. indexer.AddDocumentToCache(&types.DocumentIndex{
  46. DocId: 1,
  47. Keywords: []types.KeywordIndex{
  48. {"token2", 0, []int{0}},
  49. {"token3", 0, []int{7}},
  50. },
  51. }, false)
  52. // doc2 = "token1 token2 token3"
  53. indexer.AddDocumentToCache(&types.DocumentIndex{
  54. DocId: 2,
  55. Keywords: []types.KeywordIndex{
  56. {"token1", 0, []int{0}},
  57. {"token2", 0, []int{7}},
  58. },
  59. }, true)
  60. utils.Expect(t, "2 ", indicesToString(&indexer, "token1"))
  61. utils.Expect(t, "1 2 ", indicesToString(&indexer, "token2"))
  62. utils.Expect(t, "1 ", indicesToString(&indexer, "token3"))
  63. indexer.RemoveDocumentToCache(2, false)
  64. // doc1 = "token1 token3"
  65. indexer.AddDocumentToCache(&types.DocumentIndex{
  66. DocId: 1,
  67. Keywords: []types.KeywordIndex{
  68. {"token1", 0, []int{0}},
  69. {"token3", 0, []int{7}},
  70. },
  71. }, true)
  72. utils.Expect(t, "1 ", indicesToString(&indexer, "token1"))
  73. utils.Expect(t, "", indicesToString(&indexer, "token2"))
  74. utils.Expect(t, "1 ", indicesToString(&indexer, "token3"))
  75. // doc2 = "token1 token2 token3"
  76. indexer.AddDocumentToCache(&types.DocumentIndex{
  77. DocId: 2,
  78. Keywords: []types.KeywordIndex{
  79. {"token1", 0, []int{0}},
  80. {"token2", 0, []int{7}},
  81. {"token3", 0, []int{14}},
  82. },
  83. }, true)
  84. utils.Expect(t, "1 2 ", indicesToString(&indexer, "token1"))
  85. utils.Expect(t, "2 ", indicesToString(&indexer, "token2"))
  86. utils.Expect(t, "1 2 ", indicesToString(&indexer, "token3"))
  87. // doc3 = "token1 token3"
  88. indexer.AddDocumentToCache(&types.DocumentIndex{
  89. DocId: 3,
  90. Keywords: []types.KeywordIndex{
  91. {"token1", 0, []int{0}},
  92. {"token2", 0, []int{7}},
  93. },
  94. }, false)
  95. indexer.RemoveDocumentToCache(3, false)
  96. indexer.AddDocumentToCache(nil, true)
  97. utils.Expect(t, "1 2 ", indicesToString(&indexer, "token1"))
  98. utils.Expect(t, "2 ", indicesToString(&indexer, "token2"))
  99. utils.Expect(t, "1 2 ", indicesToString(&indexer, "token3"))
  100. }
  101. func TestLookupLocationsIndex(t *testing.T) {
  102. var indexer Indexer
  103. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  104. // doc1 = "token2 token3"
  105. indexer.AddDocumentToCache(&types.DocumentIndex{
  106. DocId: 1,
  107. Keywords: []types.KeywordIndex{
  108. {"token2", 0, []int{0}},
  109. {"token3", 0, []int{7}},
  110. },
  111. }, false)
  112. // doc2 = "token1 token2 token3"
  113. indexer.AddDocumentToCache(&types.DocumentIndex{
  114. DocId: 2,
  115. Keywords: []types.KeywordIndex{
  116. {"token1", 0, []int{0}},
  117. {"token2", 0, []int{7}},
  118. {"token3", 0, []int{14}},
  119. },
  120. }, false)
  121. // doc3 = "token1 token2"
  122. indexer.AddDocumentToCache(&types.DocumentIndex{
  123. DocId: 3,
  124. Keywords: []types.KeywordIndex{
  125. {"token1", 0, []int{0}},
  126. {"token2", 0, []int{7}},
  127. },
  128. }, false)
  129. // doc4 = "token2"
  130. indexer.AddDocumentToCache(&types.DocumentIndex{
  131. DocId: 4,
  132. Keywords: []types.KeywordIndex{
  133. {"token2", 0, []int{0}},
  134. },
  135. }, false)
  136. // doc7 = "token1 token3"
  137. indexer.AddDocumentToCache(&types.DocumentIndex{
  138. DocId: 7,
  139. Keywords: []types.KeywordIndex{
  140. {"token1", 0, []int{0}},
  141. {"token3", 0, []int{7}},
  142. },
  143. }, false)
  144. // doc9 = "token3"
  145. indexer.AddDocumentToCache(&types.DocumentIndex{
  146. DocId: 9,
  147. Keywords: []types.KeywordIndex{
  148. {"token3", 0, []int{0}},
  149. },
  150. }, true)
  151. utils.Expect(t, "2 3 7 ", indicesToString(&indexer, "token1"))
  152. utils.Expect(t, "1 2 3 4 ", indicesToString(&indexer, "token2"))
  153. utils.Expect(t, "1 2 7 9 ", indicesToString(&indexer, "token3"))
  154. utils.Expect(t, "", indexedDocsToString(indexer.Lookup([]string{"token4"}, []string{}, nil, false)))
  155. utils.Expect(t, "[7 0 [0]] [3 0 [0]] [2 0 [0]] ",
  156. indexedDocsToString(indexer.Lookup([]string{"token1"}, []string{}, nil, false)))
  157. utils.Expect(t, "", indexedDocsToString(indexer.Lookup([]string{"token1", "token4"}, []string{}, nil, false)))
  158. utils.Expect(t, "[3 1 [0 7]] [2 1 [0 7]] ",
  159. indexedDocsToString(indexer.Lookup([]string{"token1", "token2"}, []string{}, nil, false)))
  160. utils.Expect(t, "[3 13 [7 0]] [2 13 [7 0]] ",
  161. indexedDocsToString(indexer.Lookup([]string{"token2", "token1"}, []string{}, nil, false)))
  162. utils.Expect(t, "[7 1 [0 7]] [2 8 [0 14]] ",
  163. indexedDocsToString(indexer.Lookup([]string{"token1", "token3"}, []string{}, nil, false)))
  164. utils.Expect(t, "[7 13 [7 0]] [2 20 [14 0]] ",
  165. indexedDocsToString(indexer.Lookup([]string{"token3", "token1"}, []string{}, nil, false)))
  166. utils.Expect(t, "[2 1 [7 14]] [1 1 [0 7]] ",
  167. indexedDocsToString(indexer.Lookup([]string{"token2", "token3"}, []string{}, nil, false)))
  168. utils.Expect(t, "[2 13 [14 7]] [1 13 [7 0]] ",
  169. indexedDocsToString(indexer.Lookup([]string{"token3", "token2"}, []string{}, nil, false)))
  170. utils.Expect(t, "[2 2 [0 7 14]] ",
  171. indexedDocsToString(indexer.Lookup([]string{"token1", "token2", "token3"}, []string{}, nil, false)))
  172. utils.Expect(t, "[2 26 [14 7 0]] ",
  173. indexedDocsToString(indexer.Lookup([]string{"token3", "token2", "token1"}, []string{}, nil, false)))
  174. }
  175. func TestLookupDocIdsIndex(t *testing.T) {
  176. var indexer Indexer
  177. indexer.Init(types.IndexerInitOptions{IndexType: types.DocIdsIndex})
  178. // doc1 = "token2 token3"
  179. indexer.AddDocumentToCache(&types.DocumentIndex{
  180. DocId: 1,
  181. Keywords: []types.KeywordIndex{
  182. {"token2", 0, []int{0}},
  183. {"token3", 0, []int{7}},
  184. },
  185. }, false)
  186. // doc2 = "token1 token2 token3"
  187. indexer.AddDocumentToCache(&types.DocumentIndex{
  188. DocId: 2,
  189. Keywords: []types.KeywordIndex{
  190. {"token1", 0, []int{0}},
  191. {"token2", 0, []int{7}},
  192. {"token3", 0, []int{14}},
  193. },
  194. }, false)
  195. // doc3 = "token1 token2"
  196. indexer.AddDocumentToCache(&types.DocumentIndex{
  197. DocId: 3,
  198. Keywords: []types.KeywordIndex{
  199. {"token1", 0, []int{0}},
  200. {"token2", 0, []int{7}},
  201. },
  202. }, false)
  203. // doc4 = "token2"
  204. indexer.AddDocumentToCache(&types.DocumentIndex{
  205. DocId: 4,
  206. Keywords: []types.KeywordIndex{
  207. {"token2", 0, []int{0}},
  208. },
  209. }, false)
  210. // doc7 = "token1 token3"
  211. indexer.AddDocumentToCache(&types.DocumentIndex{
  212. DocId: 7,
  213. Keywords: []types.KeywordIndex{
  214. {"token1", 0, []int{0}},
  215. {"token3", 0, []int{7}},
  216. },
  217. }, false)
  218. // doc9 = "token3"
  219. indexer.AddDocumentToCache(&types.DocumentIndex{
  220. DocId: 9,
  221. Keywords: []types.KeywordIndex{
  222. {"token3", 0, []int{0}},
  223. },
  224. }, true)
  225. utils.Expect(t, "2 3 7 ", indicesToString(&indexer, "token1"))
  226. utils.Expect(t, "1 2 3 4 ", indicesToString(&indexer, "token2"))
  227. utils.Expect(t, "1 2 7 9 ", indicesToString(&indexer, "token3"))
  228. utils.Expect(t, "", indexedDocsToString(indexer.Lookup([]string{"token4"}, []string{}, nil, false)))
  229. utils.Expect(t, "[7 0 []] [3 0 []] [2 0 []] ",
  230. indexedDocsToString(indexer.Lookup([]string{"token1"}, []string{}, nil, false)))
  231. utils.Expect(t, "", indexedDocsToString(indexer.Lookup([]string{"token1", "token4"}, []string{}, nil, false)))
  232. utils.Expect(t, "[3 0 []] [2 0 []] ",
  233. indexedDocsToString(indexer.Lookup([]string{"token1", "token2"}, []string{}, nil, false)))
  234. utils.Expect(t, "[3 0 []] [2 0 []] ",
  235. indexedDocsToString(indexer.Lookup([]string{"token2", "token1"}, []string{}, nil, false)))
  236. utils.Expect(t, "[7 0 []] [2 0 []] ",
  237. indexedDocsToString(indexer.Lookup([]string{"token1", "token3"}, []string{}, nil, false)))
  238. utils.Expect(t, "[7 0 []] [2 0 []] ",
  239. indexedDocsToString(indexer.Lookup([]string{"token3", "token1"}, []string{}, nil, false)))
  240. utils.Expect(t, "[2 0 []] [1 0 []] ",
  241. indexedDocsToString(indexer.Lookup([]string{"token2", "token3"}, []string{}, nil, false)))
  242. utils.Expect(t, "[2 0 []] [1 0 []] ",
  243. indexedDocsToString(indexer.Lookup([]string{"token3", "token2"}, []string{}, nil, false)))
  244. utils.Expect(t, "[2 0 []] ",
  245. indexedDocsToString(indexer.Lookup([]string{"token1", "token2", "token3"}, []string{}, nil, false)))
  246. utils.Expect(t, "[2 0 []] ",
  247. indexedDocsToString(indexer.Lookup([]string{"token3", "token2", "token1"}, []string{}, nil, false)))
  248. }
  249. func TestLookupWithProximity(t *testing.T) {
  250. var indexer Indexer
  251. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  252. // doc1 = "token2 token4 token4 token2 token3 token4"
  253. indexer.AddDocumentToCache(&types.DocumentIndex{
  254. DocId: 1,
  255. Keywords: []types.KeywordIndex{
  256. {"token2", 0, []int{0, 21}},
  257. {"token3", 0, []int{28}},
  258. {"token4", 0, []int{7, 14, 35}},
  259. },
  260. }, true)
  261. utils.Expect(t, "[1 1 [21 28]] ",
  262. indexedDocsToString(indexer.Lookup([]string{"token2", "token3"}, []string{}, nil, false)))
  263. // doc1 = "t2 t1 . . . t2 t3"
  264. indexer.AddDocumentToCache(&types.DocumentIndex{
  265. DocId: 1,
  266. Keywords: []types.KeywordIndex{
  267. {"t1", 0, []int{3}},
  268. {"t2", 0, []int{0, 12}},
  269. {"t3", 0, []int{15}},
  270. },
  271. }, true)
  272. utils.Expect(t, "[1 8 [3 12 15]] ",
  273. indexedDocsToString(indexer.Lookup([]string{"t1", "t2", "t3"}, []string{}, nil, false)))
  274. // doc1 = "t3 t2 t1 . . . . . t2 t3"
  275. indexer.AddDocumentToCache(&types.DocumentIndex{
  276. DocId: 1,
  277. Keywords: []types.KeywordIndex{
  278. {"t1", 0, []int{6}},
  279. {"t2", 0, []int{3, 19}},
  280. {"t3", 0, []int{0, 22}},
  281. },
  282. }, true)
  283. utils.Expect(t, "[1 10 [6 3 0]] ",
  284. indexedDocsToString(indexer.Lookup([]string{"t1", "t2", "t3"}, []string{}, nil, false)))
  285. }
  286. func TestLookupWithPartialLocations(t *testing.T) {
  287. var indexer Indexer
  288. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  289. // doc1 = "token2 token4 token4 token2 token3 token4" + "label1"(不在文本中)
  290. indexer.AddDocumentToCache(&types.DocumentIndex{
  291. DocId: 1,
  292. Keywords: []types.KeywordIndex{
  293. {"token2", 0, []int{0, 21}},
  294. {"token3", 0, []int{28}},
  295. {"label1", 0, []int{}},
  296. {"token4", 0, []int{7, 14, 35}},
  297. },
  298. }, false)
  299. // doc2 = "token2 token4 token4 token2 token3 token4"
  300. indexer.AddDocumentToCache(&types.DocumentIndex{
  301. DocId: 2,
  302. Keywords: []types.KeywordIndex{
  303. {"token2", 0, []int{0, 21}},
  304. {"token3", 0, []int{28}},
  305. {"token4", 0, []int{7, 14, 35}},
  306. },
  307. }, true)
  308. utils.Expect(t, "1 ", indicesToString(&indexer, "label1"))
  309. utils.Expect(t, "[1 1 [21 28]] ",
  310. indexedDocsToString(indexer.Lookup([]string{"token2", "token3"}, []string{"label1"}, nil, false)))
  311. }
  312. func TestLookupWithBM25(t *testing.T) {
  313. var indexer Indexer
  314. indexer.Init(types.IndexerInitOptions{
  315. IndexType: types.FrequenciesIndex,
  316. BM25Parameters: &types.BM25Parameters{
  317. K1: 1,
  318. B: 1,
  319. },
  320. })
  321. // doc1 = "token2 token4 token4 token2 token3 token4"
  322. indexer.AddDocumentToCache(&types.DocumentIndex{
  323. DocId: 1,
  324. TokenLength: 6,
  325. Keywords: []types.KeywordIndex{
  326. {"token2", 3, []int{0, 21}},
  327. {"token3", 7, []int{28}},
  328. {"token4", 15, []int{7, 14, 35}},
  329. },
  330. }, false)
  331. // doc2 = "token6 token7"
  332. indexer.AddDocumentToCache(&types.DocumentIndex{
  333. DocId: 2,
  334. TokenLength: 2,
  335. Keywords: []types.KeywordIndex{
  336. {"token6", 3, []int{0}},
  337. {"token7", 15, []int{7}},
  338. },
  339. }, true)
  340. outputs, _ := indexer.Lookup([]string{"token2", "token3", "token4"}, []string{}, nil, false)
  341. // BM25 = log2(3) * (12/9 + 28/17 + 60/33) = 6.3433
  342. utils.Expect(t, "76055", int(outputs[0].BM25*10000))
  343. }
  344. func TestLookupWithinDocIds(t *testing.T) {
  345. var indexer Indexer
  346. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  347. // doc1 = "token2 token3"
  348. indexer.AddDocumentToCache(&types.DocumentIndex{
  349. DocId: 1,
  350. Keywords: []types.KeywordIndex{
  351. {"token2", 0, []int{0}},
  352. {"token3", 0, []int{7}},
  353. },
  354. }, false)
  355. // doc2 = "token1 token2 token3"
  356. indexer.AddDocumentToCache(&types.DocumentIndex{
  357. DocId: 2,
  358. Keywords: []types.KeywordIndex{
  359. {"token1", 0, []int{0}},
  360. {"token2", 0, []int{7}},
  361. {"token3", 0, []int{14}},
  362. },
  363. }, false)
  364. // doc3 = "token1 token2"
  365. indexer.AddDocumentToCache(&types.DocumentIndex{
  366. DocId: 3,
  367. Keywords: []types.KeywordIndex{
  368. {"token1", 0, []int{0}},
  369. {"token2", 0, []int{7}},
  370. },
  371. }, false)
  372. // doc4 = "token2"
  373. indexer.AddDocumentToCache(&types.DocumentIndex{
  374. DocId: 4,
  375. Keywords: []types.KeywordIndex{
  376. {"token2", 0, []int{0}},
  377. },
  378. }, true)
  379. docIds := make(map[uint64]bool)
  380. docIds[1] = true
  381. docIds[3] = true
  382. utils.Expect(t, "[3 0 [7]] [1 0 [0]] ",
  383. indexedDocsToString(indexer.Lookup([]string{"token2"}, []string{}, docIds, false)))
  384. }
  385. func TestLookupWithLocations(t *testing.T) {
  386. var indexer Indexer
  387. indexer.Init(types.IndexerInitOptions{IndexType: types.LocationsIndex})
  388. // doc1 = "token2 token4 token4 token2 token3 token4"
  389. indexer.AddDocumentToCache(&types.DocumentIndex{
  390. DocId: 1,
  391. Keywords: []types.KeywordIndex{
  392. {"token2", 0, []int{0, 21}},
  393. {"token3", 0, []int{28}},
  394. {"token4", 0, []int{7, 14, 35}},
  395. },
  396. }, true)
  397. // doc2 = "token2 token4 token4 token2 token3 token4"
  398. indexer.AddDocumentToCache(&types.DocumentIndex{
  399. DocId: 2,
  400. Keywords: []types.KeywordIndex{
  401. {"token3", 0, []int{0, 21}},
  402. {"token5", 0, []int{28}},
  403. {"token2", 0, []int{7, 14, 35}},
  404. },
  405. }, true)
  406. indexer.RemoveDocumentToCache(2, true)
  407. docs, _ := indexer.Lookup([]string{"token2", "token3"}, []string{}, nil, false)
  408. utils.Expect(t, "[[0 21] [28]]", docs[0].TokenLocations)
  409. }
  410. func TestLookupWithLocations1(t *testing.T) {
  411. type Data struct {
  412. Id int
  413. Content string
  414. Labels []string
  415. }
  416. datas := make([]Data, 0)
  417. data0 := Data{Id: 0, Content: "此次百度收购将成中国互联网最大并购", Labels: []string{"百度", "中国"}}
  418. datas = append(datas, data0)
  419. data1 := Data{Id: 1, Content: "百度宣布拟全资收购91无线业务", Labels: []string{"百度"}}
  420. datas = append(datas, data1)
  421. data2 := Data{Id: 2, Content: "百度是中国最大的搜索引擎", Labels: []string{"百度"}}
  422. datas = append(datas, data2)
  423. data3 := Data{Id: 3, Content: "百度在研制无人汽车", Labels: []string{"百度"}}
  424. datas = append(datas, data3)
  425. data4 := Data{Id: 4, Content: "BAT是中国互联网三巨头", Labels: []string{"百度"}}
  426. datas = append(datas, data4)
  427. // 初始化
  428. searcher_locations := engine.Engine{}
  429. searcher_locations.Init(types.EngineInitOptions{
  430. SegmenterDictionaries: "../data/dictionary.txt",
  431. IndexerInitOptions: &types.IndexerInitOptions{
  432. IndexType: types.LocationsIndex,
  433. },
  434. })
  435. defer searcher_locations.Close()
  436. for _, data := range datas {
  437. searcher_locations.IndexDocument(uint64(data.Id), types.DocumentIndexData{Content: data.Content, Labels: data.Labels})
  438. }
  439. searcher_locations.FlushIndex()
  440. res_locations := searcher_locations.Search(types.SearchRequest{Text: "百度"})
  441. searcher_docids := engine.Engine{}
  442. searcher_docids.Init(types.EngineInitOptions{
  443. SegmenterDictionaries: "../data/dictionary.txt",
  444. IndexerInitOptions: &types.IndexerInitOptions{
  445. IndexType: types.DocIdsIndex,
  446. },
  447. })
  448. defer searcher_docids.Close()
  449. for _, data := range datas {
  450. searcher_docids.IndexDocument(uint64(data.Id), types.DocumentIndexData{Content: data.Content, Labels: data.Labels})
  451. }
  452. searcher_docids.FlushIndex()
  453. res_docids := searcher_docids.Search(types.SearchRequest{Text: "百度"})
  454. if res_docids.NumDocs != res_locations.NumDocs {
  455. t.Errorf("期待的搜索结果个数=\"%d\", 实际=\"%d\"", res_docids.NumDocs, res_locations.NumDocs)
  456. }
  457. }