Parse json, where the modifying part is in "query".
diff --git a/.gitignore b/.gitignore
index af7cd3e..c5b8dd9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@
testdata/sandbox
examples/
README.md
+todo.txt
/vendor
/termmapper
\ No newline at end of file
diff --git a/cmd/termmapper/main.go b/cmd/termmapper/main.go
index d8ff609..9d45bbc 100644
--- a/cmd/termmapper/main.go
+++ b/cmd/termmapper/main.go
@@ -151,7 +151,7 @@
}
// Apply mappings
- result, err := m.ApplyMappings(mapID, mapper.MappingOptions{
+ result, err := m.ApplyQueryMappings(mapID, mapper.MappingOptions{
Direction: direction,
FoundryA: foundryA,
FoundryB: foundryB,
diff --git a/mapper/mapper.go b/mapper/mapper.go
index 1405b58..15e3984 100644
--- a/mapper/mapper.go
+++ b/mapper/mapper.go
@@ -81,8 +81,8 @@
Direction Direction
}
-// ApplyMappings applies the specified mapping rules to a JSON object
-func (m *Mapper) ApplyMappings(mappingID string, opts MappingOptions, jsonData any) (any, error) {
+// ApplyQueryMappings applies the specified mapping rules to a JSON object
+func (m *Mapper) ApplyQueryMappings(mappingID string, opts MappingOptions, jsonData any) (any, error) {
// Validate mapping ID
if _, exists := m.mappingLists[mappingID]; !exists {
return nil, fmt.Errorf("mapping list with ID %s not found", mappingID)
@@ -91,8 +91,32 @@
// Get the parsed rules
rules := m.parsedRules[mappingID]
+ // Check if we have a wrapper object with a "query" field
+ var queryData any
+ var hasQueryWrapper bool
+
+ if jsonMap, ok := jsonData.(map[string]any); ok {
+ if query, exists := jsonMap["query"]; exists {
+ queryData = query
+ hasQueryWrapper = true
+ }
+ }
+
+ // If no query wrapper was found, use the entire input
+ if !hasQueryWrapper {
+ // If the input itself is not a valid query object, return it as is
+ if !isValidQueryObject(jsonData) {
+ return jsonData, nil
+ }
+ queryData = jsonData
+ } else if queryData == nil || !isValidQueryObject(queryData) {
+ // If we have a query wrapper but the query is nil or not a valid object,
+ // return the original data
+ return jsonData, nil
+ }
+
// Convert input JSON to AST
- jsonBytes, err := json.Marshal(jsonData)
+ jsonBytes, err := json.Marshal(queryData)
if err != nil {
return nil, fmt.Errorf("failed to marshal input JSON: %w", err)
}
@@ -168,9 +192,33 @@
return nil, fmt.Errorf("failed to parse result JSON: %w", err)
}
+ // If we had a query wrapper, put the transformed data back in it
+ if hasQueryWrapper {
+ if wrapper, ok := jsonData.(map[string]any); ok {
+ wrapper["query"] = resultData
+ return wrapper, nil
+ }
+ }
+
return resultData, nil
}
+// isValidQueryObject checks if the query data is a valid object that can be processed
+func isValidQueryObject(data any) bool {
+ // Check if it's a map
+ queryMap, ok := data.(map[string]any)
+ if !ok {
+ return false
+ }
+
+ // Check if it has the required @type field
+ if _, ok := queryMap["@type"]; !ok {
+ return false
+ }
+
+ return true
+}
+
// applyFoundryAndLayerOverrides recursively applies foundry and layer overrides to terms
func applyFoundryAndLayerOverrides(node ast.Node, foundry, layer string) {
if node == nil {
diff --git a/mapper/mapper_test.go b/mapper/mapper_test.go
index c84b7a2..74696ec 100644
--- a/mapper/mapper_test.go
+++ b/mapper/mapper_test.go
@@ -204,7 +204,7 @@
require.NoError(t, err)
// Apply mappings
- result, err := m.ApplyMappings(tt.mappingID, tt.opts, inputData)
+ result, err := m.ApplyQueryMappings(tt.mappingID, tt.opts, inputData)
if tt.expectError {
assert.Error(t, err)
return
@@ -354,7 +354,7 @@
err := json.Unmarshal([]byte(tt.input), &inputData)
require.NoError(t, err)
- result, err := m.ApplyMappings("test-mapper", MappingOptions{Direction: AtoB}, inputData)
+ result, err := m.ApplyQueryMappings("test-mapper", MappingOptions{Direction: AtoB}, inputData)
if tt.expectError {
assert.Error(t, err)
assert.Equal(t, tt.errorMsg, err.Error())
@@ -366,3 +366,170 @@
})
}
}
+
+func TestQueryWrapperMappings(t *testing.T) {
+
+ mappingList := config.MappingList{
+ ID: "test-wrapper",
+ FoundryA: "opennlp",
+ LayerA: "orth",
+ FoundryB: "upos",
+ LayerB: "orth",
+ Mappings: []config.MappingRule{
+ "[opennlp/orth=Baum] <> [opennlp/orth=X]",
+ },
+ }
+
+ // Create a new mapper
+ m, err := NewMapper([]config.MappingList{mappingList})
+ require.NoError(t, err)
+
+ tests := []struct {
+ name string
+ mappingID string
+ opts MappingOptions
+ input string
+ expected string
+ expectError bool
+ }{
+ {
+ name: "Query wrapper case",
+ mappingID: "test-wrapper",
+ opts: MappingOptions{
+ Direction: AtoB,
+ },
+ input: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "collection": {
+ "@type": "koral:doc",
+ "key": "availability",
+ "match": "match:eq",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Free corpus access policy has been added.",
+ "editor": "Kustvakt",
+ "operation": "operation:injection",
+ "src": "Kustvakt"
+ }
+ ],
+ "type": "type:regex",
+ "value": "CC.*"
+ },
+ "query": {
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "Baum",
+ "layer": "orth",
+ "match": "match:eq"
+ }
+ }
+ }`,
+ expected: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "collection": {
+ "@type": "koral:doc",
+ "key": "availability",
+ "match": "match:eq",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "_comment": "Free corpus access policy has been added.",
+ "editor": "Kustvakt",
+ "operation": "operation:injection",
+ "src": "Kustvakt"
+ }
+ ],
+ "type": "type:regex",
+ "value": "CC.*"
+ },
+ "query": {
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "X",
+ "layer": "orth",
+ "match": "match:eq"
+ }
+ }
+ }`,
+ },
+ {
+ name: "Empty query field",
+ mappingID: "test-wrapper",
+ opts: MappingOptions{
+ Direction: AtoB,
+ },
+ input: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "query": null
+ }`,
+ expected: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "query": null
+ }`,
+ },
+ {
+ name: "Missing query field",
+ mappingID: "test-wrapper",
+ opts: MappingOptions{
+ Direction: AtoB,
+ },
+ input: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "collection": {
+ "@type": "koral:doc"
+ }
+ }`,
+ expected: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "collection": {
+ "@type": "koral:doc"
+ }
+ }`,
+ },
+ {
+ name: "Query field with non-object value",
+ mappingID: "test-wrapper",
+ opts: MappingOptions{
+ Direction: AtoB,
+ },
+ input: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "query": "invalid"
+ }`,
+ expected: `{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "query": "invalid"
+ }`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Parse input JSON
+ var inputData interface{}
+ err := json.Unmarshal([]byte(tt.input), &inputData)
+ require.NoError(t, err)
+
+ // Apply mappings
+ result, err := m.ApplyQueryMappings(tt.mappingID, tt.opts, inputData)
+ if tt.expectError {
+ assert.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+
+ // Parse expected JSON
+ var expectedData interface{}
+ err = json.Unmarshal([]byte(tt.expected), &expectedData)
+ require.NoError(t, err)
+
+ // Compare results
+ assert.Equal(t, expectedData, result)
+ })
+ }
+}