Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adds type and fields attributes in resource and data sources for search_index #1605

Merged
merged 27 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1306c0b
add type field
lantoli Nov 7, 2023
a79554d
add fields field
lantoli Nov 8, 2023
b050b0f
log when using existing cluster
lantoli Nov 10, 2023
3180d68
remove skip
lantoli Nov 10, 2023
2b19e79
change random prefixes
lantoli Nov 10, 2023
e3bb1e5
fix tests
lantoli Nov 10, 2023
1d69ae4
const collectionName
lantoli Nov 10, 2023
02b2b92
const searchAnalyzer
lantoli Nov 10, 2023
46a83d3
const resourceName and datasourceName
lantoli Nov 10, 2023
a615529
reduce cluster name size
lantoli Nov 10, 2023
62125d8
don't check dangling indices for existing clusters as it takes some t…
lantoli Nov 10, 2023
e8fa159
failing test for vector search index
lantoli Nov 10, 2023
71a8a7c
create vector index in test
lantoli Nov 10, 2023
90980ac
change data source
lantoli Nov 10, 2023
98a5bf3
continue if it can't unmarshal
lantoli Nov 10, 2023
f50782b
add fields to search-indexes data source
lantoli Nov 10, 2023
1cf1f42
initial doc
lantoli Nov 13, 2023
1f88258
small fix in doc
lantoli Nov 13, 2023
c5f6174
fix example
lantoli Nov 13, 2023
347f18e
Update search_index.html.markdown
Zuhairahmed Nov 15, 2023
262218d
Update search_index.html.markdown
Zuhairahmed Nov 15, 2023
8dd9c8b
adapt doc and example for Fields
lantoli Nov 15, 2023
d673dd4
detect format errors in fields and mappings_fields
lantoli Nov 15, 2023
06df642
detect format errors in analyzers
lantoli Nov 15, 2023
267d30e
test stringified JSON attributes for fields
lantoli Nov 15, 2023
e31705a
generic json diff check
lantoli Nov 15, 2023
45fa280
test with explicit search type
lantoli Nov 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions mongodbatlas/data_source_mongodbatlas_search_index.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ func returnSearchIndexDSSchema() map[string]*schema.Schema {
Optional: true,
Computed: true,
},
"type": {
Type: schema.TypeString,
Optional: true,
},
"fields": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: validateSearchIndexMappingDiff,
},
}
}

Expand All @@ -105,6 +114,10 @@ func dataSourceMongoDBAtlasSearchIndexRead(ctx context.Context, d *schema.Resour
return diag.Errorf("error getting search index information: %s", err)
}

