1//  Copyright (c) 2014 Couchbase, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// 		http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package mapping
16
17import (
18	"encoding/json"
19	"fmt"
20	"reflect"
21	"testing"
22	"time"
23
24	"github.com/blevesearch/bleve/analysis/tokenizer/exception"
25	"github.com/blevesearch/bleve/analysis/tokenizer/regexp"
26	"github.com/blevesearch/bleve/document"
27	"github.com/blevesearch/bleve/numeric"
28)
29
30var mappingSource = []byte(`{
31    "types": {
32    	"beer": {
33    		"properties": {
34    			"name": {
35    				"fields": [
36    					{
37    						"name": "name",
38    						"type": "text",
39    						"analyzer": "standard",
40    						"store": true,
41    						"index": true,
42                            "include_term_vectors": true,
43                            "include_in_all": true,
44                            "docvalues": true
45    					}
46    				]
47    			}
48    		}
49    	},
50    	"brewery": {
51    	}
52    },
53    "type_field": "_type",
54    "default_type": "_default"
55}`)
56
57func buildMapping() IndexMapping {
58	nameFieldMapping := NewTextFieldMapping()
59	nameFieldMapping.Name = "name"
60	nameFieldMapping.Analyzer = "standard"
61
62	beerMapping := NewDocumentMapping()
63	beerMapping.AddFieldMappingsAt("name", nameFieldMapping)
64
65	breweryMapping := NewDocumentMapping()
66
67	mapping := NewIndexMapping()
68	mapping.AddDocumentMapping("beer", beerMapping)
69	mapping.AddDocumentMapping("brewery", breweryMapping)
70
71	return mapping
72}
73
74func TestUnmarshalMappingJSON(t *testing.T) {
75	mapping := buildMapping()
76
77	var indexMapping IndexMappingImpl
78	err := json.Unmarshal(mappingSource, &indexMapping)
79	if err != nil {
80		t.Fatal(err)
81	}
82	if !reflect.DeepEqual(&indexMapping, mapping) {
83		t.Errorf("expected %#v,\n got %#v", mapping, &indexMapping)
84	}
85}
86
87func TestMappingStructWithJSONTags(t *testing.T) {
88
89	mapping := buildMapping()
90
91	x := struct {
92		NoJSONTag string
93		Name      string `json:"name"`
94	}{
95		Name: "marty",
96	}
97
98	doc := document.NewDocument("1")
99	err := mapping.MapDocument(doc, x)
100	if err != nil {
101		t.Fatal(err)
102	}
103	foundJSONName := false
104	foundNoJSONName := false
105	count := 0
106	for _, f := range doc.Fields {
107		if f.Name() == "name" {
108			foundJSONName = true
109		}
110		if f.Name() == "NoJSONTag" {
111			foundNoJSONName = true
112		}
113		count++
114	}
115	if !foundJSONName {
116		t.Errorf("expected to find field named 'name'")
117	}
118	if !foundNoJSONName {
119		t.Errorf("expected to find field named 'NoJSONTag'")
120	}
121	if count != 2 {
122		t.Errorf("expected to find 2 find, found %d", count)
123	}
124}
125
126func TestMappingStructWithJSONTagsOneDisabled(t *testing.T) {
127
128	mapping := buildMapping()
129
130	x := struct {
131		Name      string `json:"name"`
132		Title     string `json:"-"`
133		NoJSONTag string
134	}{
135		Name: "marty",
136	}
137
138	doc := document.NewDocument("1")
139	err := mapping.MapDocument(doc, x)
140	if err != nil {
141		t.Fatal(err)
142	}
143	foundJSONName := false
144	foundNoJSONName := false
145	count := 0
146	for _, f := range doc.Fields {
147		if f.Name() == "name" {
148			foundJSONName = true
149		}
150		if f.Name() == "NoJSONTag" {
151			foundNoJSONName = true
152		}
153		count++
154	}
155	if !foundJSONName {
156		t.Errorf("expected to find field named 'name'")
157	}
158	if !foundNoJSONName {
159		t.Errorf("expected to find field named 'NoJSONTag'")
160	}
161	if count != 2 {
162		t.Errorf("expected to find 2 find, found %d", count)
163	}
164}
165
166func TestMappingStructWithAlternateTags(t *testing.T) {
167
168	mapping := buildMapping()
169	mapping.(*IndexMappingImpl).DefaultMapping.StructTagKey = "bleve"
170
171	x := struct {
172		NoBLEVETag string
173		Name       string `bleve:"name"`
174	}{
175		Name: "marty",
176	}
177
178	doc := document.NewDocument("1")
179	err := mapping.MapDocument(doc, x)
180	if err != nil {
181		t.Fatal(err)
182	}
183	foundBLEVEName := false
184	foundNoBLEVEName := false
185	count := 0
186	for _, f := range doc.Fields {
187		if f.Name() == "name" {
188			foundBLEVEName = true
189		}
190		if f.Name() == "NoBLEVETag" {
191			foundNoBLEVEName = true
192		}
193		count++
194	}
195	if !foundBLEVEName {
196		t.Errorf("expected to find field named 'name'")
197	}
198	if !foundNoBLEVEName {
199		t.Errorf("expected to find field named 'NoBLEVETag'")
200	}
201	if count != 2 {
202		t.Errorf("expected to find 2 find, found %d", count)
203	}
204}
205
206func TestMappingStructWithAlternateTagsTwoDisabled(t *testing.T) {
207
208	mapping := buildMapping()
209	mapping.(*IndexMappingImpl).DefaultMapping.StructTagKey = "bleve"
210
211	x := struct {
212		Name       string `json:"-"     bleve:"name"`
213		Title      string `json:"-"     bleve:"-"`
214		NoBLEVETag string `json:"-"`
215		Extra      string `json:"extra" bleve:"-"`
216	}{
217		Name: "marty",
218	}
219
220	doc := document.NewDocument("1")
221	err := mapping.MapDocument(doc, x)
222	if err != nil {
223		t.Fatal(err)
224	}
225	foundBLEVEName := false
226	foundNoBLEVEName := false
227	count := 0
228	for _, f := range doc.Fields {
229		if f.Name() == "name" {
230			foundBLEVEName = true
231		}
232		if f.Name() == "NoBLEVETag" {
233			foundNoBLEVEName = true
234		}
235		count++
236	}
237	if !foundBLEVEName {
238		t.Errorf("expected to find field named 'name'")
239	}
240	if !foundNoBLEVEName {
241		t.Errorf("expected to find field named 'NoBLEVETag'")
242	}
243	if count != 2 {
244		t.Errorf("expected to find 2 find, found %d", count)
245	}
246}
247
248func TestMappingStructWithPointerToString(t *testing.T) {
249
250	mapping := buildMapping()
251
252	name := "marty"
253
254	x := struct {
255		Name *string
256	}{
257		Name: &name,
258	}
259
260	doc := document.NewDocument("1")
261	err := mapping.MapDocument(doc, x)
262	if err != nil {
263		t.Fatal(err)
264	}
265	found := false
266	count := 0
267	for _, f := range doc.Fields {
268		if f.Name() == "Name" {
269			found = true
270		}
271		count++
272	}
273	if !found {
274		t.Errorf("expected to find field named 'Name'")
275	}
276	if count != 1 {
277		t.Errorf("expected to find 1 find, found %d", count)
278	}
279}
280
281func TestMappingJSONWithNull(t *testing.T) {
282
283	mapping := NewIndexMapping()
284
285	jsonbytes := []byte(`{"name":"marty", "age": null}`)
286	var jsondoc interface{}
287	err := json.Unmarshal(jsonbytes, &jsondoc)
288	if err != nil {
289		t.Fatal(err)
290	}
291
292	doc := document.NewDocument("1")
293	err = mapping.MapDocument(doc, jsondoc)
294	if err != nil {
295		t.Fatal(err)
296	}
297	found := false
298	count := 0
299	for _, f := range doc.Fields {
300		if f.Name() == "name" {
301			found = true
302		}
303		count++
304	}
305	if !found {
306		t.Errorf("expected to find field named 'name'")
307	}
308	if count != 1 {
309		t.Errorf("expected to find 1 find, found %d", count)
310	}
311}
312
313func TestMappingForPath(t *testing.T) {
314
315	enFieldMapping := NewTextFieldMapping()
316	enFieldMapping.Analyzer = "en"
317
318	docMappingA := NewDocumentMapping()
319	docMappingA.AddFieldMappingsAt("name", enFieldMapping)
320
321	customMapping := NewTextFieldMapping()
322	customMapping.Analyzer = "xyz"
323	customMapping.Name = "nameCustom"
324
325	subDocMappingB := NewDocumentMapping()
326	customFieldX := NewTextFieldMapping()
327	customFieldX.Analyzer = "analyzerx"
328	subDocMappingB.AddFieldMappingsAt("desc", customFieldX)
329
330	docMappingA.AddFieldMappingsAt("author", enFieldMapping, customMapping)
331	docMappingA.AddSubDocumentMapping("child", subDocMappingB)
332
333	mapping := NewIndexMapping()
334	mapping.AddDocumentMapping("a", docMappingA)
335
336	analyzerName := mapping.AnalyzerNameForPath("name")
337	if analyzerName != enFieldMapping.Analyzer {
338		t.Errorf("expected '%s' got '%s'", enFieldMapping.Analyzer, analyzerName)
339	}
340
341	analyzerName = mapping.AnalyzerNameForPath("nameCustom")
342	if analyzerName != customMapping.Analyzer {
343		t.Errorf("expected '%s' got '%s'", customMapping.Analyzer, analyzerName)
344	}
345
346	analyzerName = mapping.AnalyzerNameForPath("child.desc")
347	if analyzerName != customFieldX.Analyzer {
348		t.Errorf("expected '%s' got '%s'", customFieldX.Analyzer, analyzerName)
349	}
350
351}
352
353func TestMappingWithTokenizerDeps(t *testing.T) {
354
355	tokNoDeps := map[string]interface{}{
356		"type":   regexp.Name,
357		"regexp": "",
358	}
359
360	tokDepsL1 := map[string]interface{}{
361		"type":       exception.Name,
362		"tokenizer":  "a",
363		"exceptions": []string{".*"},
364	}
365
366	// this tests a 1-level dependency
367	// it is run 100 times to increase the
368	// likelihood that it fails along time way
369	// (depends on key order iteration in map)
370	for i := 0; i < 100; i++ {
371
372		m := NewIndexMapping()
373		ca := customAnalysis{
374			Tokenizers: map[string]map[string]interface{}{
375				"a": tokNoDeps,
376				"b": tokDepsL1,
377			},
378		}
379		err := ca.registerAll(m)
380		if err != nil {
381			t.Fatal(err)
382		}
383	}
384
385	tokDepsL2 := map[string]interface{}{
386		"type":       "exception",
387		"tokenizer":  "b",
388		"exceptions": []string{".*"},
389	}
390
391	// now test a second-level dependency
392	for i := 0; i < 100; i++ {
393
394		m := NewIndexMapping()
395		ca := customAnalysis{
396			Tokenizers: map[string]map[string]interface{}{
397				"a": tokNoDeps,
398				"b": tokDepsL1,
399				"c": tokDepsL2,
400			},
401		}
402		err := ca.registerAll(m)
403		if err != nil {
404			t.Fatal(err)
405		}
406	}
407
408	tokUnsatisfied := map[string]interface{}{
409		"type":      "exception",
410		"tokenizer": "e",
411	}
412
413	// now make sure an unsatisfied dep still
414	// results in an error
415	m := NewIndexMapping()
416	ca := customAnalysis{
417		Tokenizers: map[string]map[string]interface{}{
418			"a": tokNoDeps,
419			"b": tokDepsL1,
420			"c": tokDepsL2,
421			"d": tokUnsatisfied,
422		},
423	}
424	err := ca.registerAll(m)
425	if err == nil {
426		t.Fatal(err)
427	}
428}
429
430func TestEnablingDisablingStoringDynamicFields(t *testing.T) {
431
432	// first verify that with system defaults, dynamic field is stored
433	data := map[string]interface{}{
434		"name": "bleve",
435	}
436	doc := document.NewDocument("x")
437	mapping := NewIndexMapping()
438	err := mapping.MapDocument(doc, data)
439	if err != nil {
440		t.Fatal(err)
441	}
442	for _, field := range doc.Fields {
443		if field.Name() == "name" && !field.Options().IsStored() {
444			t.Errorf("expected field 'name' to be stored, isn't")
445		}
446	}
447
448	// now change system level defaults, verify dynamic field is not stored
449	StoreDynamic = false
450	defer func() {
451		StoreDynamic = true
452	}()
453
454	mapping = NewIndexMapping()
455	doc = document.NewDocument("y")
456	err = mapping.MapDocument(doc, data)
457	if err != nil {
458		t.Fatal(err)
459	}
460	for _, field := range doc.Fields {
461		if field.Name() == "name" && field.Options().IsStored() {
462			t.Errorf("expected field 'name' to be not stored, is")
463		}
464	}
465
466	// now override the system level defaults inside the index mapping
467	mapping = NewIndexMapping()
468	mapping.StoreDynamic = true
469	doc = document.NewDocument("y")
470	err = mapping.MapDocument(doc, data)
471	if err != nil {
472		t.Fatal(err)
473	}
474	for _, field := range doc.Fields {
475		if field.Name() == "name" && !field.Options().IsStored() {
476			t.Errorf("expected field 'name' to be stored, isn't")
477		}
478	}
479}
480
481func TestMappingBool(t *testing.T) {
482	boolMapping := NewBooleanFieldMapping()
483	docMapping := NewDocumentMapping()
484	docMapping.AddFieldMappingsAt("prop", boolMapping)
485	mapping := NewIndexMapping()
486	mapping.AddDocumentMapping("doc", docMapping)
487
488	pprop := false
489	x := struct {
490		Prop  bool  `json:"prop"`
491		PProp *bool `json:"pprop"`
492	}{
493		Prop:  true,
494		PProp: &pprop,
495	}
496
497	doc := document.NewDocument("1")
498	err := mapping.MapDocument(doc, x)
499	if err != nil {
500		t.Fatal(err)
501	}
502	foundProp := false
503	foundPProp := false
504	count := 0
505	for _, f := range doc.Fields {
506		if f.Name() == "prop" {
507			foundProp = true
508		}
509		if f.Name() == "pprop" {
510			foundPProp = true
511		}
512		count++
513	}
514	if !foundProp {
515		t.Errorf("expected to find bool field named 'prop'")
516	}
517	if !foundPProp {
518		t.Errorf("expected to find pointer to bool field named 'pprop'")
519	}
520	if count != 2 {
521		t.Errorf("expected to find 2 fields, found %d", count)
522	}
523}
524
525func TestDisableDefaultMapping(t *testing.T) {
526	indexMapping := NewIndexMapping()
527	indexMapping.DefaultMapping.Enabled = false
528
529	data := map[string]string{
530		"name": "bleve",
531	}
532
533	doc := document.NewDocument("x")
534	err := indexMapping.MapDocument(doc, data)
535	if err != nil {
536		t.Error(err)
537	}
538
539	if len(doc.Fields) > 0 {
540		t.Errorf("expected no fields, got %d", len(doc.Fields))
541	}
542}
543
544func TestInvalidFieldMappingStrict(t *testing.T) {
545	mappingBytes := []byte(`{"includeInAll":true,"name":"a parsed name"}`)
546
547	// first unmarhsal it without strict
548	var fm FieldMapping
549	err := json.Unmarshal(mappingBytes, &fm)
550	if err != nil {
551		t.Fatal(err)
552	}
553
554	if fm.Name != "a parsed name" {
555		t.Fatalf("expect to find field mapping name 'a parsed name', got '%s'", fm.Name)
556	}
557
558	// reset
559	fm.Name = ""
560
561	// now enable strict
562	MappingJSONStrict = true
563	defer func() {
564		MappingJSONStrict = false
565	}()
566
567	expectedInvalidKeys := []string{"includeInAll"}
568	expectedErr := fmt.Errorf("field mapping contains invalid keys: %v", expectedInvalidKeys)
569	err = json.Unmarshal(mappingBytes, &fm)
570	if err.Error() != expectedErr.Error() {
571		t.Fatalf("expected err: %v, got err: %v", expectedErr, err)
572	}
573
574	if fm.Name != "a parsed name" {
575		t.Fatalf("expect to find field mapping name 'a parsed name', got '%s'", fm.Name)
576	}
577
578}
579
580func TestInvalidDocumentMappingStrict(t *testing.T) {
581	mappingBytes := []byte(`{"defaultAnalyzer":true,"enabled":false}`)
582
583	// first unmarhsal it without strict
584	var dm DocumentMapping
585	err := json.Unmarshal(mappingBytes, &dm)
586	if err != nil {
587		t.Fatal(err)
588	}
589
590	if dm.Enabled != false {
591		t.Fatalf("expect to find document mapping enabled false, got '%t'", dm.Enabled)
592	}
593
594	// reset
595	dm.Enabled = true
596
597	// now enable strict
598	MappingJSONStrict = true
599	defer func() {
600		MappingJSONStrict = false
601	}()
602
603	expectedInvalidKeys := []string{"defaultAnalyzer"}
604	expectedErr := fmt.Errorf("document mapping contains invalid keys: %v", expectedInvalidKeys)
605	err = json.Unmarshal(mappingBytes, &dm)
606	if err.Error() != expectedErr.Error() {
607		t.Fatalf("expected err: %v, got err: %v", expectedErr, err)
608	}
609
610	if dm.Enabled != false {
611		t.Fatalf("expect to find document mapping enabled false, got '%t'", dm.Enabled)
612	}
613}
614
615func TestInvalidIndexMappingStrict(t *testing.T) {
616	mappingBytes := []byte(`{"typeField":"type","default_field":"all"}`)
617
618	// first unmarhsal it without strict
619	var im IndexMappingImpl
620	err := json.Unmarshal(mappingBytes, &im)
621	if err != nil {
622		t.Fatal(err)
623	}
624
625	if im.DefaultField != "all" {
626		t.Fatalf("expect to find index mapping default field 'all', got '%s'", im.DefaultField)
627	}
628
629	// reset
630	im.DefaultField = "_all"
631
632	// now enable strict
633	MappingJSONStrict = true
634	defer func() {
635		MappingJSONStrict = false
636	}()
637
638	expectedInvalidKeys := []string{"typeField"}
639	expectedErr := fmt.Errorf("index mapping contains invalid keys: %v", expectedInvalidKeys)
640	err = json.Unmarshal(mappingBytes, &im)
641	if err.Error() != expectedErr.Error() {
642		t.Fatalf("expected err: %v, got err: %v", expectedErr, err)
643	}
644
645	if im.DefaultField != "all" {
646		t.Fatalf("expect to find index mapping default field 'all', got '%s'", im.DefaultField)
647	}
648}
649
650func TestMappingBug353(t *testing.T) {
651	dataBytes := `{
652  "Reviews": [
653    {
654      "ReviewID": "RX16692001",
655      "Content": "Usually stay near the airport..."
656    }
657	],
658	"Other": {
659	  "Inside": "text"
660  },
661  "Name": "The Inn at Baltimore White Marsh"
662}`
663
664	var data map[string]interface{}
665	err := json.Unmarshal([]byte(dataBytes), &data)
666	if err != nil {
667		t.Fatal(err)
668	}
669
670	reviewContentFieldMapping := NewTextFieldMapping()
671	reviewContentFieldMapping.Analyzer = "crazy"
672
673	reviewsMapping := NewDocumentMapping()
674	reviewsMapping.Dynamic = false
675	reviewsMapping.AddFieldMappingsAt("Content", reviewContentFieldMapping)
676	otherMapping := NewDocumentMapping()
677	otherMapping.Dynamic = false
678	mapping := NewIndexMapping()
679	mapping.DefaultMapping.AddSubDocumentMapping("Reviews", reviewsMapping)
680	mapping.DefaultMapping.AddSubDocumentMapping("Other", otherMapping)
681
682	doc := document.NewDocument("x")
683	err = mapping.MapDocument(doc, data)
684	if err != nil {
685		t.Fatal(err)
686	}
687
688	// expect doc has only 2 fields
689	if len(doc.Fields) != 2 {
690		t.Errorf("expected doc with 2 fields, got: %d", len(doc.Fields))
691		for _, f := range doc.Fields {
692			t.Logf("field named: %s", f.Name())
693		}
694	}
695}
696
697func TestAnonymousStructFields(t *testing.T) {
698
699	type Contact0 string
700
701	type Contact1 struct {
702		Name string
703	}
704
705	type Contact2 interface{}
706
707	type Contact3 interface{}
708
709	type Thing struct {
710		Contact0
711		Contact1
712		Contact2
713		Contact3
714	}
715
716	x := Thing{
717		Contact0: "hello",
718		Contact1: Contact1{
719			Name: "marty",
720		},
721		Contact2: Contact1{
722			Name: "will",
723		},
724		Contact3: "steve",
725	}
726
727	doc := document.NewDocument("1")
728	m := NewIndexMapping()
729	err := m.MapDocument(doc, x)
730	if err != nil {
731		t.Fatal(err)
732	}
733
734	if len(doc.Fields) != 4 {
735		t.Fatalf("expected 4 fields, got %d", len(doc.Fields))
736	}
737	if doc.Fields[0].Name() != "Contact0" {
738		t.Errorf("expected field named 'Contact0', got '%s'", doc.Fields[0].Name())
739	}
740	if doc.Fields[1].Name() != "Name" {
741		t.Errorf("expected field named 'Name', got '%s'", doc.Fields[1].Name())
742	}
743	if doc.Fields[2].Name() != "Contact2.Name" {
744		t.Errorf("expected field named 'Contact2.Name', got '%s'", doc.Fields[2].Name())
745	}
746	if doc.Fields[3].Name() != "Contact3" {
747		t.Errorf("expected field named 'Contact3', got '%s'", doc.Fields[3].Name())
748	}
749
750	type AnotherThing struct {
751		Contact0 `json:"Alternate0"`
752		Contact1 `json:"Alternate1"`
753		Contact2 `json:"Alternate2"`
754		Contact3 `json:"Alternate3"`
755	}
756
757	y := AnotherThing{
758		Contact0: "hello",
759		Contact1: Contact1{
760			Name: "marty",
761		},
762		Contact2: Contact1{
763			Name: "will",
764		},
765		Contact3: "steve",
766	}
767
768	doc2 := document.NewDocument("2")
769	err = m.MapDocument(doc2, y)
770	if err != nil {
771		t.Fatal(err)
772	}
773
774	if len(doc2.Fields) != 4 {
775		t.Fatalf("expected 4 fields, got %d", len(doc2.Fields))
776	}
777	if doc2.Fields[0].Name() != "Alternate0" {
778		t.Errorf("expected field named 'Alternate0', got '%s'", doc2.Fields[0].Name())
779	}
780	if doc2.Fields[1].Name() != "Alternate1.Name" {
781		t.Errorf("expected field named 'Name', got '%s'", doc2.Fields[1].Name())
782	}
783	if doc2.Fields[2].Name() != "Alternate2.Name" {
784		t.Errorf("expected field named 'Alternate2.Name', got '%s'", doc2.Fields[2].Name())
785	}
786	if doc2.Fields[3].Name() != "Alternate3" {
787		t.Errorf("expected field named 'Alternate3', got '%s'", doc2.Fields[3].Name())
788	}
789}
790
791func TestAnonymousStructFieldWithJSONStructTagEmptString(t *testing.T) {
792	type InterfaceThing interface{}
793	type Thing struct {
794		InterfaceThing `json:""`
795	}
796
797	x := Thing{
798		InterfaceThing: map[string]interface{}{
799			"key": "value",
800		},
801	}
802
803	doc := document.NewDocument("1")
804	m := NewIndexMapping()
805	err := m.MapDocument(doc, x)
806	if err != nil {
807		t.Fatal(err)
808	}
809
810	if len(doc.Fields) != 1 {
811		t.Fatalf("expected 1 field, got %d", len(doc.Fields))
812	}
813	if doc.Fields[0].Name() != "key" {
814		t.Errorf("expected field named 'key', got '%s'", doc.Fields[0].Name())
815	}
816}
817
818func TestMappingPrimitives(t *testing.T) {
819
820	tests := []struct {
821		data interface{}
822	}{
823		{data: "marty"},
824		{data: int(1)},
825		{data: int8(2)},
826		{data: int16(3)},
827		{data: int32(4)},
828		{data: int64(5)},
829		{data: uint(6)},
830		{data: uint8(7)},
831		{data: uint16(8)},
832		{data: uint32(9)},
833		{data: uint64(10)},
834		{data: float32(11.0)},
835		{data: float64(12.0)},
836		{data: false},
837	}
838
839	m := NewIndexMapping()
840	for _, test := range tests {
841		doc := document.NewDocument("x")
842		err := m.MapDocument(doc, test.data)
843		if err != nil {
844			t.Fatal(err)
845		}
846		if len(doc.Fields) != 1 {
847			t.Errorf("expected 1 field, got %d for %v", len(doc.Fields), test.data)
848		}
849	}
850}
851
852func TestMappingForGeo(t *testing.T) {
853
854	type Location struct {
855		Lat float64
856		Lon float64
857	}
858
859	nameFieldMapping := NewTextFieldMapping()
860	nameFieldMapping.Name = "name"
861	nameFieldMapping.Analyzer = "standard"
862
863	locFieldMapping := NewGeoPointFieldMapping()
864
865	thingMapping := NewDocumentMapping()
866	thingMapping.AddFieldMappingsAt("name", nameFieldMapping)
867	thingMapping.AddFieldMappingsAt("location", locFieldMapping)
868
869	mapping := NewIndexMapping()
870	mapping.DefaultMapping = thingMapping
871
872	geopoints := []interface{}{}
873
874	// geopoint as a struct
875	geopoints = append(geopoints, struct {
876		Name     string    `json:"name"`
877		Location *Location `json:"location"`
878	}{
879		Name: "struct",
880		Location: &Location{
881			Lon: -180,
882			Lat: -90,
883		},
884	})
885
886	// geopoint as a map
887	geopoints = append(geopoints, struct {
888		Name     string                 `json:"name"`
889		Location map[string]interface{} `json:"location"`
890	}{
891		Name: "map",
892		Location: map[string]interface{}{
893			"lon": -180,
894			"lat": -90,
895		},
896	})
897
898	// geopoint as a slice
899	geopoints = append(geopoints, struct {
900		Name     string        `json:"name"`
901		Location []interface{} `json:"location"`
902	}{
903		Name: "slice",
904		Location: []interface{}{
905			-180, -90,
906		},
907	})
908
909	for i, geopoint := range geopoints {
910		doc := document.NewDocument(string(i))
911		err := mapping.MapDocument(doc, geopoint)
912		if err != nil {
913			t.Fatal(err)
914		}
915
916		var foundGeo bool
917		for _, f := range doc.Fields {
918			if f.Name() == "location" {
919				foundGeo = true
920				got := f.Value()
921				expect := []byte(numeric.MustNewPrefixCodedInt64(0, 0))
922				if !reflect.DeepEqual(got, expect) {
923					t.Errorf("expected geo value: %v, got %v", expect, got)
924				}
925			}
926		}
927
928		if !foundGeo {
929			t.Errorf("expected to find geo point, did not")
930		}
931	}
932}
933
934type textMarshalable struct {
935	body  string
936	Extra string
937}
938
939func (t *textMarshalable) MarshalText() ([]byte, error) {
940	return []byte(t.body), nil
941}
942
943func TestMappingForTextMarshaler(t *testing.T) {
944	tm := struct {
945		Marshalable *textMarshalable
946	}{
947		Marshalable: &textMarshalable{
948			body:  "text",
949			Extra: "stuff",
950		},
951	}
952
953	// first verify that when using a mapping that doesn't explicity
954	// map the stuct field as text, then we traverse inside the struct
955	// and do our best
956	m := NewIndexMapping()
957	doc := document.NewDocument("x")
958	err := m.MapDocument(doc, tm)
959	if err != nil {
960		t.Fatal(err)
961	}
962
963	if len(doc.Fields) != 1 {
964		t.Fatalf("expected 1 field, got: %d", len(doc.Fields))
965	}
966	if doc.Fields[0].Name() != "Marshalable.Extra" {
967		t.Errorf("expected field to be named 'Marshalable.Extra', got: '%s'", doc.Fields[0].Name())
968	}
969	if string(doc.Fields[0].Value()) != tm.Marshalable.Extra {
970		t.Errorf("expected field value to be '%s', got: '%s'", tm.Marshalable.Extra, string(doc.Fields[0].Value()))
971	}
972
973	// now verify that when a mapping explicity
974	m = NewIndexMapping()
975	txt := NewTextFieldMapping()
976	m.DefaultMapping.AddFieldMappingsAt("Marshalable", txt)
977	doc = document.NewDocument("x")
978	err = m.MapDocument(doc, tm)
979	if err != nil {
980		t.Fatal(err)
981	}
982
983	if len(doc.Fields) != 1 {
984		t.Fatalf("expected 1 field, got: %d", len(doc.Fields))
985
986	}
987	if doc.Fields[0].Name() != "Marshalable" {
988		t.Errorf("expected field to be named 'Marshalable', got: '%s'", doc.Fields[0].Name())
989	}
990	want, err := tm.Marshalable.MarshalText()
991	if err != nil {
992		t.Fatal(err)
993	}
994	if string(doc.Fields[0].Value()) != string(want) {
995		t.Errorf("expected field value to be  '%s', got: '%s'", string(want), string(doc.Fields[0].Value()))
996	}
997
998}
999
1000func TestMappingForNilTextMarshaler(t *testing.T) {
1001	tm := struct {
1002		Marshalable *time.Time
1003	}{
1004		Marshalable: nil,
1005	}
1006
1007	// now verify that when a mapping explicity
1008	m := NewIndexMapping()
1009	txt := NewTextFieldMapping()
1010	m.DefaultMapping.AddFieldMappingsAt("Marshalable", txt)
1011	doc := document.NewDocument("x")
1012	err := m.MapDocument(doc, tm)
1013	if err != nil {
1014		t.Fatal(err)
1015	}
1016
1017	if len(doc.Fields) != 0 {
1018		t.Fatalf("expected 1 field, got: %d", len(doc.Fields))
1019
1020	}
1021
1022}
1023
1024func TestClosestDocDynamicMapping(t *testing.T) {
1025	mapping := NewIndexMapping()
1026	mapping.IndexDynamic = false
1027	mapping.DefaultMapping = NewDocumentStaticMapping()
1028	mapping.DefaultMapping.AddFieldMappingsAt("foo", NewTextFieldMapping())
1029
1030	doc := document.NewDocument("x")
1031	err := mapping.MapDocument(doc, map[string]interface{}{
1032		"foo": "value",
1033		"bar": map[string]string{
1034			"foo": "value2",
1035			"baz": "value3",
1036		},
1037	})
1038	if err != nil {
1039		t.Fatal(err)
1040	}
1041
1042	if len(doc.Fields) != 1 {
1043		t.Fatalf("expected 1 field, got: %d", len(doc.Fields))
1044	}
1045}
1046