Fix layer precedence and document precedences
Change-Id: I7fdc9f9122d7e723f98674a9f9e3010c38118ba9
diff --git a/mapper/response_test.go b/mapper/response_test.go
index 76fd45e..566f235 100644
--- a/mapper/response_test.go
+++ b/mapper/response_test.go
@@ -9,484 +9,6 @@
"github.com/stretchr/testify/require"
)
-func XTestResponseMapping(t *testing.T) {
-
- responseSnippet := `{
- "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
- "ID": null,
- "author": "Schmelzle, u.a.",
- "availability": "CC-BY-SA",
- "context": {
- "left": [
- "token",
- 0
- ],
- "right": [
- "token",
- 0
- ]
- },
- "corpusID": null,
- "corpusSigle": "WPD17",
- "docID": null,
- "docSigle": "WPD17/J80",
- "fields": [
- {
- "@type": "koral:field",
- "key": "ID"
- },
- {
- "@type": "koral:field",
- "key": "textSigle",
- "type": "type:string",
- "value": "WPD17/J80/33968"
- },
- {
- "@type": "koral:field",
- "key": "corpusID"
- },
- {
- "@type": "koral:field",
- "key": "author",
- "type": "type:text",
- "value": "Schmelzle, u.a."
- },
- {
- "@type": "koral:field",
- "key": "title",
- "type": "type:text",
- "value": "Johanne von Gemmingen"
- },
- {
- "@type": "koral:field",
- "key": "subTitle"
- },
- {
- "@type": "koral:field",
- "key": "textClass"
- },
- {
- "@type": "koral:field",
- "key": "pubPlace",
- "type": "type:string",
- "value": "URL:http://de.wikipedia.org"
- },
- {
- "@type": "koral:field",
- "key": "pubDate",
- "type": "type:date",
- "value": "2017-07-01"
- },
- {
- "@type": "koral:field",
- "key": "availability",
- "type": "type:string",
- "value": "CC-BY-SA"
- },
- {
- "@type": "koral:field",
- "key": "layerInfos",
- "type": "type:store",
- "value": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens"
- },
- {
- "@type": "koral:field",
- "key": "docSigle",
- "type": "type:string",
- "value": "WPD17/J80"
- },
- {
- "@type": "koral:field",
- "key": "corpusSigle",
- "type": "type:string",
- "value": "WPD17"
- }
- ],
- "hasSnippet": true,
- "hasTokens": false,
- "layerInfos": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens",
- "matchID": "p162-165(1)163-163x_yuvMM6VZLzLe_qZ0zb9yguvk37eDi-pSoL1nBdUkhNs",
- "meta": {
- "version": "Krill-0.64.1"
- },
- "pubDate": "2017-07-01",
- "pubPlace": "URL:http://de.wikipedia.org",
- "snippet": "<span class=\"context-left\">` +
- `</span>` +
- `<span class=\"match\">` +
- `<mark>` +
- `<span title=\"corenlp/p:ART\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:sg\">` +
- `<span title=\"marmot/p:ART\">` +
- `<span title=\"opennlp/p:ART\">` +
- `<span title=\"tt/l:die\">` +
- `<span title=\"tt/p:ART\">Der</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:ADJA\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:degree:pos\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:sg\">` +
- `<span title=\"marmot/p:ADJA\">` +
- `<span title=\"opennlp/p:ADJA\">` +
- `<span title=\"tt/l:alt\">` +
- `<span title=\"tt/p:ADJA\">alte</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:NN\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:sg\">` +
- `<span title=\"marmot/p:NN\">` +
- `<span title=\"opennlp/p:NN\">` +
- `<span title=\"tt/l:Baum\">` +
- `<span title=\"tt/p:NN\">Baum</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</mark> ` +
- `<span title=\"corenlp/p:KON\">` +
- `<span title=\"marmot/p:KON\">` +
- `<span title=\"opennlp/p:KON\">` +
- `<span title=\"tt/l:und\">` +
- `<span title=\"tt/p:KON\">und</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:ADJA\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:degree:pos\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:pl\">` +
- `<span title=\"marmot/p:ADJA\">` +
- `<span title=\"opennlp/p:ADJA\">` +
- `<span title=\"tt/l:andere\">` +
- `<span title=\"tt/p:PIAT\">` +
- `<span title=\"tt/p:PIS\">andere</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:NN\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:pl\">` +
- `<span title=\"marmot/p:NN\">` +
- `<span title=\"opennlp/p:NN\">` +
- `<span title=\"tt/l:Märchen\">` +
- `<span title=\"tt/p:NN\">Märchen</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>, ` +
- `<span title=\"corenlp/p:CARD\">` +
- `<span title=\"marmot/p:CARD\">` +
- `<span title=\"opennlp/p:CARD\">` +
- `<span title=\"tt/l:@card@\">` +
- `<span title=\"tt/p:CARD\">1946</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `</span>` +
- `<span class=\"context-right\"></span>",` +
- `"subTitle": null,
- "textClass": null,
- "textID": null,
- "textSigle": "WPD17/J80/33968",
- "title": "Johanne von Gemmingen"
-}`
-
- expectedOutput := `{
- "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
- "ID": null,
- "author": "Schmelzle, u.a.",
- "availability": "CC-BY-SA",
- "context": {
- "left": [
- "token",
- 0
- ],
- "right": [
- "token",
- 0
- ]
- },
- "corpusID": null,
- "corpusSigle": "WPD17",
- "docID": null,
- "docSigle": "WPD17/J80",
- "fields": [
- {
- "@type": "koral:field",
- "key": "ID"
- },
- {
- "@type": "koral:field",
- "key": "textSigle",
- "type": "type:string",
- "value": "WPD17/J80/33968"
- },
- {
- "@type": "koral:field",
- "key": "corpusID"
- },
- {
- "@type": "koral:field",
- "key": "author",
- "type": "type:text",
- "value": "Schmelzle, u.a."
- },
- {
- "@type": "koral:field",
- "key": "title",
- "type": "type:text",
- "value": "Johanne von Gemmingen"
- },
- {
- "@type": "koral:field",
- "key": "subTitle"
- },
- {
- "@type": "koral:field",
- "key": "textClass"
- },
- {
- "@type": "koral:field",
- "key": "pubPlace",
- "type": "type:string",
- "value": "URL:http://de.wikipedia.org"
- },
- {
- "@type": "koral:field",
- "key": "pubDate",
- "type": "type:date",
- "value": "2017-07-01"
- },
- {
- "@type": "koral:field",
- "key": "availability",
- "type": "type:string",
- "value": "CC-BY-SA"
- },
- {
- "@type": "koral:field",
- "key": "layerInfos",
- "type": "type:store",
- "value": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens"
- },
- {
- "@type": "koral:field",
- "key": "docSigle",
- "type": "type:string",
- "value": "WPD17/J80"
- },
- {
- "@type": "koral:field",
- "key": "corpusSigle",
- "type": "type:string",
- "value": "WPD17"
- }
- ],
- "hasSnippet": true,
- "hasTokens": false,
- "layerInfos": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens",
- "matchID": "p162-165(1)163-163x_yuvMM6VZLzLe_qZ0zb9yguvk37eDi-pSoL1nBdUkhNs",
- "meta": {
- "version": "Krill-0.64.1"
- },
- "pubDate": "2017-07-01",
- "pubPlace": "URL:http://de.wikipedia.org",
- "snippet": "<span class=\"context-left\">` +
- `</span>` +
- `<span class=\"match\">` +
- `<mark>` +
- `<span title=\"corenlp/p:ART\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:sg\">` +
- `<span title=\"marmot/p:ART\">` +
- `<span title=\"opennlp/p:ART\">` +
- `<span title=\"tt/l:die\">` +
- `<span title=\"tt/p:ART\">` +
- `<span title=\"opennlp/p:M\" class=\"notinindex\">` +
- `<span title=\"opennlp/m:M\" class=\"notinindex\">Der</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:ADJA\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:degree:pos\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:sg\">` +
- `<span title=\"marmot/p:ADJA\">` +
- `<span title=\"opennlp/p:ADJA\">` +
- `<span title=\"tt/l:alt\">` +
- `<span title=\"tt/p:ADJA\">` +
- `<span title=\"opennlp/p:M\" class=\"notinindex\">` +
- `<span title=\"opennlp/m:M\" class=\"notinindex\">alte</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:NN\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:sg\">` +
- `<span title=\"marmot/p:NN\">` +
- `<span title=\"opennlp/p:NN\">` +
- `<span title=\"tt/l:Baum\">` +
- `<span title=\"tt/p:NN\">` +
- `<span title=\"opennlp/p:M\" class=\"notinindex\">` +
- `<span title=\"opennlp/m:M\" class=\"notinindex\">Baum</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</mark> ` +
- `<span title=\"corenlp/p:KON\">` +
- `<span title=\"marmot/p:KON\">` +
- `<span title=\"opennlp/p:KON\">` +
- `<span title=\"tt/l:und\">` +
- `<span title=\"tt/p:KON\">und</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:ADJA\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:degree:pos\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:pl\">` +
- `<span title=\"marmot/p:ADJA\">` +
- `<span title=\"opennlp/p:ADJA\">` +
- `<span title=\"tt/l:andere\">` +
- `<span title=\"tt/p:PIAT\">` +
- `<span title=\"tt/p:PIS\">` +
- `<span title=\"opennlp/p:M\" class=\"notinindex\">` +
- `<span title=\"opennlp/m:M\" class=\"notinindex\">andere</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `<span title=\"corenlp/p:NN\">` +
- `<span title=\"marmot/m:case:nom\">` +
- `<span title=\"marmot/m:gender:masc\">` +
- `<span title=\"marmot/m:number:pl\">` +
- `<span title=\"marmot/p:NN\">` +
- `<span title=\"opennlp/p:NN\">` +
- `<span title=\"tt/l:Märchen\">` +
- `<span title=\"tt/p:NN\">` +
- `<span title=\"opennlp/p:M\" class=\"notinindex\">` +
- `<span title=\"opennlp/p:M\" class=\"notinindex\">Märchen</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span>, ` +
- `<span title=\"corenlp/p:CARD\">` +
- `<span title=\"marmot/p:CARD\">` +
- `<span title=\"opennlp/p:CARD\">` +
- `<span title=\"tt/l:@card@\">` +
- `<span title=\"tt/p:CARD\">1946</span>` +
- `</span>` +
- `</span>` +
- `</span>` +
- `</span> ` +
- `</span>` +
- `<span class=\"context-right\"></span>",` +
- `"subTitle": null,
- "textClass": null,
- "textID": null,
- "textSigle": "WPD17/J80/33968",
- "title": "Johanne von Gemmingen"
-}`
-
- // Create test mapping list specifically for token to termGroup test
- mappingList := config.MappingList{
- ID: "test-mapper",
- FoundryA: "marmot",
- LayerA: "m",
- FoundryB: "opennlp", // Keep the same foundry for both sides
- LayerB: "p",
- Mappings: []config.MappingRule{
- "[gender=masc] <> [opennlp/p=M & opennlp/m=M]",
- },
- }
-
- // Create a new mapper
- m, err := NewMapper([]config.MappingList{mappingList})
- require.NoError(t, err)
-
- var inputData any
- err = json.Unmarshal([]byte(responseSnippet), &inputData)
- assert.Nil(t, err)
-
- result, err := m.ApplyResponseMappings("test-mapper", MappingOptions{Direction: AtoB}, inputData)
- assert.Nil(t, err)
-
- var expectedData any
- err = json.Unmarshal([]byte(expectedOutput), &expectedData)
-
- assert.Equal(t, expectedData, result)
- assert.Nil(t, err)
-}
-
// TestResponseMappingAnnotationCreation tests creating new annotations based on RestrictToObligatory
func TestResponseMappingAnnotationCreation(t *testing.T) {
// Simple snippet with a single annotated token
@@ -970,3 +492,86 @@
author := resultMap["author"].(string)
assert.Equal(t, "John Doe", author)
}
+
+// TestResponseMappingWithLayerOverride tests layer precedence rules
+func TestResponseMappingWithLayerOverride(t *testing.T) {
+ // Test 1: Explicit layer in mapping rule should take precedence over MappingOptions
+ t.Run("Explicit layer takes precedence", func(t *testing.T) {
+ responseSnippet := `{
+ "snippet": "<span title=\"marmot/p:DET\">Der</span>"
+ }`
+
+ // Mapping rule with explicit layer [p=DT] - this should NOT be overridden
+ mappingList := config.MappingList{
+ ID: "test-layer-precedence",
+ FoundryA: "marmot",
+ LayerA: "p",
+ FoundryB: "opennlp",
+ LayerB: "p", // default layer
+ Mappings: []config.MappingRule{
+ "[DET] <> [p=DT]", // Explicit layer "p" should not be overridden
+ },
+ }
+
+ m, err := NewMapper([]config.MappingList{mappingList})
+ require.NoError(t, err)
+
+ var inputData any
+ err = json.Unmarshal([]byte(responseSnippet), &inputData)
+ require.NoError(t, err)
+
+ // Apply with layer override - should NOT affect explicit layer in mapping rule
+ result, err := m.ApplyResponseMappings("test-layer-precedence", MappingOptions{
+ Direction: AtoB,
+ LayerB: "pos", // This should NOT override the explicit "p" layer in [p=DT]
+ }, inputData)
+ require.NoError(t, err)
+
+ resultMap := result.(map[string]any)
+ snippet := resultMap["snippet"].(string)
+
+ // Should use explicit layer "p" from mapping rule, NOT "pos" from override
+ assert.Contains(t, snippet, `title="opennlp/p:DT" class="notinindex"`)
+ assert.NotContains(t, snippet, `title="opennlp/pos:DT" class="notinindex"`)
+ })
+
+ // Test 2: Implicit layer in mapping rule should use MappingOptions layer override
+ t.Run("Implicit layer uses MappingOptions override", func(t *testing.T) {
+ responseSnippet := `{
+ "snippet": "<span title=\"marmot/p:DET\">Der</span>"
+ }`
+
+ // Mapping rule with implicit layer [DT] - this should use layer override
+ mappingList := config.MappingList{
+ ID: "test-layer-override",
+ FoundryA: "marmot",
+ LayerA: "p",
+ FoundryB: "opennlp",
+ LayerB: "p", // default layer
+ Mappings: []config.MappingRule{
+ "[DET] <> [DT]", // No explicit layer - should use override
+ },
+ }
+
+ m, err := NewMapper([]config.MappingList{mappingList})
+ require.NoError(t, err)
+
+ var inputData any
+ err = json.Unmarshal([]byte(responseSnippet), &inputData)
+ require.NoError(t, err)
+
+ // Apply with layer override - should affect implicit layer in mapping rule
+ result, err := m.ApplyResponseMappings("test-layer-override", MappingOptions{
+ Direction: AtoB,
+ LayerB: "pos", // This should override the default layer for [DT]
+ }, inputData)
+ require.NoError(t, err)
+
+ resultMap := result.(map[string]any)
+ snippet := resultMap["snippet"].(string)
+
+ // Should use layer "pos" from override, NOT default "p" layer
+ assert.Contains(t, snippet, `title="opennlp/pos:DT" class="notinindex"`)
+ assert.NotContains(t, snippet, `title="opennlp/p:DT" class="notinindex"`)
+ })
+}