if err := d.Set("type", searchIndex.Type); err != nil {
return diag.Errorf("error setting `type` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("index_id", indexID); err != nil {
return diag.Errorf("error setting `index_id` for search index (%s): %s", d.Id(), err)
}
Expand Down Expand Up @@ -140,21 +153,34 @@ func dataSourceMongoDBAtlasSearchIndexRead(ctx context.Context, d *schema.Resour
return diag.Errorf("error setting `searchAnalyzer` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("mappings_dynamic", searchIndex.Mappings.Dynamic); err != nil {
return diag.Errorf("error setting `mappings_dynamic` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("synonyms", flattenSearchIndexSynonyms(searchIndex.Synonyms)); err != nil {
return diag.Errorf("error setting `synonyms` for search index (%s): %s", d.Id(), err)
}

if len(searchIndex.Mappings.Fields) > 0 {
searchIndexMappingFields, err := marshalSearchIndex(searchIndex.Mappings.Fields)
if searchIndex.Mappings != nil {
marcosuma marked this conversation as resolved.
Show resolved Hide resolved
if err := d.Set("mappings_dynamic", searchIndex.Mappings.Dynamic); err != nil {
return diag.Errorf("error setting `mappings_dynamic` for search index (%s): %s", d.Id(), err)
}

if len(searchIndex.Mappings.Fields) > 0 {
searchIndexMappingFields, err := marshalSearchIndex(searchIndex.Mappings.Fields)
if err != nil {
return diag.FromErr(err)
}
if err := d.Set("mappings_fields", searchIndexMappingFields); err != nil {
return diag.Errorf("error setting `mappings_fields` for for search index (%s): %s", d.Id(), err)
}
}
}

if len(searchIndex.Fields) > 0 {
fields, err := marshalSearchIndex(searchIndex.Fields)
if err != nil {
return diag.FromErr(err)
}
if err := d.Set("mappings_fields", searchIndexMappingFields); err != nil {
return diag.Errorf("error setting `mappings_fields` for for search index (%s): %s", d.Id(), err)

if err := d.Set("fields", fields); err != nil {
return diag.Errorf("error setting `fields` for for search index (%s): %s", d.Id(), err)
}
}

Expand Down
44 changes: 28 additions & 16 deletions mongodbatlas/data_source_mongodbatlas_search_indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,25 +100,29 @@ func flattenSearchIndexes(searchIndexes []admin.ClusterSearchIndex, projectID, c

for i := range searchIndexes {
searchIndexesMap[i] = map[string]any{
"project_id": projectID,
"cluster_name": clusterName,
"analyzer": searchIndexes[i].Analyzer,
"collection_name": searchIndexes[i].CollectionName,
"database": searchIndexes[i].Database,
"index_id": searchIndexes[i].IndexID,
"mappings_dynamic": searchIndexes[i].Mappings.Dynamic,
marcosuma marked this conversation as resolved.
Show resolved Hide resolved
"name": searchIndexes[i].Name,
"search_analyzer": searchIndexes[i].SearchAnalyzer,
"status": searchIndexes[i].Status,
"synonyms": flattenSearchIndexSynonyms(searchIndexes[i].Synonyms),
"project_id": projectID,
"cluster_name": clusterName,
"analyzer": searchIndexes[i].Analyzer,
"collection_name": searchIndexes[i].CollectionName,
"database": searchIndexes[i].Database,
"index_id": searchIndexes[i].IndexID,
"name": searchIndexes[i].Name,
"search_analyzer": searchIndexes[i].SearchAnalyzer,
"status": searchIndexes[i].Status,
"synonyms": flattenSearchIndexSynonyms(searchIndexes[i].Synonyms),
"type": searchIndexes[i].Type,
}

if len(searchIndexes[i].Mappings.Fields) > 0 {
searchIndexMappingFields, err := marshalSearchIndex(searchIndexes[i].Mappings.Fields)
if err != nil {
return nil, err
if searchIndexes[i].Mappings != nil {
searchIndexesMap[i]["mappings_dynamic"] = searchIndexes[i].Mappings.Dynamic

if len(searchIndexes[i].Mappings.Fields) > 0 {
searchIndexMappingFields, err := marshalSearchIndex(searchIndexes[i].Mappings.Fields)
if err != nil {
return nil, err
}
searchIndexesMap[i]["mappings_fields"] = searchIndexMappingFields
}
searchIndexesMap[i]["mappings_fields"] = searchIndexMappingFields
}

if len(searchIndexes[i].Analyzers) > 0 {
Expand All @@ -128,6 +132,14 @@ func flattenSearchIndexes(searchIndexes []admin.ClusterSearchIndex, projectID, c
}
searchIndexesMap[i]["analyzers"] = searchIndexAnalyzers
}

if len(searchIndexes[i].Fields) > 0 {
fields, err := marshalSearchIndex(searchIndexes[i].Fields)
if err != nil {
return nil, err
}
searchIndexesMap[i]["fields"] = fields
}
}

return searchIndexesMap, nil
Expand Down
141 changes: 111 additions & 30 deletions mongodbatlas/resource_mongodbatlas_search_index.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import (
"go.mongodb.org/atlas-sdk/v20231115001/admin"
)

const (
vectorSearch = "vectorSearch"
)

func resourceMongoDBAtlasSearchIndex() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceMongoDBAtlasSearchIndexCreate,
Expand Down Expand Up @@ -115,6 +119,15 @@ func returnSearchIndexSchema() map[string]*schema.Schema {
Type: schema.TypeBool,
Optional: true,
},
"type": {
Type: schema.TypeString,
Optional: true,
},
"fields": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have we considered adding a conflictsWith validation to ensure vectorSearch attributes are not defined together with regular search attributes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at the end I preferred that all those checks are done in the server. If we do them in client-side there is some risk that both logics differ now or in the future.

Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: validateSearchIndexMappingDiff,
},
}
}

Expand Down Expand Up @@ -181,12 +194,12 @@ func resourceMongoDBAtlasSearchIndexUpdate(ctx context.Context, d *schema.Resour
return diag.Errorf("error getting search index information: %s", err)
}

if d.HasChange("analyzer") {
searchIndex.Analyzer = stringPtr(d.Get("analyzer").(string))
if d.HasChange("type") {
searchIndex.Type = stringPtr(d.Get("type").(string))
}

if d.HasChange("analyzers") {
searchIndex.Analyzers = unmarshalSearchIndexAnalyzersFields(d.Get("analyzers").(string))
if d.HasChange("analyzer") {
searchIndex.Analyzer = stringPtr(d.Get("analyzer").(string))
}

if d.HasChange("collection_name") {
Expand All @@ -210,8 +223,28 @@ func resourceMongoDBAtlasSearchIndexUpdate(ctx context.Context, d *schema.Resour
searchIndex.Mappings.Dynamic = &dynamic
}

if d.HasChange("analyzers") {
analyzers, err := unmarshalSearchIndexAnalyzersFields(d.Get("analyzers").(string))
if err != nil {
return err
}
searchIndex.Analyzers = analyzers
}

if d.HasChange("mappings_fields") {
searchIndex.Mappings.Fields = unmarshalSearchIndexMappingFields(d.Get("mappings_fields").(string))
mappingsFields, err := unmarshalSearchIndexMappingFields(d.Get("mappings_fields").(string))
if err != nil {
return err
}
searchIndex.Mappings.Fields = mappingsFields
}

if d.HasChange("fields") {
fields, err := unmarshalSearchIndexFields(d.Get("fields").(string))
if err != nil {
return err
}
searchIndex.Fields = fields
}

if d.HasChange("synonyms") {
Expand Down Expand Up @@ -272,6 +305,10 @@ func resourceMongoDBAtlasSearchIndexRead(ctx context.Context, d *schema.Resource
return diag.Errorf("error setting `index_id` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("type", searchIndex.Type); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the API always responds with a value for the type field? Would consider marking as computed or defining a default value in the schema to avoid inconsistencies in the state.

Copy link
Member Author

@lantoli lantoli Nov 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the index was created withouth type, it won't return type. API responds same as in creation so I won't use computed.
as it's defined as optional, it has default value = "" (zero value for strings), this is what we want. When refreshing the state the "type" attribute is added to the state with "" but no plan change is shown to the users.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to have this question/answer represented with a test, not necessarily blocking this PR (unless it's trivial creating it).

return diag.Errorf("error setting `type` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("analyzer", searchIndex.Analyzer); err != nil {
return diag.Errorf("error setting `analyzer` for search index (%s): %s", d.Id(), err)
}
Expand All @@ -283,7 +320,7 @@ func resourceMongoDBAtlasSearchIndexRead(ctx context.Context, d *schema.Resource
}

if err := d.Set("analyzers", searchIndexMappingFields); err != nil {
return diag.Errorf("error setting `analyzer` for search index (%s): %s", d.Id(), err)
return diag.Errorf("error setting `analyzers` for search index (%s): %s", d.Id(), err)
}
}

Expand All @@ -303,22 +340,35 @@ func resourceMongoDBAtlasSearchIndexRead(ctx context.Context, d *schema.Resource
return diag.Errorf("error setting `searchAnalyzer` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("mappings_dynamic", searchIndex.Mappings.Dynamic); err != nil {
return diag.Errorf("error setting `mappings_dynamic` for search index (%s): %s", d.Id(), err)
}

if err := d.Set("synonyms", flattenSearchIndexSynonyms(searchIndex.Synonyms)); err != nil {
return diag.Errorf("error setting `synonyms` for search index (%s): %s", d.Id(), err)
}

if len(searchIndex.Mappings.Fields) > 0 {
searchIndexMappingFields, err := marshalSearchIndex(searchIndex.Mappings.Fields)
if searchIndex.Mappings != nil {
if err := d.Set("mappings_dynamic", searchIndex.Mappings.Dynamic); err != nil {
return diag.Errorf("error setting `mappings_dynamic` for search index (%s): %s", d.Id(), err)
}

if len(searchIndex.Mappings.Fields) > 0 {
searchIndexMappingFields, err := marshalSearchIndex(searchIndex.Mappings.Fields)
if err != nil {
return diag.FromErr(err)
}

if err := d.Set("mappings_fields", searchIndexMappingFields); err != nil {
return diag.Errorf("error setting `mappings_fields` for for search index (%s): %s", d.Id(), err)
}
}
}

if len(searchIndex.Fields) > 0 {
fields, err := marshalSearchIndex(searchIndex.Fields)
if err != nil {
return diag.FromErr(err)
}

if err := d.Set("mappings_fields", searchIndexMappingFields); err != nil {
return diag.Errorf("error setting `mappings_fields` for for search index (%s): %s", d.Id(), err)
if err := d.Set("fields", fields); err != nil {
return diag.Errorf("error setting `fields` for for search index (%s): %s", d.Id(), err)
}
}

Expand Down Expand Up @@ -346,21 +396,42 @@ func resourceMongoDBAtlasSearchIndexCreate(ctx context.Context, d *schema.Resour
connV2 := meta.(*MongoDBClient).AtlasV2
projectID := d.Get("project_id").(string)
clusterName := d.Get("cluster_name").(string)
dynamic := d.Get("mappings_dynamic").(bool)
indexType := d.Get("type").(string)
tibulca marked this conversation as resolved.
Show resolved Hide resolved

searchIndexRequest := &admin.ClusterSearchIndex{
Type: stringPtr(indexType),
Analyzer: stringPtr(d.Get("analyzer").(string)),
Analyzers: unmarshalSearchIndexAnalyzersFields(d.Get("analyzers").(string)),
CollectionName: d.Get("collection_name").(string),
Database: d.Get("database").(string),
Mappings: &admin.ApiAtlasFTSMappings{
Dynamic: &dynamic,
Fields: unmarshalSearchIndexMappingFields(d.Get("mappings_fields").(string)),
},
Name: d.Get("name").(string),
SearchAnalyzer: stringPtr(d.Get("search_analyzer").(string)),
Status: stringPtr(d.Get("status").(string)),
Synonyms: expandSearchIndexSynonyms(d),
}

if indexType == vectorSearch {
fields, err := unmarshalSearchIndexFields(d.Get("fields").(string))
if err != nil {
return err
}
searchIndexRequest.Fields = fields
} else {
analyzers, err := unmarshalSearchIndexAnalyzersFields(d.Get("analyzers").(string))
if err != nil {
return err
}
searchIndexRequest.Analyzers = analyzers
mappingsFields, err := unmarshalSearchIndexMappingFields(d.Get("mappings_fields").(string))
if err != nil {
return err
}
dynamic := d.Get("mappings_dynamic").(bool)
searchIndexRequest.Mappings = &admin.ApiAtlasFTSMappings{
Dynamic: &dynamic,
Fields: mappingsFields,
}
}

dbSearchIndexRes, _, err := connV2.AtlasSearchApi.CreateAtlasSearchIndex(ctx, projectID, clusterName, searchIndexRequest).Execute()
if err != nil {
return diag.Errorf("error creating index: %s", err)
Expand Down Expand Up @@ -467,28 +538,38 @@ func validateSearchAnalyzersDiff(k, old, newStr string, d *schema.ResourceData)
return true
}

func unmarshalSearchIndexMappingFields(mappingString string) map[string]any {
func unmarshalSearchIndexMappingFields(mappingString string) (map[string]any, diag.Diagnostics) {
if mappingString == "" {
return nil
return nil, nil
}
var fields map[string]any
if err := json.Unmarshal([]byte(mappingString), &fields); err != nil {
log.Printf("[ERROR] cannot unmarshal search index mapping fields: %v", err)
return nil
return nil, diag.Errorf("cannot unmarshal search index attribute `mappings_fields` because it has an incorrect format")
}
return fields
return fields, nil
}

func unmarshalSearchIndexFields(fieldsStr string) ([]map[string]any, diag.Diagnostics) {
if fieldsStr == "" {
return nil, nil
}
var fields []map[string]any
if err := json.Unmarshal([]byte(fieldsStr), &fields); err != nil {
return nil, diag.Errorf("cannot unmarshal search index attribute `fields` because it has an incorrect format")
}

return fields, nil
}

func unmarshalSearchIndexAnalyzersFields(mappingString string) []admin.ApiAtlasFTSAnalyzers {
func unmarshalSearchIndexAnalyzersFields(mappingString string) ([]admin.ApiAtlasFTSAnalyzers, diag.Diagnostics) {
if mappingString == "" {
return nil
return nil, nil
}
var fields []admin.ApiAtlasFTSAnalyzers
if err := json.Unmarshal([]byte(mappingString), &fields); err != nil {
log.Printf("[ERROR] cannot unmarshal search index mapping fields: %v", err)
return nil
return nil, diag.Errorf("cannot unmarshal search index attribute `analyzers` because it has an incorrect format")
}
return fields
return fields, nil
}

func resourceSearchIndexRefreshFunc(ctx context.Context, clusterName, projectID, indexID string, connV2 *admin.APIClient) retry.StateRefreshFunc {
Expand Down
Loading
Loading