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