blob: 71cef1d13c71bdbdafdef842e45fa325ec0d2155 [file] [log] [blame]
Akron9bff63a2025-05-30 14:47:18 +02001package mapper
2
3import (
4 "testing"
5
Akron2ef703c2025-07-03 15:57:42 +02006 "github.com/KorAP/Koral-Mapper/config"
Akron9bff63a2025-05-30 14:47:18 +02007)
8
9// BenchmarkApplyQueryMappings benchmarks the optimized ApplyQueryMappings method
10func BenchmarkApplyQueryMappings(b *testing.B) {
11 // Setup test data
12 mappingList := config.MappingList{
13 ID: "test-mapper",
14 FoundryA: "opennlp",
15 LayerA: "p",
16 FoundryB: "tt",
17 LayerB: "p",
18 Mappings: []config.MappingRule{
19 "[PIAT] <> [PDAT]",
20 "[PPER & opennlp/m=PronType:Prs] <> [PPER]",
21 "[PRELS] <> [PRELAT]",
22 "[PIDAT] <> [PDAT]",
23 "[DET & opennlp/m=AdjType:Pdt] <> [ART]",
24 "[DET & opennlp/m=PronType:Ind] <> [PIS]",
25 },
26 }
27
28 mapper, err := NewMapper([]config.MappingList{mappingList})
29 if err != nil {
30 b.Fatalf("Failed to create mapper: %v", err)
31 }
32
33 // Test cases that represent different scenarios
34 testCases := []struct {
35 name string
36 data map[string]any
37 opts MappingOptions
38 }{
39 {
40 name: "Simple_term_match",
41 data: map[string]any{
42 "@type": "koral:term",
43 "foundry": "opennlp",
44 "key": "PIAT",
45 "layer": "p",
46 "match": "match:eq",
47 },
48 opts: MappingOptions{Direction: true},
49 },
50 {
51 name: "Complex_term_group_with_match",
52 data: map[string]any{
53 "@type": "koral:termGroup",
54 "operands": []any{
55 map[string]any{
56 "@type": "koral:term",
57 "foundry": "opennlp",
58 "key": "DET",
59 "layer": "p",
60 "match": "match:eq",
61 },
62 map[string]any{
63 "@type": "koral:term",
64 "foundry": "opennlp",
65 "key": "AdjType",
66 "layer": "m",
67 "match": "match:eq",
68 "value": "Pdt",
69 },
70 },
71 "relation": "relation:and",
72 },
73 opts: MappingOptions{Direction: true},
74 },
75 {
76 name: "No_match_scenario",
77 data: map[string]any{
78 "@type": "koral:term",
79 "foundry": "opennlp",
80 "key": "NOMATCH",
81 "layer": "p",
82 "match": "match:eq",
83 },
84 opts: MappingOptions{Direction: true},
85 },
86 {
87 name: "With_foundry_override",
88 data: map[string]any{
89 "@type": "koral:term",
90 "foundry": "opennlp",
91 "key": "PIAT",
92 "layer": "p",
93 "match": "match:eq",
94 },
95 opts: MappingOptions{
96 Direction: true,
97 FoundryB: "custom",
98 LayerB: "pos",
99 },
100 },
101 {
102 name: "Nested_token_structure",
103 data: map[string]any{
104 "@type": "koral:token",
105 "wrap": map[string]any{
106 "@type": "koral:termGroup",
107 "operands": []any{
108 map[string]any{
109 "@type": "koral:term",
110 "foundry": "opennlp",
111 "key": "PPER",
112 "layer": "p",
113 "match": "match:eq",
114 },
115 map[string]any{
116 "@type": "koral:term",
117 "foundry": "opennlp",
118 "key": "PronType",
119 "layer": "m",
120 "match": "match:eq",
121 "value": "Prs",
122 },
123 },
124 "relation": "relation:and",
125 },
126 },
127 opts: MappingOptions{Direction: true, AddRewrites: true},
128 },
129 }
130
131 // Run benchmarks for each test case
132 for _, tc := range testCases {
133 b.Run(tc.name, func(b *testing.B) {
134 for i := 0; i < b.N; i++ {
135 _, err := mapper.ApplyQueryMappings("test-mapper", tc.opts, tc.data)
136 if err != nil {
137 b.Fatalf("ApplyQueryMappings failed: %v", err)
138 }
139 }
140 })
141 }
142}
143
Akron9663af92026-02-20 13:45:08 +0100144// BenchmarkApplyResponseMappings benchmarks the SAX-based response annotation
145func BenchmarkApplyResponseMappings(b *testing.B) {
146 mappingList := config.MappingList{
147 ID: "bench-response",
148 FoundryA: "marmot",
149 LayerA: "p",
150 FoundryB: "opennlp",
151 LayerB: "p",
152 Mappings: []config.MappingRule{
153 "[DET] <> [DT]",
154 "[NN] <> [NOUN]",
155 "[ADJA] <> [ADJ]",
156 },
157 }
158
159 mapper, err := NewMapper([]config.MappingList{mappingList})
160 if err != nil {
161 b.Fatalf("Failed to create mapper: %v", err)
162 }
163
164 responseData := map[string]any{
165 "snippet": `<span title="marmot/p:DET">Der</span> <span title="marmot/p:ADJA">alte</span> <span title="marmot/p:NN">Mann</span>`,
166 }
167
168 opts := MappingOptions{Direction: AtoB}
169
Akron038397a2026-03-05 16:51:27 +0100170 for b.Loop() {
Akron9663af92026-02-20 13:45:08 +0100171 _, err := mapper.ApplyResponseMappings("bench-response", opts, responseData)
172 if err != nil {
173 b.Fatalf("ApplyResponseMappings failed: %v", err)
174 }
175 }
176}
177
Akron9bff63a2025-05-30 14:47:18 +0200178// BenchmarkApplyQueryMappingsWorstCase benchmarks the worst case scenario
179// where we have many rules but no matches (tests optimization effectiveness)
180func BenchmarkApplyQueryMappingsWorstCase(b *testing.B) {
181 // Create a mapper with many rules
182 manyRules := make([]config.MappingRule, 100)
Akrona8b9fbc2026-03-05 16:43:05 +0100183 for i := range 100 {
Akron9bff63a2025-05-30 14:47:18 +0200184 ruleChar := string(rune('A' + i%26))
185 manyRules[i] = config.MappingRule("[UNUSED" + ruleChar + "] <> [TARGET" + ruleChar + "]")
186 }
187
188 mappingList := config.MappingList{
189 ID: "many-rules",
190 FoundryA: "opennlp",
191 LayerA: "p",
192 FoundryB: "tt",
193 LayerB: "p",
194 Mappings: manyRules,
195 }
196
197 mapper, err := NewMapper([]config.MappingList{mappingList})
198 if err != nil {
199 b.Fatalf("Failed to create mapper: %v", err)
200 }
201
202 // Test data that won't match any rule
203 testData := map[string]any{
204 "@type": "koral:term",
205 "foundry": "opennlp",
206 "key": "NOMATCH",
207 "layer": "p",
208 "match": "match:eq",
209 }
210
211 opts := MappingOptions{Direction: true}
212
Akron038397a2026-03-05 16:51:27 +0100213 for b.Loop() {
Akron9bff63a2025-05-30 14:47:18 +0200214 _, err := mapper.ApplyQueryMappings("many-rules", opts, testData)
215 if err != nil {
216 b.Fatalf("ApplyQueryMappings failed: %v", err)
217 }
218 }
219}