...

Source file src/go.mongodb.org/mongo-driver/mongo/integration/search_index_prose_test.go

Documentation: go.mongodb.org/mongo-driver/mongo/integration

     1  // Copyright (C) MongoDB, Inc. 2023-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package integration
     8  
     9  import (
    10  	"context"
    11  	"os"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"go.mongodb.org/mongo-driver/bson"
    17  	"go.mongodb.org/mongo-driver/internal/assert"
    18  	"go.mongodb.org/mongo-driver/internal/require"
    19  	"go.mongodb.org/mongo-driver/internal/uuid"
    20  	"go.mongodb.org/mongo-driver/mongo"
    21  	"go.mongodb.org/mongo-driver/mongo/integration/mtest"
    22  	"go.mongodb.org/mongo-driver/mongo/options"
    23  )
    24  
    25  func TestSearchIndexProse(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	const timeout = 5 * time.Minute
    29  
    30  	uri := os.Getenv("TEST_INDEX_URI")
    31  	if uri == "" {
    32  		t.Skip("skipping")
    33  	}
    34  
    35  	opts := options.Client().ApplyURI(uri).SetTimeout(timeout)
    36  	mt := mtest.New(t, mtest.NewOptions().ClientOptions(opts).MinServerVersion("7.0").Topologies(mtest.ReplicaSet))
    37  
    38  	mt.Run("case 1: Driver can successfully create and list search indexes", func(mt *mtest.T) {
    39  		ctx := context.Background()
    40  
    41  		_, err := mt.Coll.InsertOne(ctx, bson.D{})
    42  		require.NoError(mt, err, "failed to insert")
    43  
    44  		view := mt.Coll.SearchIndexes()
    45  
    46  		definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
    47  		searchName := "test-search-index"
    48  		opts := options.SearchIndexes().SetName(searchName)
    49  		index, err := view.CreateOne(ctx, mongo.SearchIndexModel{
    50  			Definition: definition,
    51  			Options:    opts,
    52  		})
    53  		require.NoError(mt, err, "failed to create index")
    54  		require.Equal(mt, searchName, index, "unmatched name")
    55  
    56  		var doc bson.Raw
    57  		for doc == nil {
    58  			cursor, err := view.List(ctx, opts)
    59  			require.NoError(mt, err, "failed to list")
    60  
    61  			if !cursor.Next(ctx) {
    62  				break
    63  			}
    64  			if cursor.Current.Lookup("queryable").Boolean() {
    65  				doc = cursor.Current
    66  			} else {
    67  				t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
    68  				time.Sleep(5 * time.Second)
    69  			}
    70  		}
    71  		require.NotNil(mt, doc, "got empty document")
    72  		assert.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
    73  		expected, err := bson.Marshal(definition)
    74  		require.NoError(mt, err, "failed to marshal definition")
    75  		actual := doc.Lookup("latestDefinition").Value
    76  		assert.Equal(mt, expected, actual, "unmatched definition")
    77  	})
    78  
    79  	mt.Run("case 2: Driver can successfully create multiple indexes in batch", func(mt *mtest.T) {
    80  		ctx := context.Background()
    81  
    82  		_, err := mt.Coll.InsertOne(ctx, bson.D{})
    83  		require.NoError(mt, err, "failed to insert")
    84  
    85  		view := mt.Coll.SearchIndexes()
    86  
    87  		definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
    88  		models := []mongo.SearchIndexModel{
    89  			{
    90  				Definition: definition,
    91  				Options:    options.SearchIndexes().SetName("test-search-index-1"),
    92  			},
    93  			{
    94  				Definition: definition,
    95  				Options:    options.SearchIndexes().SetName("test-search-index-2"),
    96  			},
    97  		}
    98  		indexes, err := view.CreateMany(ctx, models)
    99  		require.NoError(mt, err, "failed to create index")
   100  		require.Equal(mt, len(indexes), 2, "expected 2 indexes")
   101  		for _, model := range models {
   102  			require.Contains(mt, indexes, *model.Options.Name)
   103  		}
   104  
   105  		getDocument := func(opts *options.SearchIndexesOptions) bson.Raw {
   106  			for {
   107  				cursor, err := view.List(ctx, opts)
   108  				require.NoError(mt, err, "failed to list")
   109  
   110  				if !cursor.Next(ctx) {
   111  					return nil
   112  				}
   113  				if cursor.Current.Lookup("queryable").Boolean() {
   114  					return cursor.Current
   115  				}
   116  				t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
   117  				time.Sleep(5 * time.Second)
   118  			}
   119  		}
   120  
   121  		var wg sync.WaitGroup
   122  		wg.Add(len(models))
   123  		for i := range models {
   124  			go func(opts *options.SearchIndexesOptions) {
   125  				defer wg.Done()
   126  
   127  				doc := getDocument(opts)
   128  				require.NotNil(mt, doc, "got empty document")
   129  				assert.Equal(mt, *opts.Name, doc.Lookup("name").StringValue(), "unmatched name")
   130  				expected, err := bson.Marshal(definition)
   131  				require.NoError(mt, err, "failed to marshal definition")
   132  				actual := doc.Lookup("latestDefinition").Value
   133  				assert.Equal(mt, expected, actual, "unmatched definition")
   134  			}(models[i].Options)
   135  		}
   136  		wg.Wait()
   137  	})
   138  
   139  	mt.Run("case 3: Driver can successfully drop search indexes", func(mt *mtest.T) {
   140  		ctx := context.Background()
   141  
   142  		_, err := mt.Coll.InsertOne(ctx, bson.D{})
   143  		require.NoError(mt, err, "failed to insert")
   144  
   145  		view := mt.Coll.SearchIndexes()
   146  
   147  		definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
   148  		searchName := "test-search-index"
   149  		opts := options.SearchIndexes().SetName(searchName)
   150  		index, err := view.CreateOne(ctx, mongo.SearchIndexModel{
   151  			Definition: definition,
   152  			Options:    opts,
   153  		})
   154  		require.NoError(mt, err, "failed to create index")
   155  		require.Equal(mt, searchName, index, "unmatched name")
   156  
   157  		var doc bson.Raw
   158  		for doc == nil {
   159  			cursor, err := view.List(ctx, opts)
   160  			require.NoError(mt, err, "failed to list")
   161  
   162  			if !cursor.Next(ctx) {
   163  				break
   164  			}
   165  			if cursor.Current.Lookup("queryable").Boolean() {
   166  				doc = cursor.Current
   167  			} else {
   168  				t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
   169  				time.Sleep(5 * time.Second)
   170  			}
   171  		}
   172  		require.NotNil(mt, doc, "got empty document")
   173  		require.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
   174  
   175  		err = view.DropOne(ctx, searchName)
   176  		require.NoError(mt, err, "failed to drop index")
   177  		for {
   178  			cursor, err := view.List(ctx, opts)
   179  			require.NoError(mt, err, "failed to list")
   180  
   181  			if !cursor.Next(ctx) {
   182  				break
   183  			}
   184  			t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
   185  			time.Sleep(5 * time.Second)
   186  		}
   187  	})
   188  
   189  	mt.Run("case 4: Driver can update a search index", func(mt *mtest.T) {
   190  		ctx := context.Background()
   191  
   192  		_, err := mt.Coll.InsertOne(ctx, bson.D{})
   193  		require.NoError(mt, err, "failed to insert")
   194  
   195  		view := mt.Coll.SearchIndexes()
   196  
   197  		definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
   198  		searchName := "test-search-index"
   199  		opts := options.SearchIndexes().SetName(searchName)
   200  		index, err := view.CreateOne(ctx, mongo.SearchIndexModel{
   201  			Definition: definition,
   202  			Options:    opts,
   203  		})
   204  		require.NoError(mt, err, "failed to create index")
   205  		require.Equal(mt, searchName, index, "unmatched name")
   206  
   207  		getDocument := func() bson.Raw {
   208  			for {
   209  				cursor, err := view.List(ctx, opts)
   210  				require.NoError(mt, err, "failed to list")
   211  
   212  				if !cursor.Next(ctx) {
   213  					return nil
   214  				}
   215  				if cursor.Current.Lookup("queryable").Boolean() {
   216  					return cursor.Current
   217  				}
   218  				t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
   219  				time.Sleep(5 * time.Second)
   220  			}
   221  		}
   222  
   223  		doc := getDocument()
   224  		require.NotNil(mt, doc, "got empty document")
   225  		require.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
   226  
   227  		definition = bson.D{{"mappings", bson.D{{"dynamic", true}}}}
   228  		err = view.UpdateOne(ctx, searchName, definition)
   229  		require.NoError(mt, err, "failed to drop index")
   230  		doc = getDocument()
   231  		require.NotNil(mt, doc, "got empty document")
   232  		assert.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
   233  		assert.Equal(mt, "READY", doc.Lookup("status").StringValue(), "unexpected status")
   234  		expected, err := bson.Marshal(definition)
   235  		require.NoError(mt, err, "failed to marshal definition")
   236  		actual := doc.Lookup("latestDefinition").Value
   237  		assert.Equal(mt, expected, actual, "unmatched definition")
   238  	})
   239  
   240  	mt.Run("case 5: dropSearchIndex suppresses namespace not found errors", func(mt *mtest.T) {
   241  		ctx := context.Background()
   242  
   243  		id, err := uuid.New()
   244  		require.NoError(mt, err)
   245  
   246  		collection := mt.CreateCollection(mtest.Collection{
   247  			Name: id.String(),
   248  		}, false)
   249  
   250  		err = collection.SearchIndexes().DropOne(ctx, "foo")
   251  		require.NoError(mt, err)
   252  	})
   253  }
   254  

View as plain text