Files
upstream-bleve/index_update_test.go
upstream-mirror f8d3d9f0cf
Some checks failed
Coverage / coverage (push) Failing after 6s
Tests / test (1.24.x, ubuntu-latest) (push) Failing after 42s
Tests / test (1.26.x, ubuntu-latest) (push) Failing after 38s
Tests / test (1.25.x, ubuntu-latest) (push) Failing after 35s
Tests / test (1.25.x, windows-latest) (push) Has been cancelled
Tests / test (1.26.x, macos-latest) (push) Has been cancelled
Tests / test (1.26.x, windows-latest) (push) Has been cancelled
Tests / test (1.24.x, macos-latest) (push) Has been cancelled
Tests / test (1.24.x, windows-latest) (push) Has been cancelled
Tests / test (1.25.x, macos-latest) (push) Has been cancelled
snapshot of https://github.com/blevesearch/bleve.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 22:27:16 +05:00

3251 lines
86 KiB
Go

// Copyright (c) 2025 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bleve
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"reflect"
"testing"
"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
"github.com/blevesearch/bleve/v2/analysis/analyzer/simple"
"github.com/blevesearch/bleve/v2/analysis/analyzer/standard"
"github.com/blevesearch/bleve/v2/analysis/datetime/percent"
"github.com/blevesearch/bleve/v2/analysis/datetime/sanitized"
"github.com/blevesearch/bleve/v2/analysis/lang/en"
"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
"github.com/blevesearch/bleve/v2/analysis/tokenizer/letter"
"github.com/blevesearch/bleve/v2/analysis/tokenizer/whitespace"
"github.com/blevesearch/bleve/v2/index/scorch"
"github.com/blevesearch/bleve/v2/index/scorch/mergeplan"
"github.com/blevesearch/bleve/v2/mapping"
index "github.com/blevesearch/bleve_index_api"
)
func TestCompareFieldMapping(t *testing.T) {
tests := []struct {
original *mapping.FieldMapping
updated *mapping.FieldMapping
indexFieldInfo *index.UpdateFieldInfo
err bool
}{
{ // both nil => error
original: nil,
updated: nil,
indexFieldInfo: nil,
err: true,
},
{ // updated nil => delete all
original: &mapping.FieldMapping{},
updated: nil,
indexFieldInfo: &index.UpdateFieldInfo{
Deleted: true,
},
err: false,
},
{ // type changed => not updatable
original: &mapping.FieldMapping{
Type: "text",
},
updated: &mapping.FieldMapping{
Type: "datetime",
},
indexFieldInfo: nil,
err: true,
},
{ // synonym source changed for text => updatable
original: &mapping.FieldMapping{
Type: "text",
SynonymSource: "a",
},
updated: &mapping.FieldMapping{
Type: "text",
SynonymSource: "b",
},
indexFieldInfo: &index.UpdateFieldInfo{},
err: false,
},
{ // analyser changed for text => not updatable
original: &mapping.FieldMapping{
Type: "text",
Analyzer: "a",
},
updated: &mapping.FieldMapping{
Type: "text",
Analyzer: "b",
},
indexFieldInfo: nil,
err: true,
},
{ // dims changed for vector => not updatable
original: &mapping.FieldMapping{
Type: "vector",
Dims: 128,
Similarity: "l2_norm",
VectorIndexOptimizedFor: "memory-efficient",
},
updated: &mapping.FieldMapping{
Type: "vector",
Dims: 1024,
Similarity: "l2_norm",
VectorIndexOptimizedFor: "memory-efficient",
},
indexFieldInfo: nil,
err: true,
},
{ // similarity changed for vectorbase64 => not updatable
original: &mapping.FieldMapping{
Type: "vector_base64",
Similarity: "l2_norm",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
},
updated: &mapping.FieldMapping{
Type: "vector_base64",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
},
indexFieldInfo: nil,
err: true,
},
{ // vectorindexoptimizedfor chagned for vector => not updatable
original: &mapping.FieldMapping{
Type: "vector",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
},
updated: &mapping.FieldMapping{
Type: "vector",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "latency",
},
indexFieldInfo: nil,
err: true,
},
{ // gpu changed for vector => not updatable
original: &mapping.FieldMapping{
Type: "vector",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
GPU: false,
},
updated: &mapping.FieldMapping{
Type: "vector",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
GPU: true,
},
indexFieldInfo: nil,
err: true,
},
{ // gpu changed for vectorbase64 => not updatable
original: &mapping.FieldMapping{
Type: "vector_base64",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
GPU: false,
},
updated: &mapping.FieldMapping{
Type: "vector_base64",
Similarity: "dot_product",
Dims: 128,
VectorIndexOptimizedFor: "memory-efficient",
GPU: true,
},
indexFieldInfo: nil,
err: true,
},
{ // includeinall changed => not updatable
original: &mapping.FieldMapping{
Type: "numeric",
IncludeInAll: true,
},
updated: &mapping.FieldMapping{
Type: "numeric",
IncludeInAll: false,
},
indexFieldInfo: nil,
err: true,
},
{ //includetermvectors changed => not updatable
original: &mapping.FieldMapping{
Type: "numeric",
IncludeTermVectors: false,
},
updated: &mapping.FieldMapping{
Type: "numeric",
IncludeTermVectors: true,
},
indexFieldInfo: nil,
err: true,
},
{ // store changed after all checks => updatable with store delete
original: &mapping.FieldMapping{
Type: "numeric",
SkipFreqNorm: true,
},
updated: &mapping.FieldMapping{
Type: "numeric",
SkipFreqNorm: false,
},
indexFieldInfo: nil,
err: true,
},
{ // index changed after all checks => updatable with index and docvalues delete
original: &mapping.FieldMapping{
Type: "geopoint",
Index: true,
},
updated: &mapping.FieldMapping{
Type: "geopoint",
Index: false,
},
indexFieldInfo: &index.UpdateFieldInfo{
Index: true,
DocValues: true,
},
err: false,
},
{ // docvalues changed after all checks => docvalues delete
original: &mapping.FieldMapping{
Type: "numeric",
DocValues: true,
},
updated: &mapping.FieldMapping{
Type: "numeric",
DocValues: false,
},
indexFieldInfo: &index.UpdateFieldInfo{
DocValues: true,
},
err: false,
},
{ // no relavent changes => continue but no op
original: &mapping.FieldMapping{
Name: "",
Type: "datetime",
Analyzer: "a",
Store: true,
Index: false,
IncludeTermVectors: true,
IncludeInAll: false,
DateFormat: "a",
DocValues: false,
SkipFreqNorm: true,
Dims: 128,
Similarity: "dot_product",
VectorIndexOptimizedFor: "memory-efficient",
SynonymSource: "a",
},
updated: &mapping.FieldMapping{
Name: "",
Type: "datetime",
Analyzer: "b",
Store: true,
Index: false,
IncludeTermVectors: true,
IncludeInAll: false,
DateFormat: "a",
DocValues: false,
SkipFreqNorm: true,
Dims: 256,
Similarity: "l2_norm",
VectorIndexOptimizedFor: "latency",
SynonymSource: "b",
},
indexFieldInfo: &index.UpdateFieldInfo{},
err: false,
},
}
for i, test := range tests {
rv, err := compareFieldMapping(test.original, test.updated)
if err == nil && test.err || err != nil && !test.err {
t.Errorf("Unexpected error value for test %d, expecting %t, got %v\n", i, test.err, err)
}
if rv == nil && test.indexFieldInfo != nil || rv != nil && test.indexFieldInfo == nil || !reflect.DeepEqual(rv, test.indexFieldInfo) {
t.Errorf("Unexpected index field info value for test %d, expecting %+v, got %+v, err %v", i, test.indexFieldInfo, rv, err)
}
}
}
func TestCompareMappings(t *testing.T) {
tests := []struct {
original *mapping.IndexMappingImpl
updated *mapping.IndexMappingImpl
err bool
}{
{ // changed type field when non empty mappings are present => error
original: &mapping.IndexMappingImpl{
TypeField: "a",
TypeMapping: map[string]*mapping.DocumentMapping{
"a": {},
"b": {},
},
},
updated: &mapping.IndexMappingImpl{
TypeField: "b",
TypeMapping: map[string]*mapping.DocumentMapping{
"a": {},
"b": {},
},
},
err: true,
},
{ // changed default type => error
original: &mapping.IndexMappingImpl{
DefaultType: "a",
},
updated: &mapping.IndexMappingImpl{
DefaultType: "b",
},
err: true,
},
{ // changed default analyzer => analyser true
original: &mapping.IndexMappingImpl{
DefaultAnalyzer: "a",
},
updated: &mapping.IndexMappingImpl{
DefaultAnalyzer: "b",
},
err: false,
},
{ // changed default datetimeparser => datetimeparser true
original: &mapping.IndexMappingImpl{
DefaultDateTimeParser: "a",
},
updated: &mapping.IndexMappingImpl{
DefaultDateTimeParser: "b",
},
err: false,
},
{ // changed default synonym source => synonym source true
original: &mapping.IndexMappingImpl{
DefaultSynonymSource: "a",
},
updated: &mapping.IndexMappingImpl{
DefaultSynonymSource: "b",
},
err: false,
},
{ // changed default field => false
original: &mapping.IndexMappingImpl{
DefaultField: "a",
},
updated: &mapping.IndexMappingImpl{
DefaultField: "b",
},
err: false,
},
{ // changed index dynamic => error
original: &mapping.IndexMappingImpl{
IndexDynamic: true,
},
updated: &mapping.IndexMappingImpl{
IndexDynamic: false,
},
err: true,
},
{ // changed store dynamic => error
original: &mapping.IndexMappingImpl{
StoreDynamic: false,
},
updated: &mapping.IndexMappingImpl{
StoreDynamic: true,
},
err: true,
},
{ // changed docvalues dynamic => error
original: &mapping.IndexMappingImpl{
DocValuesDynamic: true,
},
updated: &mapping.IndexMappingImpl{
DocValuesDynamic: false,
},
err: true,
},
}
for i, test := range tests {
err := compareMappings(test.original, test.updated)
if err == nil && test.err || err != nil && !test.err {
t.Errorf("Unexpected error value for test %d, expecting %t, got %v\n", i, test.err, err)
}
}
}
func TestCompareAnalysers(t *testing.T) {
ori := mapping.NewIndexMapping()
ori.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
ori.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
ori.DefaultMapping.AddFieldMappingsAt("c", NewTextFieldMapping())
ori.DefaultMapping.Properties["b"].DefaultAnalyzer = "3xbla"
ori.DefaultMapping.Properties["c"].DefaultAnalyzer = simple.Name
upd := mapping.NewIndexMapping()
upd.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
upd.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
upd.DefaultMapping.AddFieldMappingsAt("c", NewTextFieldMapping())
upd.DefaultMapping.Properties["b"].DefaultAnalyzer = "3xbla"
upd.DefaultMapping.Properties["c"].DefaultAnalyzer = simple.Name
if err := ori.AddCustomAnalyzer("3xbla", map[string]interface{}{
"type": custom.Name,
"tokenizer": whitespace.Name,
"token_filters": []interface{}{lowercase.Name, "stop_en"},
}); err != nil {
t.Fatal(err)
}
if err := upd.AddCustomAnalyzer("3xbla", map[string]interface{}{
"type": custom.Name,
"tokenizer": whitespace.Name,
"token_filters": []interface{}{lowercase.Name, "stop_en"},
}); err != nil {
t.Fatal(err)
}
oriPaths := map[string]*pathInfo{
"a": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "text",
},
},
},
dynamic: false,
path: "a",
parentPath: "",
},
"b": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "text",
},
},
},
dynamic: false,
path: "b",
parentPath: "",
},
"c": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "text",
},
},
},
dynamic: false,
path: "c",
parentPath: "",
},
}
updPaths := map[string]*pathInfo{
"a": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "text",
},
},
},
dynamic: false,
path: "a",
parentPath: "",
},
"b": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "text",
},
},
},
dynamic: false,
path: "b",
parentPath: "",
},
"c": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "text",
},
},
},
dynamic: false,
path: "c",
parentPath: "",
},
}
// Test case has identical analysers for text fields
err := compareAnalysers(oriPaths, updPaths, ori, upd)
if err != nil {
t.Errorf("Expected error to be nil, got %v", err)
}
ori2 := mapping.NewIndexMapping()
ori2.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
ori2.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
ori2.DefaultMapping.AddFieldMappingsAt("c", NewTextFieldMapping())
ori2.DefaultMapping.Properties["b"].DefaultAnalyzer = "3xbla"
ori2.DefaultMapping.Properties["c"].DefaultAnalyzer = simple.Name
upd2 := mapping.NewIndexMapping()
upd2.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
upd2.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
upd2.DefaultMapping.AddFieldMappingsAt("c", NewTextFieldMapping())
upd2.DefaultMapping.Properties["b"].DefaultAnalyzer = "3xbla"
upd2.DefaultMapping.Properties["c"].DefaultAnalyzer = simple.Name
if err := ori2.AddCustomAnalyzer("3xbla", map[string]interface{}{
"type": custom.Name,
"tokenizer": whitespace.Name,
"token_filters": []interface{}{lowercase.Name, "stop_en"},
}); err != nil {
t.Fatal(err)
}
if err := upd2.AddCustomAnalyzer("3xbla", map[string]interface{}{
"type": custom.Name,
"tokenizer": letter.Name,
"token_filters": []interface{}{lowercase.Name, "stop_en"},
}); err != nil {
t.Fatal(err)
}
// Test case has different custom analyser for field "b"
err = compareAnalysers(oriPaths, updPaths, ori2, upd2)
if err == nil {
t.Errorf("Expected error, got nil")
}
}
func TestCompareDatetimeParsers(t *testing.T) {
ori := mapping.NewIndexMapping()
ori.DefaultMapping.AddFieldMappingsAt("a", NewDateTimeFieldMapping())
ori.DefaultMapping.AddFieldMappingsAt("b", NewDateTimeFieldMapping())
ori.DefaultMapping.AddFieldMappingsAt("c", NewDateTimeFieldMapping())
ori.DefaultMapping.Properties["b"].Fields[0].DateFormat = "customDT"
ori.DefaultMapping.Properties["c"].Fields[0].DateFormat = percent.Name
upd := mapping.NewIndexMapping()
upd.DefaultMapping.AddFieldMappingsAt("a", NewDateTimeFieldMapping())
upd.DefaultMapping.AddFieldMappingsAt("b", NewDateTimeFieldMapping())
upd.DefaultMapping.AddFieldMappingsAt("c", NewDateTimeFieldMapping())
upd.DefaultMapping.Properties["b"].Fields[0].DateFormat = "customDT"
upd.DefaultMapping.Properties["c"].Fields[0].DateFormat = percent.Name
err := ori.AddCustomDateTimeParser("customDT", map[string]interface{}{
"type": sanitized.Name,
"layouts": []interface{}{
"02/01/2006 15:04:05",
"2006/01/02 3:04PM",
},
})
if err != nil {
t.Fatal(err)
}
err = upd.AddCustomDateTimeParser("customDT", map[string]interface{}{
"type": sanitized.Name,
"layouts": []interface{}{
"02/01/2006 15:04:05",
"2006/01/02 3:04PM",
},
})
if err != nil {
t.Fatal(err)
}
oriPaths := map[string]*pathInfo{
"a": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "datetime",
},
},
},
dynamic: false,
path: "a",
parentPath: "",
},
"b": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "datetime",
DateFormat: "customDT",
},
},
},
dynamic: false,
path: "b",
parentPath: "",
},
"c": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "datetime",
},
},
},
dynamic: false,
path: "c",
parentPath: "",
},
}
updPaths := map[string]*pathInfo{
"a": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "datetime",
},
},
},
dynamic: false,
path: "a",
parentPath: "",
},
"b": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "datetime",
DateFormat: "customDT",
},
},
},
dynamic: false,
path: "b",
parentPath: "",
},
"c": {
fieldMapInfo: []*fieldMapInfo{
{
fieldMapping: &mapping.FieldMapping{
Type: "datetime",
},
},
},
dynamic: false,
path: "c",
parentPath: "",
},
}
// Test case has identical datetime parsers for all fields
err = compareDateTimeParsers(oriPaths, updPaths, ori, upd)
if err != nil {
t.Fatalf("Expected error to be nil, got %v", err)
}
ori2 := mapping.NewIndexMapping()
ori2.DefaultMapping.AddFieldMappingsAt("a", NewDateTimeFieldMapping())
ori2.DefaultMapping.AddFieldMappingsAt("b", NewDateTimeFieldMapping())
ori2.DefaultMapping.AddFieldMappingsAt("c", NewDateTimeFieldMapping())
ori2.DefaultMapping.Properties["b"].Fields[0].DateFormat = "customDT"
ori2.DefaultMapping.Properties["c"].Fields[0].DateFormat = percent.Name
upd2 := mapping.NewIndexMapping()
upd2.DefaultMapping.AddFieldMappingsAt("a", NewDateTimeFieldMapping())
upd2.DefaultMapping.AddFieldMappingsAt("b", NewDateTimeFieldMapping())
upd2.DefaultMapping.AddFieldMappingsAt("c", NewDateTimeFieldMapping())
upd2.DefaultMapping.Properties["b"].Fields[0].DateFormat = "customDT"
upd2.DefaultMapping.Properties["c"].Fields[0].DateFormat = percent.Name
err = ori2.AddCustomDateTimeParser("customDT", map[string]interface{}{
"type": sanitized.Name,
"layouts": []interface{}{
"02/01/2006 15:04:05",
"2006/01/02 3:04PM",
},
})
if err != nil {
t.Fatal(err)
}
err = upd2.AddCustomDateTimeParser("customDT", map[string]interface{}{
"type": sanitized.Name,
"layouts": []interface{}{
"02/01/2006 15:04:05",
"2006/01/02",
},
})
if err != nil {
t.Fatal(err)
}
// test case has different custom datetime parser for field "b"
err = compareDateTimeParsers(oriPaths, updPaths, ori2, upd2)
if err == nil {
t.Errorf("Expected error, got nil")
}
}
func TestCompareSynonymSources(t *testing.T) {
ori := mapping.NewIndexMapping()
ori.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
ori.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
ori.DefaultMapping.DefaultSynonymSource = "syn1"
ori.DefaultMapping.Properties["b"].Fields[0].SynonymSource = "syn2"
upd := mapping.NewIndexMapping()
upd.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
upd.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
upd.DefaultMapping.DefaultSynonymSource = "syn1"
upd.DefaultMapping.Properties["b"].Fields[0].SynonymSource = "syn2"
err := ori.AddSynonymSource("syn1", map[string]interface{}{
"collection": "col1",
"analyzer": simple.Name,
})
if err != nil {
t.Fatal(err)
}
err = ori.AddSynonymSource("syn2", map[string]interface{}{
"collection": "col2",
"analyzer": standard.Name,
})
if err != nil {
t.Fatal(err)
}
err = upd.AddSynonymSource("syn1", map[string]interface{}{
"collection": "col1",
"analyzer": simple.Name,
})
if err != nil {
t.Fatal(err)
}
err = upd.AddSynonymSource("syn2", map[string]interface{}{
"collection": "col2",
"analyzer": standard.Name,
})
if err != nil {
t.Fatal(err)
}
// Test case has identical synonym sources
err = compareSynonymSources(ori, upd)
if err != nil {
t.Errorf("Expected error to be nil, got %v", err)
}
ori2 := mapping.NewIndexMapping()
ori2.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
ori2.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
ori2.DefaultMapping.DefaultSynonymSource = "syn1"
ori2.DefaultMapping.Properties["b"].Fields[0].SynonymSource = "syn2"
upd2 := mapping.NewIndexMapping()
upd2.DefaultMapping.AddFieldMappingsAt("a", NewTextFieldMapping())
upd2.DefaultMapping.AddFieldMappingsAt("b", NewTextFieldMapping())
upd2.DefaultMapping.DefaultSynonymSource = "syn1"
upd2.DefaultMapping.Properties["b"].Fields[0].SynonymSource = "syn2"
err = ori2.AddSynonymSource("syn1", map[string]interface{}{
"collection": "col1",
"analyzer": simple.Name,
})
if err != nil {
t.Fatal(err)
}
err = ori2.AddSynonymSource("syn2", map[string]interface{}{
"collection": "col2",
"analyzer": standard.Name,
})
if err != nil {
t.Fatal(err)
}
err = upd2.AddSynonymSource("syn1", map[string]interface{}{
"collection": "col1",
"analyzer": simple.Name,
})
if err != nil {
t.Fatal(err)
}
err = upd2.AddSynonymSource("syn2", map[string]interface{}{
"collection": "col3",
"analyzer": standard.Name,
})
if err != nil {
t.Fatal(err)
}
// test case has different synonym sources
err = compareSynonymSources(ori2, upd2)
if err == nil {
t.Errorf("Expected error, got nil")
}
}
func TestDeletedFields(t *testing.T) {
tests := []struct {
original *mapping.IndexMappingImpl
updated *mapping.IndexMappingImpl
fieldInfo map[string]*index.UpdateFieldInfo
err bool
}{
{
// changed default analyzer with index dynamic
// => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{},
DefaultAnalyzer: standard.Name,
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{},
DefaultAnalyzer: simple.Name,
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// changed default analyzer within a mapping with index dynamic
// => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: true,
DefaultAnalyzer: standard.Name,
},
DefaultAnalyzer: "",
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: true,
DefaultAnalyzer: simple.Name,
},
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// changed default datetime parser with index dynamic
// => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{},
DefaultDateTimeParser: percent.Name,
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{},
DefaultDateTimeParser: sanitized.Name,
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// no change between original and updated having type and default mapping
// => empty fieldInfo with no error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: map[string]*index.UpdateFieldInfo{},
err: false,
},
{
// no changes in type mappings and default mapping disabled with changes
// => empty fieldInfo with no error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: false,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: false,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"d": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: map[string]*index.UpdateFieldInfo{},
err: false,
},
{
// new type mappings in updated => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// new mappings in default mapping => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// fully removed mapping in type with some dynamic => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: true,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// semi removed mapping in default with some dynamic
// proper fieldInfo with no errors
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: map[string]*index.UpdateFieldInfo{
"b": {
Index: true,
DocValues: true,
},
},
err: false,
},
{
// two fields from diff paths with removed content matching
// => relavent fieldInfo
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: map[string]*index.UpdateFieldInfo{
"a": {
Index: true,
DocValues: true,
},
},
err: false,
},
{
// two fields from diff paths with removed content not matching
// => error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: nil,
err: true,
},
{
// two fields from the same path => relavent fieldInfo
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Name: "a",
Type: "numeric",
Index: true,
Store: true,
},
{
Name: "b",
Type: "numeric",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Name: "a",
Type: "numeric",
Index: false,
Store: true,
},
{
Name: "b",
Type: "numeric",
Index: true,
Store: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: map[string]*index.UpdateFieldInfo{
"a": {
Index: true,
DocValues: true,
},
"b": {
Store: true,
},
},
err: false,
},
{
// one store, one index, one dynamic and one all removed in type and default
// => relavent fieldInfo without error
original: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Store: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map3": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
DocValues: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"d": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: true,
Store: true,
DocValues: true,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
updated: &mapping.IndexMappingImpl{
TypeMapping: map[string]*mapping.DocumentMapping{
"map1": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Index: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map2": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
Store: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
"map3": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "numeric",
DocValues: false,
},
},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
},
DefaultMapping: &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "",
DefaultSynonymSource: "",
},
IndexDynamic: false,
StoreDynamic: false,
DocValuesDynamic: false,
CustomAnalysis: NewIndexMapping().CustomAnalysis,
},
fieldInfo: map[string]*index.UpdateFieldInfo{
"a": {
Index: true,
DocValues: true,
},
"b": {
Store: true,
},
"c": {
DocValues: true,
},
"d": {
Deleted: true,
},
},
err: false,
},
}
for i, test := range tests {
info, err := DeletedFields(test.original, test.updated)
if err == nil && test.err || err != nil && !test.err {
t.Errorf("Unexpected error value for test %d, expecting %t, got %v\n", i, test.err, err)
}
if info == nil && test.fieldInfo != nil || info != nil && test.fieldInfo == nil || !reflect.DeepEqual(info, test.fieldInfo) {
t.Errorf("Unexpected default info value for test %d, expecting %+v, got %+v, err %v", i, test.fieldInfo, info, err)
}
}
}
func TestIndexUpdateText(t *testing.T) {
tmpIndexPath := createTmpIndexPath(t)
defer cleanupTmpIndexPath(t, tmpIndexPath)
indexMappingBefore := mapping.NewIndexMapping()
indexMappingBefore.TypeMapping = map[string]*mapping.DocumentMapping{}
indexMappingBefore.DefaultMapping = &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"d": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
}
indexMappingBefore.IndexDynamic = false
indexMappingBefore.StoreDynamic = false
indexMappingBefore.DocValuesDynamic = false
index, err := New(tmpIndexPath, indexMappingBefore)
if err != nil {
t.Fatal(err)
}
doc1 := map[string]interface{}{"a": "xyz", "b": "abc", "c": "def", "d": "ghi"}
doc2 := map[string]interface{}{"a": "uvw", "b": "rst", "c": "klm", "d": "pqr"}
doc3 := map[string]interface{}{"a": "xyz", "b": "def", "c": "abc", "d": "mno"}
batch := index.NewBatch()
err = batch.Index("001", doc1)
if err != nil {
t.Fatal(err)
}
err = batch.Index("002", doc2)
if err != nil {
t.Fatal(err)
}
err = batch.Index("003", doc3)
if err != nil {
t.Fatal(err)
}
err = index.Batch(batch)
if err != nil {
t.Fatal(err)
}
err = index.Close()
if err != nil {
t.Fatal(err)
}
indexMappingAfter := mapping.NewIndexMapping()
indexMappingAfter.TypeMapping = map[string]*mapping.DocumentMapping{}
indexMappingAfter.DefaultMapping = &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: false,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: false,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
}
indexMappingAfter.IndexDynamic = false
indexMappingAfter.StoreDynamic = false
indexMappingAfter.DocValuesDynamic = false
mappingString, err := json.Marshal(indexMappingAfter)
if err != nil {
t.Fatal(err)
}
config := map[string]interface{}{
"updated_mapping": string(mappingString),
}
index, err = OpenUsing(tmpIndexPath, config)
if err != nil {
t.Fatal(err)
}
defer func() {
err := index.Close()
if err != nil {
t.Fatal(err)
}
}()
q1 := NewSearchRequest(NewQueryStringQuery("a:*"))
q1.Fields = append(q1.Fields, "a")
res1, err := index.Search(q1)
if err != nil {
t.Fatal(err)
}
if len(res1.Hits) != 3 {
t.Errorf("Expected 3 hits, got %d\n", len(res1.Hits))
}
if len(res1.Hits[0].Fields) != 1 {
t.Errorf("Expected 1 field, got %d\n", len(res1.Hits[0].Fields))
}
q2 := NewSearchRequest(NewQueryStringQuery("b:*"))
q2.Fields = append(q2.Fields, "b")
res2, err := index.Search(q2)
if err != nil {
t.Fatal(err)
}
if len(res2.Hits) != 0 {
t.Errorf("Expected 0 hits, got %d\n", len(res2.Hits))
}
q3 := NewSearchRequest(NewQueryStringQuery("c:*"))
q3.Fields = append(q3.Fields, "c")
res3, err := index.Search(q3)
if err != nil {
t.Fatal(err)
}
if len(res3.Hits) != 3 {
t.Errorf("Expected 3 hits, got %d\n", len(res3.Hits))
}
if len(res3.Hits[0].Fields) != 0 {
t.Errorf("Expected 0 fields, got %d\n", len(res3.Hits[0].Fields))
}
q4 := NewSearchRequest(NewQueryStringQuery("d:*"))
q4.Fields = append(q4.Fields, "d")
res4, err := index.Search(q4)
if err != nil {
t.Fatal(err)
}
if len(res4.Hits) != 0 {
t.Errorf("Expected 0 hits, got %d\n", len(res4.Hits))
}
}
func TestIndexUpdateSynonym(t *testing.T) {
tmpIndexPath := createTmpIndexPath(t)
defer cleanupTmpIndexPath(t, tmpIndexPath)
synonymCollection := "collection1"
synonymSourceName := "english"
analyzer := en.AnalyzerName
synonymSourceConfig := map[string]interface{}{
"collection": synonymCollection,
"analyzer": analyzer,
}
a := mapping.NewTextFieldMapping()
a.Analyzer = analyzer
a.SynonymSource = synonymSourceName
a.IncludeInAll = false
b := mapping.NewTextFieldMapping()
b.Analyzer = analyzer
b.SynonymSource = synonymSourceName
b.IncludeInAll = false
c := mapping.NewTextFieldMapping()
c.Analyzer = analyzer
c.SynonymSource = synonymSourceName
c.IncludeInAll = false
indexMappingBefore := mapping.NewIndexMapping()
indexMappingBefore.DefaultMapping.AddFieldMappingsAt("a", a)
indexMappingBefore.DefaultMapping.AddFieldMappingsAt("b", b)
indexMappingBefore.DefaultMapping.AddFieldMappingsAt("c", c)
err := indexMappingBefore.AddSynonymSource(synonymSourceName, synonymSourceConfig)
if err != nil {
t.Fatal(err)
}
indexMappingBefore.IndexDynamic = false
indexMappingBefore.StoreDynamic = false
indexMappingBefore.DocValuesDynamic = false
index, err := New(tmpIndexPath, indexMappingBefore)
if err != nil {
t.Fatal(err)
}
doc1 := map[string]interface{}{
"a": `The hardworking employee consistently strives to exceed expectations.
His industrious nature makes him a valuable asset to any team.
His conscientious attention to detail ensures that projects are completed efficiently and accurately.
He remains persistent even in the face of challenges.`,
"b": `The hardworking employee consistently strives to exceed expectations.
His industrious nature makes him a valuable asset to any team.
His conscientious attention to detail ensures that projects are completed efficiently and accurately.
He remains persistent even in the face of challenges.`,
"c": `The hardworking employee consistently strives to exceed expectations.
His industrious nature makes him a valuable asset to any team.
His conscientious attention to detail ensures that projects are completed efficiently and accurately.
He remains persistent even in the face of challenges.`,
}
doc2 := map[string]interface{}{
"a": `The tranquil surroundings of the retreat provide a perfect escape from the hustle and bustle of city life.
Guests enjoy the peaceful atmosphere, which is perfect for relaxation and rejuvenation.
The calm environment offers the ideal place to meditate and connect with nature.
Even the most stressed individuals find themselves feeling relaxed and at ease.`,
"b": `The tranquil surroundings of the retreat provide a perfect escape from the hustle and bustle of city life.
Guests enjoy the peaceful atmosphere, which is perfect for relaxation and rejuvenation.
The calm environment offers the ideal place to meditate and connect with nature.
Even the most stressed individuals find themselves feeling relaxed and at ease.`,
"c": `The tranquil surroundings of the retreat provide a perfect escape from the hustle and bustle of city life.
Guests enjoy the peaceful atmosphere, which is perfect for relaxation and rejuvenation.
The calm environment offers the ideal place to meditate and connect with nature.
Even the most stressed individuals find themselves feeling relaxed and at ease.`,
}
synDoc1 := &SynonymDefinition{Synonyms: []string{"hardworking", "industrious", "conscientious", "persistent", "focused", "devoted"}}
synDoc2 := &SynonymDefinition{Synonyms: []string{"tranquil", "peaceful", "calm", "relaxed", "unruffled"}}
batch := index.NewBatch()
err = batch.IndexSynonym("001", synonymCollection, synDoc1)
if err != nil {
t.Fatal(err)
}
err = batch.IndexSynonym("002", synonymCollection, synDoc2)
if err != nil {
t.Fatal(err)
}
err = batch.Index("003", doc1)
if err != nil {
t.Fatal(err)
}
err = batch.Index("004", doc2)
if err != nil {
t.Fatal(err)
}
err = index.Batch(batch)
if err != nil {
t.Fatal(err)
}
err = index.Close()
if err != nil {
t.Fatal(err)
}
indexMappingAfter := mapping.NewIndexMapping()
indexMappingAfter.DefaultMapping.AddFieldMappingsAt("a", a)
b.Index = false
indexMappingAfter.DefaultMapping.AddFieldMappingsAt("b", b)
err = indexMappingAfter.AddSynonymSource(synonymSourceName, synonymSourceConfig)
if err != nil {
t.Fatal(err)
}
indexMappingAfter.IndexDynamic = false
indexMappingAfter.StoreDynamic = false
indexMappingAfter.DocValuesDynamic = false
mappingString, err := json.Marshal(indexMappingAfter)
if err != nil {
t.Fatal(err)
}
config := map[string]interface{}{
"updated_mapping": string(mappingString),
}
index, err = OpenUsing(tmpIndexPath, config)
if err != nil {
t.Fatal(err)
}
defer func() {
err := index.Close()
if err != nil {
t.Fatal(err)
}
}()
q1 := NewSearchRequest(NewQueryStringQuery("a:devoted"))
res1, err := index.Search(q1)
if err != nil {
t.Fatal(err)
}
if len(res1.Hits) != 1 {
t.Errorf("Expected 1 hit, got %d\n", len(res1.Hits))
}
q2 := NewSearchRequest(NewQueryStringQuery("b:devoted"))
res2, err := index.Search(q2)
if err != nil {
t.Fatal(err)
}
if len(res2.Hits) != 0 {
t.Errorf("Expected 0 hits, got %d\n", len(res2.Hits))
}
q3 := NewSearchRequest(NewQueryStringQuery("c:unruffled"))
res3, err := index.Search(q3)
if err != nil {
t.Fatal(err)
}
if len(res3.Hits) != 0 {
t.Errorf("Expected 0 hits, got %d\n", len(res3.Hits))
}
}
func TestIndexUpdateMerge(t *testing.T) {
tmpIndexPath := createTmpIndexPath(t)
defer cleanupTmpIndexPath(t, tmpIndexPath)
indexMappingBefore := mapping.NewIndexMapping()
indexMappingBefore.TypeMapping = map[string]*mapping.DocumentMapping{}
indexMappingBefore.DefaultMapping = &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"d": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
}
indexMappingBefore.IndexDynamic = false
indexMappingBefore.StoreDynamic = false
indexMappingBefore.DocValuesDynamic = false
index, err := New(tmpIndexPath, indexMappingBefore)
if err != nil {
t.Fatal(err)
}
numDocsPerBatch := 1000
numBatches := 10
var batch *Batch
doc := make(map[string]interface{})
const letters = "abcdefghijklmnopqrstuvwxyz"
randStr := func() string {
result := make([]byte, 3)
for i := 0; i < 3; i++ {
result[i] = letters[rand.Intn(len(letters))]
}
return string(result)
}
for i := 0; i < numBatches; i++ {
batch = index.NewBatch()
for j := 0; j < numDocsPerBatch; j++ {
doc["a"] = randStr()
doc["b"] = randStr()
doc["c"] = randStr()
doc["d"] = randStr()
err = batch.Index(fmt.Sprintf("%d", i*numDocsPerBatch+j), doc)
if err != nil {
t.Fatal(err)
}
}
err = index.Batch(batch)
if err != nil {
t.Fatal(err)
}
}
err = index.Close()
if err != nil {
t.Fatal(err)
}
indexMappingAfter := mapping.NewIndexMapping()
indexMappingAfter.TypeMapping = map[string]*mapping.DocumentMapping{}
indexMappingAfter.DefaultMapping = &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"b": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: false,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
"c": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: false,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
}
indexMappingAfter.IndexDynamic = false
indexMappingAfter.StoreDynamic = false
indexMappingAfter.DocValuesDynamic = false
mappingString, err := json.Marshal(indexMappingAfter)
if err != nil {
t.Fatal(err)
}
config := map[string]interface{}{
"updated_mapping": string(mappingString),
}
index, err = OpenUsing(tmpIndexPath, config)
if err != nil {
t.Fatal(err)
}
defer func() {
err := index.Close()
if err != nil {
t.Fatal(err)
}
}()
impl, ok := index.(*indexImpl)
if !ok {
t.Fatalf("Typecasting index to indexImpl failed")
}
sindex, ok := impl.i.(*scorch.Scorch)
if !ok {
t.Fatalf("Typecasting index to scorch index failed")
}
err = sindex.ForceMerge(context.Background(), &mergeplan.SingleSegmentMergePlanOptions)
if err != nil {
t.Fatal(err)
}
q1 := NewSearchRequest(NewQueryStringQuery("a:*"))
q1.Fields = append(q1.Fields, "a")
res1, err := index.Search(q1)
if err != nil {
t.Fatal(err)
}
if len(res1.Hits) != 10 {
t.Errorf("Expected 10 hits, got %d\n", len(res1.Hits))
}
if len(res1.Hits[0].Fields) != 1 {
t.Errorf("Expected 1 field, got %d\n", len(res1.Hits[0].Fields))
}
q2 := NewSearchRequest(NewQueryStringQuery("b:*"))
q2.Fields = append(q2.Fields, "b")
res2, err := index.Search(q2)
if err != nil {
t.Fatal(err)
}
if len(res2.Hits) != 0 {
t.Errorf("Expected 0 hits, got %d\n", len(res2.Hits))
}
q3 := NewSearchRequest(NewQueryStringQuery("c:*"))
q3.Fields = append(q3.Fields, "c")
res3, err := index.Search(q3)
if err != nil {
t.Fatal(err)
}
if len(res3.Hits) != 10 {
t.Errorf("Expected 10 hits, got %d\n", len(res3.Hits))
}
if len(res3.Hits[0].Fields) != 0 {
t.Errorf("Expected 0 fields, got %d\n", len(res3.Hits[0].Fields))
}
q4 := NewSearchRequest(NewQueryStringQuery("d:*"))
q4.Fields = append(q4.Fields, "d")
res4, err := index.Search(q4)
if err != nil {
t.Fatal(err)
}
if len(res4.Hits) != 0 {
t.Errorf("Expected 0 hits, got %d\n", len(res4.Hits))
}
}
func BenchmarkIndexUpdateText(b *testing.B) {
tmpIndexPath := createTmpIndexPath(b)
defer cleanupTmpIndexPath(b, tmpIndexPath)
indexMappingBefore := mapping.NewIndexMapping()
indexMappingBefore.TypeMapping = map[string]*mapping.DocumentMapping{}
indexMappingBefore.DefaultMapping = &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: true,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
}
indexMappingBefore.IndexDynamic = false
indexMappingBefore.StoreDynamic = false
indexMappingBefore.DocValuesDynamic = false
index, err := New(tmpIndexPath, indexMappingBefore)
if err != nil {
b.Fatal(err)
}
numDocsPerBatch := 1000
numBatches := 5
var batch *Batch
doc := make(map[string]interface{})
const letters = "abcdefghijklmnopqrstuvwxyz"
randStr := func() string {
result := make([]byte, 3)
for i := 0; i < 3; i++ {
result[i] = letters[rand.Intn(len(letters))]
}
return string(result)
}
for i := 0; i < numBatches; i++ {
batch = index.NewBatch()
for j := 0; j < numDocsPerBatch; j++ {
doc["a"] = randStr()
err = batch.Index(fmt.Sprintf("%d", i*numDocsPerBatch+j), doc)
if err != nil {
b.Fatal(err)
}
}
err = index.Batch(batch)
if err != nil {
b.Fatal(err)
}
}
err = index.Close()
if err != nil {
b.Fatal(err)
}
indexMappingAfter := mapping.NewIndexMapping()
indexMappingAfter.TypeMapping = map[string]*mapping.DocumentMapping{}
indexMappingAfter.DefaultMapping = &mapping.DocumentMapping{
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{
"a": {
Enabled: true,
Dynamic: false,
Properties: map[string]*mapping.DocumentMapping{},
Fields: []*mapping.FieldMapping{
{
Type: "text",
Index: true,
Store: false,
},
},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
},
},
Fields: []*mapping.FieldMapping{},
DefaultAnalyzer: "standard",
DefaultSynonymSource: "",
}
indexMappingAfter.IndexDynamic = false
indexMappingAfter.StoreDynamic = false
indexMappingAfter.DocValuesDynamic = false
mappingString, err := json.Marshal(indexMappingAfter)
if err != nil {
b.Fatal(err)
}
config := map[string]interface{}{
"updated_mapping": string(mappingString),
}
index, err = OpenUsing(tmpIndexPath, config)
if err != nil {
b.Fatal(err)
}
defer func() {
err := index.Close()
if err != nil {
b.Fatal(err)
}
}()
b.ResetTimer()
for i := 0; i < b.N; i++ {
q := NewQueryStringQuery("a:*")
req := NewSearchRequest(q)
if _, err = index.Search(req); err != nil {
b.Fatal(err)
}
}
}
func TestIndexUpdateNestedMapping(t *testing.T) {
// Helper: create a mapping with optional nested structure
createCompanyMapping := func(nestedEmployees, nestedDepartments, nestedProjects, nestedLocations bool) *mapping.IndexMappingImpl {
rv := mapping.NewIndexMapping()
companyMapping := mapping.NewDocumentMapping()
// Basic fields
companyMapping.AddFieldMappingsAt("id", mapping.NewTextFieldMapping())
companyMapping.AddFieldMappingsAt("name", mapping.NewTextFieldMapping())
var deptMapping *mapping.DocumentMapping
// Departments nested conditionally
if !nestedDepartments {
deptMapping = mapping.NewDocumentMapping()
} else {
deptMapping = mapping.NewNestedDocumentMapping()
}
deptMapping.AddFieldMappingsAt("name", mapping.NewTextFieldMapping())
deptMapping.AddFieldMappingsAt("budget", mapping.NewNumericFieldMapping())
// Employees nested conditionally
var empMapping *mapping.DocumentMapping
if !nestedEmployees {
empMapping = mapping.NewNestedDocumentMapping()
} else {
empMapping = mapping.NewDocumentMapping()
}
empMapping.AddFieldMappingsAt("name", mapping.NewTextFieldMapping())
empMapping.AddFieldMappingsAt("role", mapping.NewTextFieldMapping())
deptMapping.AddSubDocumentMapping("employees", empMapping)
// Projects nested conditionally
var projMapping *mapping.DocumentMapping
if !nestedProjects {
projMapping = mapping.NewNestedDocumentMapping()
} else {
projMapping = mapping.NewDocumentMapping()
}
projMapping.AddFieldMappingsAt("title", mapping.NewTextFieldMapping())
projMapping.AddFieldMappingsAt("status", mapping.NewTextFieldMapping())
deptMapping.AddSubDocumentMapping("projects", projMapping)
companyMapping.AddSubDocumentMapping("departments", deptMapping)
// Locations nested conditionally
var locMapping *mapping.DocumentMapping
if nestedLocations {
locMapping = mapping.NewNestedDocumentMapping()
} else {
locMapping = mapping.NewDocumentMapping()
}
locMapping.AddFieldMappingsAt("address", mapping.NewTextFieldMapping())
locMapping.AddFieldMappingsAt("city", mapping.NewTextFieldMapping())
companyMapping.AddSubDocumentMapping("locations", locMapping)
rv.DefaultMapping.AddSubDocumentMapping("company", companyMapping)
return rv
}
tests := []struct {
name string
original *mapping.IndexMappingImpl
updated *mapping.IndexMappingImpl
expectErr bool
}{
{
name: "No nested to all nested",
original: createCompanyMapping(false, false, false, false),
updated: createCompanyMapping(true, true, true, true),
expectErr: true,
},
{
name: "No nested to mixed nested",
original: createCompanyMapping(false, false, false, false),
updated: createCompanyMapping(true, false, true, false),
expectErr: true,
},
{
name: "No nested to mixed nested",
original: createCompanyMapping(false, false, false, false),
updated: createCompanyMapping(true, true, true, false),
expectErr: true,
},
{
name: "Mixed nested to no nested",
original: createCompanyMapping(false, true, false, true),
updated: createCompanyMapping(false, false, true, true),
expectErr: true,
},
{
name: "All nested to no nested",
original: createCompanyMapping(true, true, true, true),
updated: createCompanyMapping(false, false, false, false),
expectErr: true,
},
{
name: "Mixed nested to all nested",
original: createCompanyMapping(true, false, true, false),
updated: createCompanyMapping(true, true, true, true),
expectErr: true,
},
{
name: "All nested to mixed nested",
original: createCompanyMapping(true, true, true, true),
updated: createCompanyMapping(true, false, true, false),
expectErr: true,
},
{
name: "No nested to no nested",
original: createCompanyMapping(false, false, false, false),
updated: createCompanyMapping(false, false, false, false),
expectErr: false,
},
{
name: "All nested to all nested",
original: createCompanyMapping(true, true, true, true),
updated: createCompanyMapping(true, true, true, true),
expectErr: false,
},
}
for _, test := range tests {
_, err := DeletedFields(test.original, test.updated)
if (err != nil) != test.expectErr {
t.Errorf("Test '%s' unexpected error state: got %v, expectErr %t", test.name, err, test.expectErr)
}
}
}