Support legacy fields
diff --git a/mapper/mapper.go b/mapper/mapper.go
index 0377c88..424896e 100644
--- a/mapper/mapper.go
+++ b/mapper/mapper.go
@@ -178,17 +178,36 @@
replacement = token.Wrap
}
- // Apply foundry and layer overrides
- if opts.Direction { // true means AtoB
- applyFoundryAndLayerOverrides(pattern, opts.FoundryA, opts.LayerA)
- applyFoundryAndLayerOverrides(replacement, opts.FoundryB, opts.LayerB)
- } else {
- applyFoundryAndLayerOverrides(pattern, opts.FoundryB, opts.LayerB)
- applyFoundryAndLayerOverrides(replacement, opts.FoundryA, opts.LayerA)
+ // Create deep copies of pattern and replacement to avoid modifying the original parsed rules
+ patternBytes, err := parser.SerializeToJSON(pattern)
+ if err != nil {
+ return nil, fmt.Errorf("failed to serialize pattern for copying: %w", err)
+ }
+ patternCopy, err := parser.ParseJSON(patternBytes)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse pattern copy: %w", err)
}
- // Create matcher and apply replacement
- m, err := matcher.NewMatcher(ast.Pattern{Root: pattern}, ast.Replacement{Root: replacement})
+ replacementBytes, err := parser.SerializeToJSON(replacement)
+ if err != nil {
+ return nil, fmt.Errorf("failed to serialize replacement for copying: %w", err)
+ }
+ replacementCopy, err := parser.ParseJSON(replacementBytes)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse replacement copy: %w", err)
+ }
+
+ // Apply foundry and layer overrides to the copies
+ if opts.Direction { // true means AtoB
+ applyFoundryAndLayerOverrides(patternCopy, opts.FoundryA, opts.LayerA)
+ applyFoundryAndLayerOverrides(replacementCopy, opts.FoundryB, opts.LayerB)
+ } else {
+ applyFoundryAndLayerOverrides(patternCopy, opts.FoundryB, opts.LayerB)
+ applyFoundryAndLayerOverrides(replacementCopy, opts.FoundryA, opts.LayerA)
+ }
+
+ // Create matcher and apply replacement using the copies
+ m, err := matcher.NewMatcher(ast.Pattern{Root: patternCopy}, ast.Replacement{Root: replacementCopy})
if err != nil {
return nil, fmt.Errorf("failed to create matcher: %w", err)
}
@@ -293,8 +312,38 @@
// Restore rewrites if they existed
if oldRewrites != nil {
- if resultMap, ok := resultData.(map[string]any); ok {
- resultMap["rewrites"] = oldRewrites
+ // Process old rewrites through AST to ensure backward compatibility
+ if rewritesList, ok := oldRewrites.([]any); ok {
+ processedRewrites := make([]any, len(rewritesList))
+ for i, rewriteData := range rewritesList {
+ // Marshal and unmarshal each rewrite to apply backward compatibility
+ rewriteBytes, err := json.Marshal(rewriteData)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal old rewrite %d: %w", i, err)
+ }
+ var rewrite ast.Rewrite
+ if err := json.Unmarshal(rewriteBytes, &rewrite); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal old rewrite %d: %w", i, err)
+ }
+ // Marshal back to get the transformed version
+ transformedBytes, err := json.Marshal(&rewrite)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal transformed rewrite %d: %w", i, err)
+ }
+ var transformedRewrite any
+ if err := json.Unmarshal(transformedBytes, &transformedRewrite); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal transformed rewrite %d: %w", i, err)
+ }
+ processedRewrites[i] = transformedRewrite
+ }
+ if resultMap, ok := resultData.(map[string]any); ok {
+ resultMap["rewrites"] = processedRewrites
+ }
+ } else {
+ // If it's not a list, restore as-is
+ if resultMap, ok := resultData.(map[string]any); ok {
+ resultMap["rewrites"] = oldRewrites
+ }
}
}
diff --git a/mapper/mapper_test.go b/mapper/mapper_test.go
index c97238b..b2fb377 100644
--- a/mapper/mapper_test.go
+++ b/mapper/mapper_test.go
@@ -263,6 +263,149 @@
}`,
expectError: false,
},
+ {
+ name: "Query with legacy rewrite field names",
+ mappingID: "test-mapper",
+ opts: MappingOptions{
+ Direction: AtoB,
+ },
+ input: `{
+ "@type": "koral:token",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Legacy rewrite with source instead of editor",
+ "source": "LegacyEditor",
+ "operation": "operation:legacy",
+ "origin": "LegacySource"
+ }
+ ],
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "PIDAT",
+ "layer": "p",
+ "match": "match:eq"
+ }
+ }`,
+ expected: `{
+ "@type": "koral:token",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Legacy rewrite with source instead of editor",
+ "editor": "LegacyEditor",
+ "operation": "operation:legacy",
+ "src": "LegacySource"
+ }
+ ],
+ "wrap": {
+ "@type": "koral:termGroup",
+ "operands": [
+ {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "PIDAT",
+ "layer": "p",
+ "match": "match:eq"
+ },
+ {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "AdjType",
+ "layer": "p",
+ "match": "match:eq",
+ "value": "Pdt"
+ }
+ ],
+ "relation": "relation:and"
+ }
+ }`,
+ },
+ {
+ name: "Query with mixed legacy and modern rewrite fields",
+ mappingID: "test-mapper",
+ opts: MappingOptions{
+ Direction: AtoB,
+ },
+ input: `{
+ "@type": "koral:token",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Modern rewrite",
+ "editor": "ModernEditor",
+ "operation": "operation:modern",
+ "original": {
+ "@type": "koral:term",
+ "foundry": "original",
+ "key": "original-key"
+ }
+ },
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Legacy rewrite with precedence test",
+ "editor": "PreferredEditor",
+ "source": "IgnoredSource",
+ "operation": "operation:precedence",
+ "original": "PreferredOriginal",
+ "src": "IgnoredSrc",
+ "origin": "IgnoredOrigin"
+ }
+ ],
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "PIDAT",
+ "layer": "p",
+ "match": "match:eq"
+ }
+ }`,
+ expected: `{
+ "@type": "koral:token",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Modern rewrite",
+ "editor": "ModernEditor",
+ "operation": "operation:modern",
+ "original": {
+ "@type": "koral:term",
+ "foundry": "original",
+ "key": "original-key"
+ }
+ },
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Legacy rewrite with precedence test",
+ "editor": "PreferredEditor",
+ "operation": "operation:precedence",
+ "original": "PreferredOriginal"
+ }
+ ],
+ "wrap": {
+ "@type": "koral:termGroup",
+ "operands": [
+ {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "PIDAT",
+ "layer": "p",
+ "match": "match:eq"
+ },
+ {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "AdjType",
+ "layer": "p",
+ "match": "match:eq",
+ "value": "Pdt"
+ }
+ ],
+ "relation": "relation:and"
+ }
+ }`,
+ },
}
for _, tt := range tests {