初始提交: Gitea 项目代码
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meilisearch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Filter represents a filter for meilisearch queries.
|
||||
// It's just a simple wrapper around a string.
|
||||
// DO NOT assume that it is a complete implementation.
|
||||
type Filter interface {
|
||||
Statement() string
|
||||
}
|
||||
|
||||
type FilterAnd struct {
|
||||
filters []Filter
|
||||
}
|
||||
|
||||
func (f *FilterAnd) Statement() string {
|
||||
var statements []string
|
||||
for _, filter := range f.filters {
|
||||
if s := filter.Statement(); s != "" {
|
||||
statements = append(statements, fmt.Sprintf("(%s)", s))
|
||||
}
|
||||
}
|
||||
return strings.Join(statements, " AND ")
|
||||
}
|
||||
|
||||
func (f *FilterAnd) And(filter Filter) *FilterAnd {
|
||||
f.filters = append(f.filters, filter)
|
||||
return f
|
||||
}
|
||||
|
||||
type FilterOr struct {
|
||||
filters []Filter
|
||||
}
|
||||
|
||||
func (f *FilterOr) Statement() string {
|
||||
var statements []string
|
||||
for _, filter := range f.filters {
|
||||
if s := filter.Statement(); s != "" {
|
||||
statements = append(statements, fmt.Sprintf("(%s)", s))
|
||||
}
|
||||
}
|
||||
return strings.Join(statements, " OR ")
|
||||
}
|
||||
|
||||
func (f *FilterOr) Or(filter Filter) *FilterOr {
|
||||
f.filters = append(f.filters, filter)
|
||||
return f
|
||||
}
|
||||
|
||||
type FilterIn string
|
||||
|
||||
// NewFilterIn creates a new FilterIn.
|
||||
// It supports int64 only, to avoid extra works to handle strings with special characters.
|
||||
func NewFilterIn[T int64](field string, values ...T) FilterIn {
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
}
|
||||
vs := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
vs[i] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return FilterIn(fmt.Sprintf("%s IN [%v]", field, strings.Join(vs, ", ")))
|
||||
}
|
||||
|
||||
func (f FilterIn) Statement() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
type FilterEq string
|
||||
|
||||
// NewFilterEq creates a new FilterEq.
|
||||
// It supports int64 and bool only, to avoid extra works to handle strings with special characters.
|
||||
func NewFilterEq[T bool | int64](field string, value T) FilterEq {
|
||||
return FilterEq(fmt.Sprintf("%s = %v", field, value))
|
||||
}
|
||||
|
||||
func (f FilterEq) Statement() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
type FilterNot string
|
||||
|
||||
func NewFilterNot(filter Filter) FilterNot {
|
||||
return FilterNot(fmt.Sprintf("NOT (%s)", filter.Statement()))
|
||||
}
|
||||
|
||||
func (f FilterNot) Statement() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
type FilterGte string
|
||||
|
||||
// NewFilterGte creates a new FilterGte.
|
||||
// It supports int64 only, to avoid extra works to handle strings with special characters.
|
||||
func NewFilterGte[T int64](field string, value T) FilterGte {
|
||||
return FilterGte(fmt.Sprintf("%s >= %v", field, value))
|
||||
}
|
||||
|
||||
func (f FilterGte) Statement() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
type FilterLte string
|
||||
|
||||
// NewFilterLte creates a new FilterLte.
|
||||
// It supports int64 only, to avoid extra works to handle strings with special characters.
|
||||
func NewFilterLte[T int64](field string, value T) FilterLte {
|
||||
return FilterLte(fmt.Sprintf("%s <= %v", field, value))
|
||||
}
|
||||
|
||||
func (f FilterLte) Statement() string {
|
||||
return string(f)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meilisearch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
)
|
||||
|
||||
// Indexer represents a basic meilisearch indexer implementation
|
||||
type Indexer struct {
|
||||
Client meilisearch.ServiceManager
|
||||
|
||||
url, apiKey string
|
||||
indexName string
|
||||
version int
|
||||
settings *meilisearch.Settings
|
||||
}
|
||||
|
||||
func NewIndexer(url, apiKey, indexName string, version int, settings *meilisearch.Settings) *Indexer {
|
||||
return &Indexer{
|
||||
url: url,
|
||||
apiKey: apiKey,
|
||||
indexName: indexName,
|
||||
version: version,
|
||||
settings: settings,
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the indexer
|
||||
func (i *Indexer) Init(_ context.Context) (bool, error) {
|
||||
if i == nil {
|
||||
return false, errors.New("cannot init nil indexer")
|
||||
}
|
||||
|
||||
if i.Client != nil {
|
||||
return false, errors.New("indexer is already initialized")
|
||||
}
|
||||
|
||||
i.Client = meilisearch.New(i.url, meilisearch.WithAPIKey(i.apiKey))
|
||||
_, err := i.Client.GetIndex(i.VersionedIndexName())
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
_, err = i.Client.CreateIndex(&meilisearch.IndexConfig{
|
||||
Uid: i.VersionedIndexName(),
|
||||
PrimaryKey: "id",
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
i.checkOldIndexes()
|
||||
|
||||
_, err = i.Client.Index(i.VersionedIndexName()).UpdateSettings(i.settings)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Ping checks if the indexer is available
|
||||
func (i *Indexer) Ping(ctx context.Context) error {
|
||||
if i == nil {
|
||||
return errors.New("cannot ping nil indexer")
|
||||
}
|
||||
if i.Client == nil {
|
||||
return errors.New("indexer is not initialized")
|
||||
}
|
||||
resp, err := i.Client.Health()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Status != "available" {
|
||||
// See https://docs.meilisearch.com/reference/api/health.html#status
|
||||
return fmt.Errorf("status of meilisearch is not available: %s", resp.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the indexer
|
||||
func (i *Indexer) Close() {
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
i.Client = nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meilisearch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
)
|
||||
|
||||
// VersionedIndexName returns the full index name with version
|
||||
func (i *Indexer) VersionedIndexName() string {
|
||||
return versionedIndexName(i.indexName, i.version)
|
||||
}
|
||||
|
||||
func versionedIndexName(indexName string, version int) string {
|
||||
if version == 0 {
|
||||
// Old index name without version
|
||||
return indexName
|
||||
}
|
||||
|
||||
// The format of the index name is <index_name>_v<version>, not <index_name>.v<version> like elasticsearch.
|
||||
// Because meilisearch does not support "." in index name, it should contain only alphanumeric characters, hyphens (-) and underscores (_).
|
||||
// See https://www.meilisearch.com/docs/learn/core_concepts/indexes#index-uid
|
||||
|
||||
return fmt.Sprintf("%s_v%d", indexName, version)
|
||||
}
|
||||
|
||||
func (i *Indexer) checkOldIndexes() {
|
||||
for v := 0; v < i.version; v++ {
|
||||
indexName := versionedIndexName(i.indexName, v)
|
||||
_, err := i.Client.GetIndex(indexName)
|
||||
if err == nil {
|
||||
log.Warn("Found older meilisearch index named %q, Gitea will keep the old NOT DELETED. You can delete the old version after the upgrade succeed.", indexName)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user