mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
- Add Bleve client for keyword search functionality - Integrate Bleve service into application builder - Add BleveIndexPath configuration - Update domain mappings for proper indexing - Add comprehensive documentation and tests
166 lines
4.5 KiB
Go
166 lines
4.5 KiB
Go
package bleve
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"tercul/internal/domain"
|
|
|
|
blevelib "github.com/blevesearch/bleve/v2"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type BleveClient struct {
|
|
index blevelib.Index
|
|
}
|
|
|
|
// NewBleveClient initializes or opens a Bleve index at the given path.
|
|
func NewBleveClient(indexPath string) (*BleveClient, error) {
|
|
var index blevelib.Index
|
|
var err error
|
|
|
|
if _, err = os.Stat(indexPath); os.IsNotExist(err) {
|
|
// Create a new index if it doesn't exist
|
|
indexMapping := blevelib.NewIndexMapping()
|
|
index, err = blevelib.New(indexPath, indexMapping)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Println("Created a new Bleve index at:", indexPath)
|
|
} else {
|
|
// Open an existing index
|
|
index, err = blevelib.Open(indexPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Println("Opened an existing Bleve index at:", indexPath)
|
|
}
|
|
|
|
return &BleveClient{index: index}, nil
|
|
}
|
|
|
|
// AddTranslation indexes a single translation into the Bleve index.
|
|
func (bc *BleveClient) AddTranslation(translation domain.Translation) error {
|
|
// Create a structured document containing all relevant translation fields
|
|
document := map[string]interface{}{
|
|
"id": translation.ID,
|
|
"title": translation.Title,
|
|
"content": translation.Content,
|
|
"description": translation.Description,
|
|
"language": translation.Language,
|
|
"status": translation.Status,
|
|
"translatable_id": translation.TranslatableID,
|
|
"translatable_type": translation.TranslatableType,
|
|
"translator_id": func() uint {
|
|
if translation.TranslatorID != nil {
|
|
return *translation.TranslatorID
|
|
}
|
|
return 0
|
|
}(),
|
|
}
|
|
|
|
// Use the translation's ID as the unique document ID
|
|
docID := fmt.Sprintf("%d", translation.ID)
|
|
return bc.index.Index(docID, document)
|
|
}
|
|
|
|
// AddTranslations indexes all translations from the database into the Bleve index.
|
|
func (bc *BleveClient) AddTranslations(db *gorm.DB) error {
|
|
const batchSize = 50000
|
|
offset := 0
|
|
|
|
for {
|
|
// Fetch translations in batches
|
|
var translations []domain.Translation
|
|
err := db.Offset(offset).Limit(batchSize).Find(&translations).Error
|
|
if err != nil {
|
|
log.Printf("Error fetching translations from the database: %v", err)
|
|
return err
|
|
}
|
|
|
|
// Break if no more translations to process
|
|
if len(translations) == 0 {
|
|
break
|
|
}
|
|
|
|
// Create a Bleve batch for better indexing performance
|
|
batch := bc.index.NewBatch()
|
|
for _, translation := range translations {
|
|
// Create a structured document for each translation
|
|
document := map[string]interface{}{
|
|
"id": translation.ID,
|
|
"title": translation.Title,
|
|
"content": translation.Content,
|
|
"description": translation.Description,
|
|
"language": translation.Language,
|
|
"status": translation.Status,
|
|
"translatable_id": translation.TranslatableID,
|
|
"translatable_type": translation.TranslatableType,
|
|
"translator_id": func() uint {
|
|
if translation.TranslatorID != nil {
|
|
return *translation.TranslatorID
|
|
}
|
|
return 0
|
|
}(),
|
|
}
|
|
|
|
docID := fmt.Sprintf("%d", translation.ID)
|
|
err = batch.Index(docID, document)
|
|
if err != nil {
|
|
log.Printf("Error indexing translation ID %s: %v", docID, err)
|
|
}
|
|
}
|
|
|
|
// Commit the batch to the index
|
|
err = bc.index.Batch(batch)
|
|
if err != nil {
|
|
log.Printf("Error committing batch to the Bleve index: %v", err)
|
|
return err
|
|
}
|
|
|
|
log.Printf("Indexed %d translations into Bleve.", len(translations))
|
|
offset += batchSize
|
|
}
|
|
|
|
log.Println("All translations have been indexed into Bleve.")
|
|
return nil
|
|
}
|
|
|
|
// Search performs a search with multiple filters and a full-text query.
|
|
func (bc *BleveClient) Search(queryString string, filters map[string]string, size int) (*blevelib.SearchResult, error) {
|
|
// Create the main query for full-text search
|
|
mainQuery := blevelib.NewMatchQuery(queryString)
|
|
mainQuery.SetFuzziness(2)
|
|
|
|
// Create a boolean query
|
|
booleanQuery := blevelib.NewBooleanQuery()
|
|
|
|
// Add the main query to the "must" clause
|
|
booleanQuery.AddMust(mainQuery)
|
|
|
|
// Add filter queries to the "must" clause
|
|
for field, value := range filters {
|
|
termQuery := blevelib.NewTermQuery(value)
|
|
termQuery.SetField(field)
|
|
booleanQuery.AddMust(termQuery)
|
|
}
|
|
|
|
// Build the search request
|
|
searchRequest := blevelib.NewSearchRequest(booleanQuery)
|
|
searchRequest.Size = size
|
|
|
|
// Execute the search
|
|
results, err := bc.index.Search(searchRequest)
|
|
if err != nil {
|
|
log.Printf("Search failed: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
// Close closes the Bleve index.
|
|
func (bc *BleveClient) Close() error {
|
|
return bc.index.Close()
|
|
}
|