mirror of https://github.com/go-gitea/gitea
parent
265675a31c
commit
92c160d8e7
File diff suppressed because one or more lines are too long
@ -0,0 +1,183 @@ |
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package issues |
||||
|
||||
import ( |
||||
"context" |
||||
"strconv" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/meilisearch/meilisearch-go" |
||||
) |
||||
|
||||
var _ Indexer = &MeilisearchIndexer{} |
||||
|
||||
// MeilisearchIndexer implements Indexer interface
|
||||
type MeilisearchIndexer struct { |
||||
client *meilisearch.Client |
||||
indexerName string |
||||
available bool |
||||
availabilityCallback func(bool) |
||||
stopTimer chan struct{} |
||||
lock sync.RWMutex |
||||
} |
||||
|
||||
// MeilisearchIndexer creates a new meilisearch indexer
|
||||
func NewMeilisearchIndexer(url, apiKey, indexerName string) (*MeilisearchIndexer, error) { |
||||
client := meilisearch.NewClient(meilisearch.ClientConfig{ |
||||
Host: url, |
||||
APIKey: apiKey, |
||||
}) |
||||
|
||||
indexer := &MeilisearchIndexer{ |
||||
client: client, |
||||
indexerName: indexerName, |
||||
available: true, |
||||
stopTimer: make(chan struct{}), |
||||
} |
||||
|
||||
ticker := time.NewTicker(10 * time.Second) |
||||
go func() { |
||||
for { |
||||
select { |
||||
case <-ticker.C: |
||||
indexer.checkAvailability() |
||||
case <-indexer.stopTimer: |
||||
ticker.Stop() |
||||
return |
||||
} |
||||
} |
||||
}() |
||||
|
||||
return indexer, nil |
||||
} |
||||
|
||||
// Init will initialize the indexer
|
||||
func (b *MeilisearchIndexer) Init() (bool, error) { |
||||
_, err := b.client.GetIndex(b.indexerName) |
||||
if err == nil { |
||||
return true, nil |
||||
} |
||||
_, err = b.client.CreateIndex(&meilisearch.IndexConfig{ |
||||
Uid: b.indexerName, |
||||
PrimaryKey: "id", |
||||
}) |
||||
if err != nil { |
||||
return false, b.checkError(err) |
||||
} |
||||
|
||||
_, err = b.client.Index(b.indexerName).UpdateFilterableAttributes(&[]string{"repo_id"}) |
||||
return false, b.checkError(err) |
||||
} |
||||
|
||||
// SetAvailabilityChangeCallback sets callback that will be triggered when availability changes
|
||||
func (b *MeilisearchIndexer) SetAvailabilityChangeCallback(callback func(bool)) { |
||||
b.lock.Lock() |
||||
defer b.lock.Unlock() |
||||
b.availabilityCallback = callback |
||||
} |
||||
|
||||
// Ping checks if meilisearch is available
|
||||
func (b *MeilisearchIndexer) Ping() bool { |
||||
b.lock.RLock() |
||||
defer b.lock.RUnlock() |
||||
return b.available |
||||
} |
||||
|
||||
// Index will save the index data
|
||||
func (b *MeilisearchIndexer) Index(issues []*IndexerData) error { |
||||
if len(issues) == 0 { |
||||
return nil |
||||
} |
||||
for _, issue := range issues { |
||||
_, err := b.client.Index(b.indexerName).AddDocuments(issue) |
||||
if err != nil { |
||||
return b.checkError(err) |
||||
} |
||||
} |
||||
// TODO: bulk send index data
|
||||
return nil |
||||
} |
||||
|
||||
// Delete deletes indexes by ids
|
||||
func (b *MeilisearchIndexer) Delete(ids ...int64) error { |
||||
if len(ids) == 0 { |
||||
return nil |
||||
} |
||||
|
||||
for _, id := range ids { |
||||
_, err := b.client.Index(b.indexerName).DeleteDocument(strconv.FormatInt(id, 10)) |
||||
if err != nil { |
||||
return b.checkError(err) |
||||
} |
||||
} |
||||
// TODO: bulk send deletes
|
||||
return nil |
||||
} |
||||
|
||||
// Search searches for issues by given conditions.
|
||||
// Returns the matching issue IDs
|
||||
func (b *MeilisearchIndexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*SearchResult, error) { |
||||
filter := make([][]string, 0, len(repoIDs)) |
||||
for _, repoID := range repoIDs { |
||||
filter = append(filter, []string{"repo_id = " + strconv.FormatInt(repoID, 10)}) |
||||
} |
||||
searchRes, err := b.client.Index(b.indexerName).Search(keyword, &meilisearch.SearchRequest{ |
||||
Filter: filter, |
||||
Limit: int64(limit), |
||||
Offset: int64(start), |
||||
}) |
||||
if err != nil { |
||||
return nil, b.checkError(err) |
||||
} |
||||
|
||||
hits := make([]Match, 0, len(searchRes.Hits)) |
||||
for _, hit := range searchRes.Hits { |
||||
hits = append(hits, Match{ |
||||
ID: int64(hit.(map[string]interface{})["id"].(float64)), |
||||
}) |
||||
} |
||||
return &SearchResult{ |
||||
Total: searchRes.TotalHits, |
||||
Hits: hits, |
||||
}, nil |
||||
} |
||||
|
||||
// Close implements indexer
|
||||
func (b *MeilisearchIndexer) Close() { |
||||
select { |
||||
case <-b.stopTimer: |
||||
default: |
||||
close(b.stopTimer) |
||||
} |
||||
} |
||||
|
||||
func (b *MeilisearchIndexer) checkError(err error) error { |
||||
return err |
||||
} |
||||
|
||||
func (b *MeilisearchIndexer) checkAvailability() { |
||||
_, err := b.client.Health() |
||||
if err != nil { |
||||
b.setAvailability(false) |
||||
return |
||||
} |
||||
b.setAvailability(true) |
||||
} |
||||
|
||||
func (b *MeilisearchIndexer) setAvailability(available bool) { |
||||
b.lock.Lock() |
||||
defer b.lock.Unlock() |
||||
|
||||
if b.available == available { |
||||
return |
||||
} |
||||
|
||||
b.available = available |
||||
if b.availabilityCallback != nil { |
||||
// Call the callback from within the lock to ensure that the ordering remains correct
|
||||
b.availabilityCallback(b.available) |
||||
} |
||||
} |
Loading…
Reference in new issue