add support for mapping to recognize/use TextMarshaler interface
Sometimes you have structs which contain data which isn't exported, or for which the correct data to index isn't just the contents of it's exported fields. In these cases your struct can implement TextMarshaler to return a suitable text representation. Previously bleve did not recognize this interface and do anything to use it. Now, if the field containing such a struct is explicitly mapped as "text" and if the struct (or pointer to it) implements TextMarshaler, we index a text field with the contents returned by MarshalText(). For backwards compatibilty, dynamic mappings will never use this feature, and will continue to traverse into the struct and index the exported fields directly. fixes #281
This commit is contained in:
parent
5c9915c6f4
commit
9359a69ee5
|
@ -15,6 +15,7 @@
|
|||
package mapping
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
@ -481,6 +482,17 @@ func (dm *DocumentMapping) processProperty(property interface{}, path []string,
|
|||
fieldMapping := newDateTimeFieldMappingDynamic(context.im)
|
||||
fieldMapping.processTime(property, pathString, path, indexes, context)
|
||||
}
|
||||
case encoding.TextMarshaler:
|
||||
txt, err := property.MarshalText()
|
||||
if err == nil && subDocMapping != nil {
|
||||
// index by explicit mapping
|
||||
for _, fieldMapping := range subDocMapping.Fields {
|
||||
if fieldMapping.Type == "text" {
|
||||
fieldMapping.processString(string(txt), pathString, path, indexes, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
dm.walkDocument(property, path, indexes, context)
|
||||
default:
|
||||
if subDocMapping != nil {
|
||||
for _, fieldMapping := range subDocMapping.Fields {
|
||||
|
@ -500,6 +512,23 @@ func (dm *DocumentMapping) processProperty(property interface{}, path []string,
|
|||
}
|
||||
}
|
||||
dm.walkDocument(property, path, indexes, context)
|
||||
case reflect.Ptr:
|
||||
switch property := property.(type) {
|
||||
case encoding.TextMarshaler:
|
||||
txt, err := property.MarshalText()
|
||||
if err == nil && subDocMapping != nil {
|
||||
// index by explicit mapping
|
||||
for _, fieldMapping := range subDocMapping.Fields {
|
||||
if fieldMapping.Type == "text" {
|
||||
fieldMapping.processString(string(txt), pathString, path, indexes, context)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dm.walkDocument(property, path, indexes, context)
|
||||
}
|
||||
default:
|
||||
dm.walkDocument(property, path, indexes, context)
|
||||
}
|
||||
default:
|
||||
dm.walkDocument(property, path, indexes, context)
|
||||
}
|
||||
|
|
|
@ -900,3 +900,69 @@ func TestMappingForGeo(t *testing.T) {
|
|||
t.Errorf("expected to find geo point, did not")
|
||||
}
|
||||
}
|
||||
|
||||
type textMarshalable struct {
|
||||
body string
|
||||
Extra string
|
||||
}
|
||||
|
||||
func (t *textMarshalable) MarshalText() ([]byte, error) {
|
||||
return []byte(t.body), nil
|
||||
}
|
||||
|
||||
func TestMappingForTextMarshaler(t *testing.T) {
|
||||
tm := struct {
|
||||
Marshalable *textMarshalable
|
||||
}{
|
||||
Marshalable: &textMarshalable{
|
||||
body: "text",
|
||||
Extra: "stuff",
|
||||
},
|
||||
}
|
||||
|
||||
// first verify that when using a mapping that doesn't explicity
|
||||
// map the stuct field as text, then we traverse inside the struct
|
||||
// and do our best
|
||||
m := NewIndexMapping()
|
||||
doc := document.NewDocument("x")
|
||||
err := m.MapDocument(doc, tm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(doc.Fields) != 1 {
|
||||
t.Fatalf("expected 1 field, got: %d", len(doc.Fields))
|
||||
}
|
||||
if doc.Fields[0].Name() != "Marshalable.Extra" {
|
||||
t.Errorf("expected field to be named 'Marshalable.Extra', got: '%s'", doc.Fields[0].Name())
|
||||
}
|
||||
if string(doc.Fields[0].Value()) != tm.Marshalable.Extra {
|
||||
t.Errorf("expected field value to be '%s', got: '%s'", tm.Marshalable.Extra, string(doc.Fields[0].Value()))
|
||||
}
|
||||
|
||||
// now verify that when a mapping explicity
|
||||
m = NewIndexMapping()
|
||||
txt := NewTextFieldMapping()
|
||||
m.DefaultMapping.AddFieldMappingsAt("Marshalable", txt)
|
||||
doc = document.NewDocument("x")
|
||||
err = m.MapDocument(doc, tm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(doc.Fields) != 1 {
|
||||
t.Fatalf("expected 1 field, got: %d", len(doc.Fields))
|
||||
|
||||
}
|
||||
if doc.Fields[0].Name() != "Marshalable" {
|
||||
t.Errorf("expected field to be named 'Marshalable', got: '%s'", doc.Fields[0].Name())
|
||||
}
|
||||
want, err := tm.Marshalable.MarshalText()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(doc.Fields[0].Value()) != string(want) {
|
||||
t.Errorf("expected field value to be '%s', got: '%s'", string(want), string(doc.Fields[0].Value()))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue