Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7acbff6
commit 32f9aef
Showing
4 changed files
with
222 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package mongorepository | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
|
||
"go.mongodb.org/mongo-driver/bson" | ||
"go.mongodb.org/mongo-driver/mongo" | ||
"go.mongodb.org/mongo-driver/mongo/options" | ||
) | ||
|
||
// CreateFullTextIndex creates a full-text index in the MongoDB collection based on the specified key and options. | ||
// It takes a context.Context as the first argument, the key for the index as the second argument, | ||
// and optional IndexOption(s) as the third argument(s). | ||
// The function returns an error if the index creation fails. | ||
func (r *mongoRepository[T]) CreateFullTextIndex(ctx context.Context, keys map[string]int32, lang string) error { | ||
// Build the index keys and weights | ||
idxKeys := make(bson.D, 0, len(keys)) | ||
weights := make(bson.D, 0, len(keys)) | ||
for k, w := range keys { | ||
idxKeys = append(idxKeys, bson.E{Key: k, Value: "text"}) | ||
weights = append(weights, bson.E{Key: k, Value: w}) | ||
} | ||
if lang == "" { | ||
lang = "english" | ||
} | ||
|
||
// Set index options | ||
idxOpt := options.Index() | ||
idxOpt.SetWeights(weights) | ||
idxOpt.SetDefaultLanguage(lang) | ||
idxOpt.SetName(fmt.Sprintf("%s_fts_index", lang)) | ||
idxOpt.SetSparse(true) | ||
|
||
// Create the index | ||
indexModel := mongo.IndexModel{ | ||
Keys: idxKeys, | ||
Options: idxOpt, | ||
} | ||
|
||
// Create the index | ||
if _, err := r.collection.Indexes().CreateOne(ctx, indexModel); err != nil { | ||
return errors.Join(ErrFailedToCreateIndex, err) | ||
} | ||
return nil | ||
} | ||
|
||
// Search finds documents in the collection based on the provided search term. | ||
// It allows skipping a certain number of documents and limiting the number of documents to be returned. | ||
// The function returns a slice of documents of type T and an error. | ||
func (r *mongoRepository[T]) Search(ctx context.Context, skip, limit int64, searchTerm string) ([]T, error) { | ||
filter := bson.M{"$text": bson.M{"$search": searchTerm}} | ||
if limit == 0 { | ||
limit = 10 | ||
} | ||
// Set the find options | ||
findOptions := options.Find(). | ||
SetSkip(skip). | ||
SetLimit(limit). | ||
SetProjection(bson.M{"score": bson.M{"$meta": "textScore"}}). | ||
SetSort(bson.D{{Key: "score", Value: bson.M{"$meta": "textScore"}}}) | ||
// Find documents | ||
cursor, err := r.collection.Find(ctx, filter, findOptions) | ||
if err != nil { | ||
if errors.Is(err, mongo.ErrNoDocuments) { | ||
return nil, errors.Join(ErrFailedToFindManyByFilter, ErrNotFound, err) | ||
} | ||
return nil, errors.Join(ErrFailedToFindManyByFilter, err) | ||
} | ||
defer cursor.Close(ctx) | ||
|
||
var results []T | ||
for cursor.Next(ctx) { | ||
var element T | ||
if err := cursor.Decode(&element); err != nil { | ||
return nil, errors.Join(ErrFailedToFindManyByFilter, err) | ||
} | ||
results = append(results, element) | ||
} | ||
|
||
if err := cursor.Err(); err != nil { | ||
return nil, errors.Join(ErrFailedToFindManyByFilter, err) | ||
} | ||
if len(results) == 0 { | ||
return nil, errors.Join(ErrFailedToFindManyByFilter, ErrNotFound) | ||
} | ||
|
||
return results, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package mongorepository_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
mongorepository "github.com/dmitrymomot/mongo-repository" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.mongodb.org/mongo-driver/bson/primitive" | ||
) | ||
|
||
func TestFullTextSearch(t *testing.T) { | ||
type User struct { | ||
ID primitive.ObjectID `bson:"_id,omitempty"` | ||
Name string `bson:"name"` | ||
Bio string `bson:"bio,omitempty"` | ||
Tags []string `bson:"tags,omitempty"` | ||
} | ||
|
||
db := setupMongoDB(t) | ||
repo := mongorepository.NewMongoRepository[User](db, "users") | ||
|
||
// Create unique index for email field | ||
require.NoError(t, repo.CreateFullTextIndex( | ||
context.Background(), | ||
map[string]int32{ | ||
"name": 10, | ||
"bio": 5, | ||
"tags": 1, | ||
}, | ||
"english", | ||
)) | ||
|
||
// Create test users | ||
users := []User{ | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Test John Doe", | ||
Bio: "Software Engineer", | ||
Tags: []string{"go", "mongodb", "developer", "test"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Jane Smith", | ||
Bio: "Data Scientist", | ||
Tags: []string{"python", "machine learning", "data analysis"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Kayla TestJohnson", | ||
Bio: "Frontend Developer", | ||
Tags: []string{"javascript", "react", "web development"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Alex Brown", | ||
Bio: "Backend Test Developer", | ||
Tags: []string{"golang", "spring", "web development"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Emily Davis", | ||
Bio: "UI/UX Designer Test", | ||
Tags: []string{"design", "user experience", "prototyping"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Michael Wilson", | ||
Bio: "Golang Engineer", | ||
Tags: []string{"test", "docker", "kubernetes", "cloud", "microservices", "go-test"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Clark Thompson", | ||
Bio: "Product Manager", | ||
Tags: []string{"agile", "scrum", "product development", "testify"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "David Lee", | ||
Bio: "Web Developer", | ||
Tags: []string{"javascript", "node.js", "react", "web development"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Jessica Martinez", | ||
Bio: "Mobile App Developer", | ||
Tags: []string{"android", "java", "kotlin"}, | ||
}, | ||
{ | ||
ID: primitive.NewObjectID(), | ||
Name: "Ryan Clark", | ||
Bio: "Data Engineer", | ||
Tags: []string{"big data", "hadoop", "test"}, | ||
}, | ||
} | ||
for _, user := range users { | ||
// Test Create | ||
id, err := repo.Create(context.Background(), user) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, id) | ||
} | ||
|
||
// Test full text search | ||
t.Run("Search", func(t *testing.T) { | ||
users, err := repo.Search(context.Background(), 0, 10, "test") | ||
require.NoError(t, err) | ||
assert.Len(t, users, 5) | ||
assert.Equal(t, "Test John Doe", users[0].Name) | ||
assert.Equal(t, "Alex Brown", users[1].Name) | ||
assert.Equal(t, "Emily Davis", users[2].Name) | ||
assert.Equal(t, "Michael Wilson", users[3].Name) | ||
assert.Equal(t, "Ryan Clark", users[4].Name) | ||
}) | ||
|
||
// Test full text search with exclusion | ||
t.Run("SearchExclude", func(t *testing.T) { | ||
users, err := repo.Search(context.Background(), 0, 10, "web -test") | ||
require.NoError(t, err) | ||
assert.Len(t, users, 2) | ||
assert.Equal(t, "David Lee", users[0].Name) | ||
assert.Equal(t, "Kayla TestJohnson", users[1].Name) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters