bleve_field_test.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package nginx_log
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/blevesearch/bleve/v2"
  6. "github.com/blevesearch/bleve/v2/mapping"
  7. )
  8. // TestBleveFieldMapping tests different field mapping approaches for file_path
  9. func TestBleveFieldMapping(t *testing.T) {
  10. // Create a temporary index in memory
  11. index, err := bleve.NewMemOnly(createTestIndexMapping())
  12. if err != nil {
  13. t.Fatalf("Failed to create test index: %v", err)
  14. }
  15. defer index.Close()
  16. // Create test data similar to what we have
  17. testEntry := &IndexedLogEntry{
  18. ID: "test_1",
  19. FilePath: "/var/log/nginx/access.log",
  20. Timestamp: time.Now(),
  21. IP: "135.220.172.38",
  22. Method: "GET",
  23. Path: "/test",
  24. Status: 200,
  25. }
  26. // Index the test entry
  27. err = index.Index(testEntry.ID, testEntry)
  28. if err != nil {
  29. t.Fatalf("Failed to index test entry: %v", err)
  30. }
  31. // Test 1: MatchAllQuery should work
  32. t.Run("MatchAllQuery", func(t *testing.T) {
  33. query := bleve.NewMatchAllQuery()
  34. searchReq := bleve.NewSearchRequest(query)
  35. searchReq.Size = 10
  36. searchReq.Fields = []string{"file_path", "ip"}
  37. result, err := index.Search(searchReq)
  38. if err != nil {
  39. t.Errorf("MatchAllQuery failed: %v", err)
  40. return
  41. }
  42. t.Logf("MatchAllQuery returned %d hits", result.Total)
  43. if result.Total == 0 {
  44. t.Error("MatchAllQuery should return at least 1 hit")
  45. }
  46. for i, hit := range result.Hits {
  47. t.Logf("Hit %d: ID=%s, Fields=%+v", i, hit.ID, hit.Fields)
  48. }
  49. })
  50. // Test 2: IP field query (should work based on logs)
  51. t.Run("IPFieldQuery", func(t *testing.T) {
  52. query := bleve.NewMatchQuery("135.220.172.38")
  53. query.SetField("ip")
  54. searchReq := bleve.NewSearchRequest(query)
  55. searchReq.Size = 10
  56. result, err := index.Search(searchReq)
  57. if err != nil {
  58. t.Errorf("IP field query failed: %v", err)
  59. return
  60. }
  61. t.Logf("IP field query returned %d hits", result.Total)
  62. if result.Total == 0 {
  63. t.Error("IP field query should return at least 1 hit")
  64. }
  65. })
  66. // Test 3: file_path field with TermQuery
  67. t.Run("FilePathTermQuery", func(t *testing.T) {
  68. query := bleve.NewTermQuery("/var/log/nginx/access.log")
  69. query.SetField("file_path")
  70. searchReq := bleve.NewSearchRequest(query)
  71. searchReq.Size = 10
  72. result, err := index.Search(searchReq)
  73. if err != nil {
  74. t.Errorf("file_path TermQuery failed: %v", err)
  75. return
  76. }
  77. t.Logf("file_path TermQuery returned %d hits", result.Total)
  78. if result.Total == 0 {
  79. t.Error("file_path TermQuery should return at least 1 hit")
  80. }
  81. })
  82. // Test 4: file_path field with MatchQuery
  83. t.Run("FilePathMatchQuery", func(t *testing.T) {
  84. query := bleve.NewMatchQuery("/var/log/nginx/access.log")
  85. query.SetField("file_path")
  86. searchReq := bleve.NewSearchRequest(query)
  87. searchReq.Size = 10
  88. result, err := index.Search(searchReq)
  89. if err != nil {
  90. t.Errorf("file_path MatchQuery failed: %v", err)
  91. return
  92. }
  93. t.Logf("file_path MatchQuery returned %d hits", result.Total)
  94. if result.Total == 0 {
  95. t.Error("file_path MatchQuery should return at least 1 hit")
  96. }
  97. })
  98. // Test 5: Different file_path mapping approaches
  99. t.Run("AlternativeFilepathMapping", func(t *testing.T) {
  100. // Create index with TextFieldMapping for file_path instead of KeywordFieldMapping
  101. altMapping := createAlternativeIndexMapping()
  102. altIndex, err := bleve.NewMemOnly(altMapping)
  103. if err != nil {
  104. t.Fatalf("Failed to create alternative index: %v", err)
  105. }
  106. defer altIndex.Close()
  107. // Index the same data
  108. err = altIndex.Index(testEntry.ID, testEntry)
  109. if err != nil {
  110. t.Fatalf("Failed to index test entry in alternative index: %v", err)
  111. }
  112. // Test with MatchQuery
  113. query := bleve.NewMatchQuery("/var/log/nginx/access.log")
  114. query.SetField("file_path")
  115. searchReq := bleve.NewSearchRequest(query)
  116. searchReq.Size = 10
  117. searchReq.Fields = []string{"file_path", "ip"}
  118. result, err := altIndex.Search(searchReq)
  119. if err != nil {
  120. t.Errorf("Alternative file_path MatchQuery failed: %v", err)
  121. return
  122. }
  123. t.Logf("Alternative file_path MatchQuery returned %d hits", result.Total)
  124. for i, hit := range result.Hits {
  125. t.Logf("Alt Hit %d: ID=%s, Fields=%+v", i, hit.ID, hit.Fields)
  126. }
  127. })
  128. // Test 6: PhraseQuery approach
  129. t.Run("FilePathPhraseQuery", func(t *testing.T) {
  130. query := bleve.NewPhraseQuery([]string{"/var/log/nginx/access.log"}, "file_path")
  131. searchReq := bleve.NewSearchRequest(query)
  132. searchReq.Size = 10
  133. result, err := index.Search(searchReq)
  134. if err != nil {
  135. t.Errorf("file_path PhraseQuery failed: %v", err)
  136. return
  137. }
  138. t.Logf("file_path PhraseQuery returned %d hits", result.Total)
  139. })
  140. // Test 7: No field specification (search all fields)
  141. t.Run("NoFieldSpecification", func(t *testing.T) {
  142. query := bleve.NewMatchQuery("/var/log/nginx/access.log")
  143. // Don't set field - search all fields
  144. searchReq := bleve.NewSearchRequest(query)
  145. searchReq.Size = 10
  146. searchReq.Fields = []string{"file_path", "ip"}
  147. result, err := index.Search(searchReq)
  148. if err != nil {
  149. t.Errorf("No field specification query failed: %v", err)
  150. return
  151. }
  152. t.Logf("No field specification query returned %d hits", result.Total)
  153. for i, hit := range result.Hits {
  154. t.Logf("NoField Hit %d: ID=%s, Fields=%+v", i, hit.ID, hit.Fields)
  155. }
  156. })
  157. }
  158. // createTestIndexMapping creates the same index mapping as the main code
  159. func createTestIndexMapping() mapping.IndexMapping {
  160. logMapping := bleve.NewDocumentMapping()
  161. // Timestamp
  162. timestampMapping := bleve.NewDateTimeFieldMapping()
  163. logMapping.AddFieldMappingsAt("timestamp", timestampMapping)
  164. // File path with TextFieldMapping + keyword analyzer (current approach)
  165. filePathMapping := bleve.NewTextFieldMapping()
  166. filePathMapping.Store = true
  167. filePathMapping.Index = true
  168. filePathMapping.Analyzer = "keyword" // Use keyword analyzer for exact matching
  169. logMapping.AddFieldMappingsAt("file_path", filePathMapping)
  170. // Other text fields
  171. textMapping := bleve.NewTextFieldMapping()
  172. textMapping.Store = true
  173. textMapping.Index = true
  174. logMapping.AddFieldMappingsAt("ip", textMapping)
  175. logMapping.AddFieldMappingsAt("method", textMapping)
  176. logMapping.AddFieldMappingsAt("path", textMapping)
  177. // Numeric fields
  178. numericMapping := bleve.NewNumericFieldMapping()
  179. numericMapping.Store = true
  180. numericMapping.Index = true
  181. logMapping.AddFieldMappingsAt("status", numericMapping)
  182. // Create index mapping
  183. indexMapping := bleve.NewIndexMapping()
  184. // Use the default mapping instead of creating a separate document type
  185. indexMapping.DefaultMapping = logMapping
  186. return indexMapping
  187. }
  188. // createAlternativeIndexMapping uses TextFieldMapping for file_path
  189. func createAlternativeIndexMapping() mapping.IndexMapping {
  190. logMapping := bleve.NewDocumentMapping()
  191. // Timestamp
  192. timestampMapping := bleve.NewDateTimeFieldMapping()
  193. logMapping.AddFieldMappingsAt("timestamp", timestampMapping)
  194. // File path with TextFieldMapping instead of KeywordFieldMapping
  195. filePathMapping := bleve.NewTextFieldMapping()
  196. filePathMapping.Store = true
  197. filePathMapping.Index = true
  198. // Use keyword analyzer for exact matching
  199. filePathMapping.Analyzer = "keyword"
  200. logMapping.AddFieldMappingsAt("file_path", filePathMapping)
  201. // Other text fields
  202. textMapping := bleve.NewTextFieldMapping()
  203. textMapping.Store = true
  204. textMapping.Index = true
  205. logMapping.AddFieldMappingsAt("ip", textMapping)
  206. logMapping.AddFieldMappingsAt("method", textMapping)
  207. logMapping.AddFieldMappingsAt("path", textMapping)
  208. // Numeric fields
  209. numericMapping := bleve.NewNumericFieldMapping()
  210. numericMapping.Store = true
  211. numericMapping.Index = true
  212. logMapping.AddFieldMappingsAt("status", numericMapping)
  213. // Create index mapping
  214. indexMapping := bleve.NewIndexMapping()
  215. // Use the default mapping instead of creating a separate document type
  216. indexMapping.DefaultMapping = logMapping
  217. return indexMapping
  218. }