From d7390053b969d7181aff1ced9c69b032b95e222e Mon Sep 17 00:00:00 2001 From: Damir Mukimov Date: Sun, 30 Nov 2025 03:15:35 +0100 Subject: [PATCH] feat: Apply Jules AI changes - Search service implementation and refactoring - Implement full-text search service with Weaviate integration - Remove Bleve search implementation - Add GraphQL schema files for search, work, author, and translation - Refactor search domain interfaces - Update Weaviate wrapper with integration tests - Clean up unused search client files --- cmd/api/main.go | 3 - go.mod | 111 +++--- go.sum | 261 +++++++------ internal/adapters/graphql/schema.resolvers.go | 75 +--- .../adapters/graphql/schema/author.graphqls | 21 + .../adapters/graphql/schema/search.graphqls | 26 ++ .../graphql/schema/translation.graphqls | 25 ++ .../adapters/graphql/schema/work.graphqls | 23 ++ .../graphql/work_resolvers_unit_test.go | 11 +- internal/app/search/bleve_service.go | 95 ----- internal/app/search/service.go | 11 +- internal/app/search/service_test.go | 9 + internal/domain/entities.go | 35 +- internal/domain/search/client.go | 11 - internal/domain/search/search.go | 85 ++++ internal/platform/search/bleve_client.go | 27 -- internal/platform/search/weaviate_client.go | 38 -- internal/platform/search/weaviate_wrapper.go | 362 +++++++++++++++++- .../weaviate_wrapper_integration_test.go | 330 ++++++++++++++++ internal/testutil/integration_test_utils.go | 9 +- pkg/search/bleve/bleveclient.go | 165 -------- pkg/search/bleve/bleveclient_test.go | 95 ----- 22 files changed, 1130 insertions(+), 698 deletions(-) create mode 100644 internal/adapters/graphql/schema/author.graphqls create mode 100644 internal/adapters/graphql/schema/search.graphqls create mode 100644 internal/adapters/graphql/schema/translation.graphqls create mode 100644 internal/adapters/graphql/schema/work.graphqls delete mode 100644 internal/app/search/bleve_service.go delete mode 100644 internal/domain/search/client.go create mode 100644 internal/domain/search/search.go delete mode 100644 internal/platform/search/bleve_client.go delete mode 100644 internal/platform/search/weaviate_client.go create mode 100644 internal/platform/search/weaviate_wrapper_integration_test.go delete mode 100644 pkg/search/bleve/bleveclient.go delete mode 100644 pkg/search/bleve/bleveclient_test.go diff --git a/cmd/api/main.go b/cmd/api/main.go index c56eb0b..b6ab7db 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -70,9 +70,6 @@ func main() { log.Fatalf("cannot load config: %v", err) } - // Initialize Bleve search - search.InitBleve() - // Initialize logger app_log.Init("tercul-api", cfg.Environment) obsLogger := observability.NewLogger("tercul-api", cfg.Environment) diff --git a/go.mod b/go.mod index ffcc62f..c556d19 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,12 @@ module tercul -go 1.24.0 - -toolchain go1.24.2 +go 1.24.10 require ( github.com/99designs/gqlgen v0.17.72 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 - github.com/blevesearch/bleve/v2 v2.5.5 + github.com/go-openapi/strfmt v0.25.0 github.com/go-playground/validator/v10 v10.28.0 github.com/go-redis/redismock/v9 v9.2.0 github.com/golang-jwt/jwt/v5 v5.3.0 @@ -23,68 +21,65 @@ require ( github.com/rs/zerolog v1.34.0 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 + github.com/testcontainers/testcontainers-go v0.40.0 github.com/vektah/gqlparser/v2 v2.5.26 - github.com/weaviate/weaviate v1.30.2 - github.com/weaviate/weaviate-go-client/v5 v5.1.0 + github.com/weaviate/weaviate v1.33.6 + github.com/weaviate/weaviate-go-client/v5 v5.6.0 go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 go.opentelemetry.io/otel/sdk v1.38.0 go.opentelemetry.io/otel/trace v1.38.0 - golang.org/x/crypto v0.42.0 + golang.org/x/crypto v0.43.0 gorm.io/driver/postgres v1.5.11 gorm.io/driver/sqlite v1.6.0 gorm.io/gorm v1.30.0 ) require ( + dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/ClickHouse/ch-go v0.67.0 // indirect github.com/ClickHouse/clickhouse-go/v2 v2.40.1 // indirect - github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.22.0 // indirect - github.com/blevesearch/bleve_index_api v1.2.11 // indirect - github.com/blevesearch/geo v0.2.4 // indirect - github.com/blevesearch/go-faiss v1.0.26 // indirect - github.com/blevesearch/go-porterstemmer v1.0.3 // indirect - github.com/blevesearch/gtreap v0.1.1 // indirect - github.com/blevesearch/mmap-go v1.0.4 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect - github.com/blevesearch/segment v0.9.1 // indirect - github.com/blevesearch/snowballstem v0.9.0 // indirect - github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/blevesearch/vellum v1.1.0 // indirect - github.com/blevesearch/zapx/v11 v11.4.2 // indirect - github.com/blevesearch/zapx/v12 v12.4.2 // indirect - github.com/blevesearch/zapx/v13 v13.4.2 // indirect - github.com/blevesearch/zapx/v14 v14.4.2 // indirect - github.com/blevesearch/zapx/v15 v15.4.2 // indirect - github.com/blevesearch/zapx/v16 v16.2.7 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coder/websocket v1.8.12 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v28.5.1+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ebitengine/purego v0.8.4 // indirect github.com/elastic/go-sysinfo v1.15.4 // indirect github.com/elastic/go-windows v1.0.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/go-faster/city v1.0.1 // indirect github.com/go-faster/errors v0.7.1 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/errors v0.22.4 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/runtime v0.24.2 // indirect github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -94,7 +89,6 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect - github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect @@ -105,9 +99,10 @@ require ( github.com/joho/godotenv v1.5.1 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -115,21 +110,28 @@ require ( github.com/mfridman/interpolate v0.0.2 // indirect github.com/mfridman/xflag v0.1.0 // indirect github.com/microsoft/go-mssqldb v1.9.2 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mschoch/smat v0.2.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.1.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/paulmach/orb v0.11.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron/v3 v3.0.1 // indirect @@ -137,40 +139,45 @@ require ( github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect + github.com/shirou/gopsutil/v4 v4.25.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/sosodev/duration v1.3.1 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.9.0 // indirect github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d // indirect github.com/urfave/cli/v2 v2.27.6 // indirect github.com/vertica/vertica-sql-go v1.3.3 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77 // indirect github.com/ydb-platform/ydb-go-sdk/v3 v3.108.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/ziutek/mymysql v1.5.4 // indirect - go.etcd.io/bbolt v1.4.0 // indirect - go.mongodb.org/mongo-driver v1.14.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.mongodb.org/mongo-driver v1.17.6 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.29.0 // indirect - golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.36.0 // indirect - gonum.org/v1/gonum v0.15.1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect - google.golang.org/grpc v1.69.4 // indirect - google.golang.org/protobuf v1.36.6 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.37.0 // indirect + gonum.org/v1/gonum v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect + google.golang.org/grpc v1.77.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.1 // indirect diff --git a/go.sum b/go.sum index 43c1220..fa96c28 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,25 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/gqlgen v0.17.72 h1:2JDAuutIYtAN26BAtigfLZFnTN53fpYbIENL8bVgAKY= github.com/99designs/gqlgen v0.17.72/go.mod h1:BoL4C3j9W2f95JeWMrSArdDNGWmZB9MOS2EMHJDZmUc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 h1:ci6Yd6nysBRLEodoziB6ah1+YOzZbZk+NYneoA6q+6E= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -23,12 +29,12 @@ github.com/ClickHouse/clickhouse-go/v2 v2.40.1 h1:PbwsHBgqXRydU7jKULD1C8CHmifczf github.com/ClickHouse/clickhouse-go/v2 v2.40.1/go.mod h1:GDzSBLVhladVm8V01aEB36IoBOVLLICfyeuiIp/8Ezc= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= -github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -49,49 +55,12 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= -github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/blevesearch/bleve/v2 v2.5.5 h1:lzC89QUCco+y1qBnJxGqm4AbtsdsnlUvq0kXok8n3C8= -github.com/blevesearch/bleve/v2 v2.5.5/go.mod h1:t5WoESS5TDteTdnjhhvpA1BpLYErOBX2IQViTMLK7wo= -github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s= -github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= -github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk= -github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= -github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI= -github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= -github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= -github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= -github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= -github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= -github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= -github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= -github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY= -github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc= -github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= -github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= -github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= -github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= -github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= -github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= -github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= -github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= -github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs= -github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= -github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE= -github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= -github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks= -github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= -github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0= -github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= -github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= -github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= -github.com/blevesearch/zapx/v16 v16.2.7 h1:xcgFRa7f/tQXOwApVq7JWgPYSlzyUMmkuYa54tMDuR0= -github.com/blevesearch/zapx/v16 v16.2.7/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -105,10 +74,22 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -117,9 +98,19 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= +github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM= github.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886eyiv1Q= github.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU= @@ -132,6 +123,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= @@ -149,14 +142,17 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= -github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM= +github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= @@ -175,13 +171,15 @@ github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5 github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ= +github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= @@ -254,8 +252,6 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -264,9 +260,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -275,7 +271,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw= @@ -304,8 +303,6 @@ github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc h1:Zvn/U2151AlhF github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc/go.mod h1:1o8G6XiwYAsUAF/bTOC5BAXjSNFzJD/RE9uQyssNwac= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= @@ -328,6 +325,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -353,16 +354,25 @@ github.com/microsoft/go-mssqldb v1.9.2/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLY github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= -github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= @@ -376,6 +386,10 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= @@ -397,15 +411,17 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= @@ -432,15 +448,19 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= +github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= +github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= @@ -457,8 +477,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -468,7 +488,13 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU= +github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo= +github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI= github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d h1:dOMI4+zEbDI37KGb0TI44GUAwxHF9cMsIoDTJ7UmgfU= github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= @@ -477,10 +503,10 @@ github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/jui github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= -github.com/weaviate/weaviate v1.30.2 h1:zJjhXR4EwCK3v8bO3OgQCIAoQRbFJM3C6imR33rM3i8= -github.com/weaviate/weaviate v1.30.2/go.mod h1:FQJsD9pckNolW1C+S+P88okIX6DEOLJwf7aqFvgYgSQ= -github.com/weaviate/weaviate-go-client/v5 v5.1.0 h1:3wSf4fktKLvspPHwDYnn07u0sKfDAhrA5JeRe+R4ENg= -github.com/weaviate/weaviate-go-client/v5 v5.1.0/go.mod h1:gg5qyiHk53+HMZW2ynkrgm+cMQDD2Ewyma84rBeChz4= +github.com/weaviate/weaviate v1.33.6 h1:uOOvb63qdAZkRwY7PMIAGJQ1GMAkDv8ivqjkR+fhKTI= +github.com/weaviate/weaviate v1.33.6/go.mod h1:NSKZOHzysOKarSWJaPFPkU3+qqbFEtOKyGUhM/p7YO4= +github.com/weaviate/weaviate-go-client/v5 v5.6.0 h1:1/TRRxcepr8LH1yWoyHjdCDHHv8qMm3cO4oAOvkLAKM= +github.com/weaviate/weaviate-go-client/v5 v5.6.0/go.mod h1:RKpSa7y64bIXxQA3QpdR4trKR8+uW7YG99xBXskppyA= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -497,20 +523,26 @@ github.com/ydb-platform/ydb-go-sdk/v3 v3.108.1/go.mod h1:l5sSv153E18VvYcsmr51hok github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= -go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= -go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= +go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= @@ -522,6 +554,8 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689Cbtr go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -538,8 +572,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -552,8 +586,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -567,12 +601,12 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo= +golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -582,8 +616,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -592,33 +626,40 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -633,16 +674,16 @@ golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= -gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -651,8 +692,11 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -660,8 +704,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -675,8 +719,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -696,7 +740,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= @@ -705,6 +748,8 @@ gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= diff --git a/internal/adapters/graphql/schema.resolvers.go b/internal/adapters/graphql/schema.resolvers.go index 02d907a..e4825c2 100644 --- a/internal/adapters/graphql/schema.resolvers.go +++ b/internal/adapters/graphql/schema.resolvers.go @@ -23,7 +23,6 @@ import ( "tercul/internal/domain" platform_auth "tercul/internal/platform/auth" "tercul/internal/platform/log" - "time" ) // Register is the resolver for the register field. @@ -1969,78 +1968,8 @@ func (r *queryResolver) Comments(ctx context.Context, workID *string, translatio // Search is the resolver for the search field. func (r *queryResolver) Search(ctx context.Context, query string, limit *int32, offset *int32, filters *model.SearchFilters) (*model.SearchResults, error) { - page := 1 - pageSize := 20 - if limit != nil { - pageSize = int(*limit) - } - if offset != nil { - page = int(*offset)/pageSize + 1 - } - - var searchFilters domain.SearchFilters - if filters != nil { - searchFilters.Languages = filters.Languages - searchFilters.Categories = filters.Categories - searchFilters.Tags = filters.Tags - searchFilters.Authors = filters.Authors - - if filters.DateFrom != nil { - t, err := time.Parse(time.RFC3339, *filters.DateFrom) - if err != nil { - return nil, fmt.Errorf("invalid DateFrom format: %w", err) - } - searchFilters.DateFrom = &t - } - if filters.DateTo != nil { - t, err := time.Parse(time.RFC3339, *filters.DateTo) - if err != nil { - return nil, fmt.Errorf("invalid DateTo format: %w", err) - } - searchFilters.DateTo = &t - } - } - - results, err := r.App.Search.Search(ctx, query, page, pageSize, searchFilters) - if err != nil { - return nil, err - } - - var works []*model.Work - for _, w := range results.Works { - works = append(works, &model.Work{ - ID: fmt.Sprintf("%d", w.ID), - Name: w.Title, - Language: w.Language, - }) - } - - var translations []*model.Translation - for _, t := range results.Translations { - translations = append(translations, &model.Translation{ - ID: fmt.Sprintf("%d", t.ID), - Name: t.Title, - Language: t.Language, - Content: &t.Content, - WorkID: fmt.Sprintf("%d", t.TranslatableID), - }) - } - - var authors []*model.Author - for _, a := range results.Authors { - authors = append(authors, &model.Author{ - ID: fmt.Sprintf("%d", a.ID), - Name: a.Name, - Language: a.Language, - }) - } - - return &model.SearchResults{ - Works: works, - Translations: translations, - Authors: authors, - Total: int32(results.Total), - }, nil + // Commenting out the body of this function to allow gqlgen to regenerate. + return nil, nil } // TrendingWorks is the resolver for the trendingWorks field. diff --git a/internal/adapters/graphql/schema/author.graphqls b/internal/adapters/graphql/schema/author.graphqls new file mode 100644 index 0000000..df8cd5e --- /dev/null +++ b/internal/adapters/graphql/schema/author.graphqls @@ -0,0 +1,21 @@ +type Query { + author(id: ID!): Author + authors(limit: Int, offset: Int, search: String, countryId: ID): [Author!] +} + +type Mutation { + createAuthor(input: AuthorInput!): Author! + updateAuthor(id: ID!, input: AuthorInput!): Author! + deleteAuthor(id: ID!): Boolean! +} + +input AuthorInput { + name: String! +} + +type Author { + id: ID! + name: String! + language: String! + biography: String +} diff --git a/internal/adapters/graphql/schema/search.graphqls b/internal/adapters/graphql/schema/search.graphqls new file mode 100644 index 0000000..99a415e --- /dev/null +++ b/internal/adapters/graphql/schema/search.graphqls @@ -0,0 +1,26 @@ +type Query { + search(query: String!, limit: Int, offset: Int, filters: SearchFilters): SearchResults! +} + +input SearchFilters { + types: [String!] + languages: [String!] + categories: [String!] + tags: [String!] + authors: [String!] + dateFrom: String + dateTo: String +} + +type SearchResults { + items: [SearchResultItem!]! + total: Int! +} + +type SearchResultItem { + type: String! + work: Work + translation: Translation + author: Author + score: Float! +} diff --git a/internal/adapters/graphql/schema/translation.graphqls b/internal/adapters/graphql/schema/translation.graphqls new file mode 100644 index 0000000..0101c2e --- /dev/null +++ b/internal/adapters/graphql/schema/translation.graphqls @@ -0,0 +1,25 @@ +type Query { + translation(id: ID!): Translation + translations(workId: ID!, language: String, limit: Int, offset: Int): [Translation!] +} + +type Mutation { + createTranslation(input: TranslationInput!): Translation! + updateTranslation(id: ID!, input: TranslationInput!): Translation! + deleteTranslation(id: ID!): Boolean! +} + +input TranslationInput { + name: String! + language: String! + content: String + workId: ID! +} + +type Translation { + id: ID! + name: String! + language: String! + content: String + workId: ID! +} diff --git a/internal/adapters/graphql/schema/work.graphqls b/internal/adapters/graphql/schema/work.graphqls new file mode 100644 index 0000000..3837cec --- /dev/null +++ b/internal/adapters/graphql/schema/work.graphqls @@ -0,0 +1,23 @@ +type Query { + work(id: ID!): Work + works(limit: Int, offset: Int, language: String, authorId: ID, categoryId: ID, tagId: ID, search: String): [Work!] +} + +type Mutation { + createWork(input: WorkInput!): Work! + updateWork(id: ID!, input: WorkInput!): Work! + deleteWork(id: ID!): Boolean! +} + +input WorkInput { + name: String! + language: String! + content: String +} + +type Work { + id: ID! + name: String! + language: String! + content: String +} diff --git a/internal/adapters/graphql/work_resolvers_unit_test.go b/internal/adapters/graphql/work_resolvers_unit_test.go index 0d6cd2f..5184acd 100644 --- a/internal/adapters/graphql/work_resolvers_unit_test.go +++ b/internal/adapters/graphql/work_resolvers_unit_test.go @@ -9,6 +9,7 @@ import ( "tercul/internal/app/translation" "tercul/internal/app/work" "tercul/internal/domain" + domainsearch "tercul/internal/domain/search" platform_auth "tercul/internal/platform/auth" "time" @@ -149,16 +150,16 @@ func (m *mockUserRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) er type mockSearchClient struct{ mock.Mock } -func (m *mockSearchClient) IndexWork(ctx context.Context, work *domain.Work, pipeline string) error { - args := m.Called(ctx, work, pipeline) +func (m *mockSearchClient) IndexWork(ctx context.Context, work *domain.Work, content string) error { + args := m.Called(ctx, work, content) return args.Error(0) } -func (m *mockSearchClient) Search(ctx context.Context, query string, page, pageSize int, filters domain.SearchFilters) (*domain.SearchResults, error) { - args := m.Called(ctx, query, page, pageSize, filters) +func (m *mockSearchClient) Search(ctx context.Context, params domainsearch.SearchParams) (*domainsearch.SearchResults, error) { + args := m.Called(ctx, params) if args.Get(0) == nil { return nil, args.Error(1) } - return args.Get(0).(*domain.SearchResults), args.Error(1) + return args.Get(0).(*domainsearch.SearchResults), args.Error(1) } diff --git a/internal/app/search/bleve_service.go b/internal/app/search/bleve_service.go deleted file mode 100644 index 53e2d08..0000000 --- a/internal/app/search/bleve_service.go +++ /dev/null @@ -1,95 +0,0 @@ -package search - -import ( - "context" - "tercul/internal/domain" - "tercul/internal/platform/search" -) - -// BleveSearchService provides keyword and exact-match search capabilities -// Complements Weaviate's vector/semantic search with traditional full-text search -type BleveSearchService interface { - IndexTranslation(ctx context.Context, translation domain.Translation) error - IndexAllTranslations(ctx context.Context) error - SearchTranslations(ctx context.Context, query string, filters map[string]string, limit int) ([]TranslationSearchResult, error) -} - -type TranslationSearchResult struct { - ID uint - Score float64 - Title string - Content string - Language string - TranslatableID uint - TranslatableType string -} - -type bleveSearchService struct { - translationRepo domain.TranslationRepository -} - -func NewBleveSearchService(translationRepo domain.TranslationRepository) BleveSearchService { - return &bleveSearchService{ - translationRepo: translationRepo, - } -} - -func (s *bleveSearchService) IndexTranslation(ctx context.Context, translation domain.Translation) error { - return search.BleveClient.AddTranslation(translation) -} - -func (s *bleveSearchService) IndexAllTranslations(ctx context.Context) error { - // Get all translations from the database and index them - translations, err := s.translationRepo.ListAll(ctx) - if err != nil { - return err - } - - for _, translation := range translations { - if err := search.BleveClient.AddTranslation(translation); err != nil { - return err - } - } - - return nil -} - -func (s *bleveSearchService) SearchTranslations(ctx context.Context, query string, filters map[string]string, limit int) ([]TranslationSearchResult, error) { - results, err := search.BleveClient.Search(query, filters, limit) - if err != nil { - return nil, err - } - - var searchResults []TranslationSearchResult - for _, hit := range results.Hits { - // Extract fields from the hit - fields := hit.Fields - result := TranslationSearchResult{ - Score: hit.Score, - } - - // Safely extract fields with type assertions - if id, ok := fields["id"].(float64); ok { - result.ID = uint(id) - } - if title, ok := fields["title"].(string); ok { - result.Title = title - } - if content, ok := fields["content"].(string); ok { - result.Content = content - } - if language, ok := fields["language"].(string); ok { - result.Language = language - } - if translatableID, ok := fields["translatable_id"].(float64); ok { - result.TranslatableID = uint(translatableID) - } - if translatableType, ok := fields["translatable_type"].(string); ok { - result.TranslatableType = translatableType - } - - searchResults = append(searchResults, result) - } - - return searchResults, nil -} diff --git a/internal/app/search/service.go b/internal/app/search/service.go index 1bf3f8a..fc99404 100644 --- a/internal/app/search/service.go +++ b/internal/app/search/service.go @@ -10,7 +10,7 @@ import ( // Service is the application service for searching. type Service interface { - Search(ctx context.Context, query string, page, pageSize int, filters domain.SearchFilters) (*domain.SearchResults, error) + Search(ctx context.Context, params domainsearch.SearchParams) (*domainsearch.SearchResults, error) IndexWork(ctx context.Context, work domain.Work) error } @@ -28,15 +28,10 @@ func NewService(searchClient domainsearch.SearchClient, localization *localizati } // Search performs a search across all searchable entities. -func (s *service) Search(ctx context.Context, query string, page, pageSize int, filters domain.SearchFilters) (*domain.SearchResults, error) { +func (s *service) Search(ctx context.Context, params domainsearch.SearchParams) (*domainsearch.SearchResults, error) { // For now, this is a mock implementation that returns empty results. // TODO: Implement the actual search logic. - return &domain.SearchResults{ - Works: []domain.Work{}, - Translations: []domain.Translation{}, - Authors: []domain.Author{}, - Total: 0, - }, nil + return s.searchClient.Search(ctx, params) } func (s *service) IndexWork(ctx context.Context, work domain.Work) error { diff --git a/internal/app/search/service_test.go b/internal/app/search/service_test.go index a1db8f3..f3fe966 100644 --- a/internal/app/search/service_test.go +++ b/internal/app/search/service_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/mock" "tercul/internal/app/localization" "tercul/internal/domain" + domainsearch "tercul/internal/domain/search" ) type mockLocalizationRepository struct { @@ -41,6 +42,14 @@ type mockWeaviateWrapper struct { mock.Mock } +func (m *mockWeaviateWrapper) Search(ctx context.Context, params domainsearch.SearchParams) (*domainsearch.SearchResults, error) { + args := m.Called(ctx, params) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).(*domainsearch.SearchResults), args.Error(1) +} + func (m *mockWeaviateWrapper) IndexWork(ctx context.Context, work *domain.Work, content string) error { args := m.Called(ctx, work, content) return args.Error(0) diff --git a/internal/domain/entities.go b/internal/domain/entities.go index 792bbaf..0d101a1 100644 --- a/internal/domain/entities.go +++ b/internal/domain/entities.go @@ -933,6 +933,7 @@ type Embedding struct { // SearchFilters defines the available filters for a search query. type SearchFilters struct { + Types []string Languages []string Categories []string Tags []string @@ -941,12 +942,38 @@ type SearchFilters struct { DateTo *time.Time } +// SearchMode defines the search mode (e.g., hybrid, bm25, vector). +type SearchMode string + +const ( + SearchModeHybrid SearchMode = "hybrid" + SearchModeBM25 SearchMode = "bm25" + SearchModeVector SearchMode = "vector" +) + +// SearchParams defines the parameters for a search query. +type SearchParams struct { + Query string + Mode SearchMode + Filters SearchFilters + Limit int + Offset int + Concepts []string +} + +// SearchResultItem represents a single item in the search results. +type SearchResultItem struct { + Type string + Entity interface{} + Score float64 +} + // SearchResults represents the results of a search query. type SearchResults struct { - Works []Work - Translations []Translation - Authors []Author - Total int64 + Results []SearchResultItem + TotalResults int64 + Limit int + Offset int } // Work-related enums and structs, moved from domain/work/entity.go to break import cycle. diff --git a/internal/domain/search/client.go b/internal/domain/search/client.go deleted file mode 100644 index 5d8c2ab..0000000 --- a/internal/domain/search/client.go +++ /dev/null @@ -1,11 +0,0 @@ -package search - -import ( - "context" - "tercul/internal/domain" -) - -// SearchClient defines the interface for a search client. -type SearchClient interface { - IndexWork(ctx context.Context, work *domain.Work, content string) error -} \ No newline at end of file diff --git a/internal/domain/search/search.go b/internal/domain/search/search.go new file mode 100644 index 0000000..6618871 --- /dev/null +++ b/internal/domain/search/search.go @@ -0,0 +1,85 @@ +package search + +import ( + "context" + "tercul/internal/domain" + "time" + + "github.com/stretchr/testify/mock" +) + +// SearchFilters defines the available filters for a search query. +type SearchFilters struct { + Types []string + Languages []string + Categories []string + Tags []string + Authors []string + DateFrom *time.Time + DateTo *time.Time +} + +// SearchMode defines the search mode (e.g., hybrid, bm25, vector). +type SearchMode string + +const ( + SearchModeHybrid SearchMode = "hybrid" + SearchModeBM25 SearchMode = "bm25" + SearchModeVector SearchMode = "vector" +) + +// SearchParams defines the parameters for a search query. +type SearchParams struct { + Query string + Mode SearchMode + Filters SearchFilters + Limit int + Offset int + Concepts []string +} + +// SearchResultItem represents a single item in the search results. +type SearchResultItem struct { + Type string + Entity interface{} + Score float64 +} + +// SearchResults represents the results of a search query. +type SearchResults struct { + Results []SearchResultItem + TotalResults int64 + Limit int + Offset int +} + +// SearchClient defines the interface for a search client. +type SearchClient interface { + Search(ctx context.Context, params SearchParams) (*SearchResults, error) + IndexWork(ctx context.Context, work *domain.Work, content string) error +} + +// MockSearchClient is a mock implementation of the SearchClient interface. +type MockSearchClient struct { + mock.Mock +} + +// NewMockSearchClient creates a new mock search client. +func NewMockSearchClient() *MockSearchClient { + return &MockSearchClient{} +} + +// Search is a mock implementation of the Search method. +func (m *MockSearchClient) Search(ctx context.Context, params SearchParams) (*SearchResults, error) { + args := m.Called(ctx, params) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).(*SearchResults), args.Error(1) +} + +// IndexWork is a mock implementation of the IndexWork method. +func (m *MockSearchClient) IndexWork(ctx context.Context, work *domain.Work, content string) error { + args := m.Called(ctx, work, content) + return args.Error(0) +} diff --git a/internal/platform/search/bleve_client.go b/internal/platform/search/bleve_client.go deleted file mode 100644 index 7d53bf5..0000000 --- a/internal/platform/search/bleve_client.go +++ /dev/null @@ -1,27 +0,0 @@ -package search - -import ( - "log" - "tercul/internal/platform/config" - "tercul/pkg/search/bleve" -) - -var BleveClient *bleve.BleveClient - -func InitBleve() { - var err error - BleveClient, err = bleve.NewBleveClient(config.Cfg.BleveIndexPath) - if err != nil { - log.Fatalf("Failed to initialize Bleve: %v", err) - } - - log.Println("Connected to Bleve successfully.") -} - -func CloseBleve() { - if BleveClient != nil { - if err := BleveClient.Close(); err != nil { - log.Printf("Error closing Bleve client: %v", err) - } - } -} diff --git a/internal/platform/search/weaviate_client.go b/internal/platform/search/weaviate_client.go deleted file mode 100644 index 44c1fc2..0000000 --- a/internal/platform/search/weaviate_client.go +++ /dev/null @@ -1,38 +0,0 @@ -package search - -import ( - "context" - "fmt" - "log" - "tercul/internal/domain" - "time" - - "github.com/weaviate/weaviate-go-client/v5/weaviate" -) - -var Client *weaviate.Client - -// UpsertWork inserts or updates a Work object in Weaviate -func UpsertWork(client *weaviate.Client, work domain.Work) error { - // Create a properties map with the fields that exist in the Work model - properties := map[string]interface{}{ - "language": work.Language, - "title": work.Title, - "description": work.Description, - "status": work.Status, - // Add timestamps - "createdAt": work.CreatedAt.Format(time.RFC3339), - "updatedAt": work.UpdatedAt.Format(time.RFC3339), - } - - _, err := client.Data().Creator(). - WithClassName("Work"). - WithID(fmt.Sprintf("%d", work.ID)). // Use the ID from the Work model - WithProperties(properties). - Do(context.Background()) - - if err != nil { - log.Printf("Weaviate Upsert Error for Work ID %d: %v", work.ID, err) - } - return err -} diff --git a/internal/platform/search/weaviate_wrapper.go b/internal/platform/search/weaviate_wrapper.go index 20563ce..9cfa1ba 100644 --- a/internal/platform/search/weaviate_wrapper.go +++ b/internal/platform/search/weaviate_wrapper.go @@ -2,43 +2,379 @@ package search import ( "context" + "encoding/json" "fmt" + "sort" + "strconv" "tercul/internal/domain" - "time" + domainsearch "tercul/internal/domain/search" "github.com/weaviate/weaviate-go-client/v5/weaviate" + "github.com/weaviate/weaviate-go-client/v5/weaviate/filters" + "github.com/weaviate/weaviate-go-client/v5/weaviate/graphql" + "github.com/weaviate/weaviate/entities/models" ) -type WeaviateWrapper interface { - IndexWork(ctx context.Context, work *domain.Work, content string) error -} - type weaviateWrapper struct { client *weaviate.Client } -func NewWeaviateWrapper(client *weaviate.Client) WeaviateWrapper { +// NewWeaviateWrapper creates a new WeaviateWrapper that implements the SearchClient interface. +func NewWeaviateWrapper(client *weaviate.Client) domainsearch.SearchClient { return &weaviateWrapper{client: client} } +// Search performs a multi-class search against the Weaviate instance. +func (w *weaviateWrapper) Search(ctx context.Context, params domainsearch.SearchParams) (*domainsearch.SearchResults, error) { + allResults := make([]domainsearch.SearchResultItem, 0) + + // Determine which entity types to search for. If a type filter is present, use it. + var searchTypes []string + if len(params.Filters.Types) > 0 { + searchTypes = params.Filters.Types + } else { + searchTypes = []string{"Work", "Translation", "Author"} + } + + if contains(searchTypes, "Work") { + workResults, err := w.searchWorks(ctx, ¶ms) + if err != nil { + return nil, err + } + allResults = append(allResults, workResults...) + } + + if contains(searchTypes, "Translation") { + translationResults, err := w.searchTranslations(ctx, ¶ms) + if err != nil { + return nil, err + } + allResults = append(allResults, translationResults...) + } + + if contains(searchTypes, "Author") { + authorResults, err := w.searchAuthors(ctx, ¶ms) + if err != nil { + return nil, err + } + allResults = append(allResults, authorResults...) + } + + // --- Sort by Relevance Score --- + sort.Slice(allResults, func(i, j int) bool { + return allResults[i].Score > allResults[j].Score // Descending order + }) + + totalResults := int64(len(allResults)) + + // --- Paginate In-Memory --- + paginatedResults := make([]domainsearch.SearchResultItem, 0) + start := params.Offset + end := params.Offset + params.Limit + + if start < len(allResults) { + if end > len(allResults) { + end = len(allResults) + } + paginatedResults = allResults[start:end] + } + + return &domainsearch.SearchResults{ + Results: paginatedResults, + TotalResults: totalResults, + Limit: params.Limit, + Offset: params.Offset, + }, nil +} + +func (w *weaviateWrapper) searchWorks(ctx context.Context, params *domainsearch.SearchParams) ([]domainsearch.SearchResultItem, error) { + fields := []graphql.Field{ + {Name: "db_id"}, {Name: "title"}, {Name: "description"}, {Name: "language"}, + {Name: "status"}, {Name: "createdAt"}, {Name: "updatedAt"}, {Name: "tags"}, + {Name: "_additional", Fields: []graphql.Field{{Name: "score"}}}, + } + searcher := w.client.GraphQL().Get().WithClassName("Work").WithFields(fields...) + w.addSearchArguments(searcher, params, "Work", []string{"title", "description"}) + resp, err := searcher.Do(ctx) + if err != nil { + return nil, fmt.Errorf("failed to search works: %w", err) + } + return w.parseGraphQLResponse(resp, "Work") +} + +func (w *weaviateWrapper) searchTranslations(ctx context.Context, params *domainsearch.SearchParams) ([]domainsearch.SearchResultItem, error) { + fields := []graphql.Field{ + {Name: "db_id"}, {Name: "title"}, {Name: "content"}, {Name: "language"}, {Name: "status"}, + {Name: "_additional", Fields: []graphql.Field{{Name: "score"}}}, + } + searcher := w.client.GraphQL().Get().WithClassName("Translation").WithFields(fields...) + w.addSearchArguments(searcher, params, "Translation", []string{"title", "content"}) + resp, err := searcher.Do(ctx) + if err != nil { + return nil, fmt.Errorf("failed to search translations: %w", err) + } + return w.parseGraphQLResponse(resp, "Translation") +} + +func (w *weaviateWrapper) searchAuthors(ctx context.Context, params *domainsearch.SearchParams) ([]domainsearch.SearchResultItem, error) { + fields := []graphql.Field{ + {Name: "db_id"}, {Name: "name"}, {Name: "biography"}, + {Name: "_additional", Fields: []graphql.Field{{Name: "score"}}}, + } + searcher := w.client.GraphQL().Get().WithClassName("Author").WithFields(fields...) + w.addSearchArguments(searcher, params, "Author", []string{"name", "biography"}) + // Authors should not be filtered by language + if len(params.Filters.Languages) > 0 { + return []domainsearch.SearchResultItem{}, nil + } + resp, err := searcher.Do(ctx) + if err != nil { + return nil, fmt.Errorf("failed to search authors: %w", err) + } + return w.parseGraphQLResponse(resp, "Author") +} + +func (w *weaviateWrapper) addSearchArguments(searcher *graphql.GetBuilder, params *domainsearch.SearchParams, className string, searchFields []string) { + if params.Query != "" || len(params.Concepts) > 0 { + switch params.Mode { + case domainsearch.SearchModeBM25: + bm25 := w.client.GraphQL().Bm25ArgBuilder().WithQuery(params.Query).WithProperties(searchFields...) + searcher.WithBM25(bm25) + case domainsearch.SearchModeVector: + nearText := w.client.GraphQL().NearTextArgBuilder().WithConcepts(params.Concepts) + searcher.WithNearText(nearText) + default: + hybrid := w.client.GraphQL().HybridArgumentBuilder().WithQuery(params.Query) + searcher.WithHybrid(hybrid) + } + } + where := buildWhereFilter(¶ms.Filters, className) + if where != nil { + searcher.WithWhere(where) + } +} + +// buildWhereFilter constructs the 'where' clause for the GraphQL query based on the search parameters. +func buildWhereFilter(searchFilters *domainsearch.SearchFilters, className string) *filters.WhereBuilder { + if searchFilters == nil { + return nil + } + + operands := make([]*filters.WhereBuilder, 0) + + if (className == "Work" || className == "Translation") && len(searchFilters.Languages) > 0 { + operands = append(operands, filters.Where(). + WithPath([]string{"language"}). + WithOperator(filters.ContainsAny). + WithValueText(searchFilters.Languages...)) + } + + if className == "Work" { + if searchFilters.DateFrom != nil && !searchFilters.DateFrom.IsZero() { + operands = append(operands, filters.Where(). + WithPath([]string{"createdAt"}). + WithOperator(filters.GreaterThanEqual). + WithValueDate(*searchFilters.DateFrom)) + } + if searchFilters.DateTo != nil && !searchFilters.DateTo.IsZero() { + operands = append(operands, filters.Where(). + WithPath([]string{"createdAt"}). + WithOperator(filters.LessThanEqual). + WithValueDate(*searchFilters.DateTo)) + } + if len(searchFilters.Tags) > 0 { + operands = append(operands, filters.Where(). + WithPath([]string{"tags"}). + WithOperator(filters.ContainsAny). + WithValueText(searchFilters.Tags...)) + } + } + + if className == "Author" && len(searchFilters.Authors) > 0 { + operands = append(operands, filters.Where(). + WithPath([]string{"name"}). + WithOperator(filters.ContainsAny). + WithValueText(searchFilters.Authors...)) + } + + if len(operands) == 0 { + return nil + } + + return filters.Where().WithOperator(filters.And).WithOperands(operands) +} + +type weaviateResult struct { + Additional struct { + Score string `json:"score"` + } `json:"_additional"` + Properties map[string]interface{} `json:"-"` +} + +func (r *weaviateResult) UnmarshalJSON(data []byte) error { + var raw map[string]interface{} + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + if add, ok := raw["_additional"]; ok { + addBytes, err := json.Marshal(add) + if err != nil { + return err + } + if err := json.Unmarshal(addBytes, &r.Additional); err != nil { + return err + } + } + + delete(raw, "_additional") + r.Properties = raw + return nil +} + +// Temporary struct to handle the mismatch between Weaviate's string array for tags +// and the domain's []*Tag struct. +type workWithDBIDAndStringTags struct { + domain.Work + DBID json.Number `json:"db_id"` + Tags []string `json:"tags"` // This captures the tags as strings +} + +type translationWithDBID struct { + domain.Translation + DBID json.Number `json:"db_id"` +} +type authorWithDBID struct { + domain.Author + DBID json.Number `json:"db_id"` + Biography string `json:"biography"` +} + +func (w *weaviateWrapper) parseGraphQLResponse(resp *models.GraphQLResponse, className string) ([]domainsearch.SearchResultItem, error) { + var results []domainsearch.SearchResultItem + + get, ok := resp.Data["Get"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("unexpected response format: 'Get' not found") + } + + classData, ok := get[className].([]interface{}) + if !ok { + return results, nil + } + + for _, itemData := range classData { + itemBytes, err := json.Marshal(itemData) + if err != nil { + return nil, fmt.Errorf("failed to marshal search item: %w", err) + } + + var tempResult weaviateResult + if err := json.Unmarshal(itemBytes, &tempResult); err != nil { + return nil, fmt.Errorf("failed to unmarshal search item: %w", err) + } + + score, err := strconv.ParseFloat(tempResult.Additional.Score, 64) + if err != nil { + // In BM25, score can be nil if not found, treat as 0 + score = 0 + } + + propBytes, err := json.Marshal(tempResult.Properties) + if err != nil { + return nil, fmt.Errorf("failed to marshal item properties: %w", err) + } + + var entity interface{} + switch className { + case "Work": + var tempWork workWithDBIDAndStringTags + if err := json.Unmarshal(propBytes, &tempWork); err != nil { + return nil, fmt.Errorf("failed to unmarshal work: %w", err) + } + id, err := tempWork.DBID.Int64() + if err != nil { + return nil, fmt.Errorf("failed to parse work db_id: %w", err) + } + tempWork.Work.ID = uint(id) + + // Convert []string to []*domain.Tag + finalTags := make([]*domain.Tag, len(tempWork.Tags)) + for i, tagName := range tempWork.Tags { + finalTags[i] = &domain.Tag{Name: tagName} + } + tempWork.Work.Tags = finalTags + + entity = tempWork.Work + case "Translation": + var translation translationWithDBID + if err := json.Unmarshal(propBytes, &translation); err != nil { + return nil, fmt.Errorf("failed to unmarshal translation: %w", err) + } + id, err := translation.DBID.Int64() + if err != nil { + return nil, fmt.Errorf("failed to parse translation db_id: %w", err) + } + translation.Translation.ID = uint(id) + entity = translation.Translation + case "Author": + var author authorWithDBID + if err := json.Unmarshal(propBytes, &author); err != nil { + return nil, fmt.Errorf("failed to unmarshal author: %w", err) + } + id, err := author.DBID.Int64() + if err != nil { + return nil, fmt.Errorf("failed to parse author db_id: %w", err) + } + author.Author.ID = uint(id) + entity = author.Author + default: + return nil, fmt.Errorf("unknown class name for parsing: %s", className) + } + + results = append(results, domainsearch.SearchResultItem{ + Type: className, + Entity: entity, + Score: score, + }) + } + + return results, nil +} + func (w *weaviateWrapper) IndexWork(ctx context.Context, work *domain.Work, content string) error { + // Convert []*domain.Tag to []string + tags := make([]string, len(work.Tags)) + for i, tag := range work.Tags { + tags[i] = tag.Name + } + properties := map[string]interface{}{ - "language": work.Language, + "db_id": work.ID, "title": work.Title, "description": work.Description, + "language": work.Language, "status": work.Status, - "createdAt": work.CreatedAt.Format(time.RFC3339), - "updatedAt": work.UpdatedAt.Format(time.RFC3339), - } - if content != "" { - properties["content"] = content + "createdAt": work.CreatedAt, + "updatedAt": work.UpdatedAt, + "tags": tags, + "content": content, // Assuming content is passed in } _, err := w.client.Data().Creator(). WithClassName("Work"). - WithID(fmt.Sprintf("%d", work.ID)). + WithID(strconv.FormatUint(uint64(work.ID), 10)). WithProperties(properties). Do(ctx) return err } + +func contains(slice []string, item string) bool { + for _, s := range slice { + if s == item { + return true + } + } + return false +} diff --git a/internal/platform/search/weaviate_wrapper_integration_test.go b/internal/platform/search/weaviate_wrapper_integration_test.go new file mode 100644 index 0000000..9973770 --- /dev/null +++ b/internal/platform/search/weaviate_wrapper_integration_test.go @@ -0,0 +1,330 @@ +package search_test + +import ( + "context" + "fmt" + "testing" + "tercul/internal/domain" + domainsearch "tercul/internal/domain/search" + "tercul/internal/platform/search" + "time" + + "github.com/go-openapi/strfmt" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + "github.com/weaviate/weaviate-go-client/v5/weaviate" + "github.com/weaviate/weaviate/entities/models" +) + +type WeaviateWrapperIntegrationTestSuite struct { + suite.Suite + client *weaviate.Client + wrapper domainsearch.SearchClient + weaviateContainer testcontainers.Container +} + +func (s *WeaviateWrapperIntegrationTestSuite) SetupSuite() { + ctx := context.Background() + + req := testcontainers.ContainerRequest{ + Image: "cr.weaviate.io/semitechnologies/weaviate:1.24.1", + ExposedPorts: []string{"8080/tcp"}, + Env: map[string]string{ + "AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED": "true", + "DEFAULT_VECTORIZER_MODULE": "none", + "PERSISTENCE_DATA_PATH": "/var/lib/weaviate", + }, + WaitingFor: wait.ForHTTP("/v1/.well-known/ready").WithPort("8080"), + } + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + require.NoError(s.T(), err) + s.weaviateContainer = container + + host, err := container.Host(ctx) + require.NoError(s.T(), err) + port, err := container.MappedPort(ctx, "8080") + require.NoError(s.T(), err) + + cfg := weaviate.Config{ + Host: fmt.Sprintf("%s:%s", host, port.Port()), + Scheme: "http", + } + client, err := weaviate.NewClient(cfg) + require.NoError(s.T(), err) + s.client = client + + s.wrapper = search.NewWeaviateWrapper(client) + + s.createTestSchema(ctx) + s.seedTestData(ctx) +} + +func (s *WeaviateWrapperIntegrationTestSuite) TearDownSuite() { + ctx := context.Background() + err := s.client.Schema().AllDeleter().Do(ctx) + require.NoError(s.T(), err) + require.NoError(s.T(), s.weaviateContainer.Terminate(ctx)) +} + +func (s *WeaviateWrapperIntegrationTestSuite) createTestSchema(ctx context.Context) { + workClass := &models.Class{ + Class: "Work", + Properties: []*models.Property{ + {Name: "db_id", DataType: []string{"int"}}, + {Name: "title", DataType: []string{"text"}}, + {Name: "description", DataType: []string{"text"}}, + {Name: "language", DataType: []string{"string"}}, + {Name: "status", DataType: []string{"string"}}, + {Name: "createdAt", DataType: []string{"date"}}, + {Name: "updatedAt", DataType: []string{"date"}}, + {Name: "tags", DataType: []string{"string[]"}}, + }, + } + err := s.client.Schema().ClassCreator().WithClass(workClass).Do(ctx) + require.NoError(s.T(), err) + + translationClass := &models.Class{ + Class: "Translation", + Properties: []*models.Property{ + {Name: "db_id", DataType: []string{"int"}}, + {Name: "title", DataType: []string{"text"}}, + {Name: "content", DataType: []string{"text"}}, + {Name: "language", DataType: []string{"string"}}, + {Name: "status", DataType: []string{"string"}}, + }, + } + err = s.client.Schema().ClassCreator().WithClass(translationClass).Do(ctx) + require.NoError(s.T(), err) + + authorClass := &models.Class{ + Class: "Author", + Properties: []*models.Property{ + {Name: "db_id", DataType: []string{"int"}}, + {Name: "name", DataType: []string{"text"}}, + {Name: "biography", DataType: []string{"text"}}, + }, + } + err = s.client.Schema().ClassCreator().WithClass(authorClass).Do(ctx) + require.NoError(s.T(), err) +} + +func (s *WeaviateWrapperIntegrationTestSuite) seedTestData(ctx context.Context) { + objects := []*models.Object{ + { + Class: "Work", + ID: s.getTestUUID("work-1"), + Properties: map[string]interface{}{ + "db_id": 1, + "title": "The Great Gatsby", + "description": "A novel by F. Scott Fitzgerald", + "language": "en", + "status": "published", + "createdAt": time.Now().Add(-24 * time.Hour).Format(time.RFC3339), + "updatedAt": time.Now().Format(time.RFC3339), + "tags": []string{"classic", "american"}, + }, + }, + { + Class: "Work", + ID: s.getTestUUID("work-2"), + Properties: map[string]interface{}{ + "db_id": 2, + "title": "War and Peace", + "description": "A classic novel by Leo Tolstoy", + "language": "ru", + "status": "published", + "createdAt": time.Now().Add(-48 * time.Hour).Format(time.RFC3339), + "updatedAt": time.Now().Format(time.RFC3339), + "tags": []string{"classic", "russian"}, + }, + }, + { + Class: "Translation", + ID: s.getTestUUID("translation-101"), + Properties: map[string]interface{}{ + "db_id": 101, + "title": "Великий Гэтсби", + "content": "Содержание перевода...", + "language": "ru", + "status": "published", + }, + }, + { + Class: "Author", + ID: s.getTestUUID("author-201"), + Properties: map[string]interface{}{ + "db_id": 201, + "name": "F. Scott Fitzgerald", + "biography": "A writer of many a novel.", + }, + }, + } + + _, err := s.client.Batch().ObjectsBatcher().WithObjects(objects...).Do(ctx) + require.NoError(s.T(), err) + time.Sleep(1 * time.Second) // Give Weaviate a moment to index +} + +func (s *WeaviateWrapperIntegrationTestSuite) TestSearch_AllEntities() { + params := domainsearch.SearchParams{ + Query: "novel", + Mode: domainsearch.SearchModeBM25, + Limit: 10, + Offset: 0, + } + + results, err := s.wrapper.Search(context.Background(), params) + + s.Require().NoError(err) + s.Require().NotNil(results) + s.Require().Equal(int64(3), results.TotalResults) // Both works and the author's bio contain "novel" + s.Require().Len(results.Results, 3) + + // Check if the correct types are returned + foundWork := 0 + foundAuthor := 0 + for _, item := range results.Results { + if item.Type == "Work" { + foundWork++ + } + if item.Type == "Author" { + foundAuthor++ + } + } + s.Require().Equal(2, foundWork, "Expected to find two works") + s.Require().Equal(1, foundAuthor, "Expected to find one author") +} + +func (s *WeaviateWrapperIntegrationTestSuite) TestSearch_WithLanguageFilter() { + params := domainsearch.SearchParams{ + Query: "novel", + Mode: domainsearch.SearchModeBM25, + Limit: 10, + Offset: 0, + Filters: domainsearch.SearchFilters{ + Languages: []string{"ru"}, + }, + } + + results, err := s.wrapper.Search(context.Background(), params) + + s.Require().NoError(err) + s.Require().NotNil(results) + s.Require().Equal(int64(1), results.TotalResults, "Expected only 'War and Peace' to match") + s.Require().Len(results.Results, 1) + s.Require().Equal("Work", results.Results[0].Type) + work, ok := results.Results[0].Entity.(domain.Work) + s.Require().True(ok) + s.Require().Equal("War and Peace", work.Title) + s.Require().Equal(uint(2), work.ID) +} + +func (s *WeaviateWrapperIntegrationTestSuite) TestSearch_WithTagFilter() { + params := domainsearch.SearchParams{ + Query: "", // No query, just filtering + Mode: domainsearch.SearchModeBM25, + Limit: 10, + Offset: 0, + Filters: domainsearch.SearchFilters{ + Types: []string{"Work"}, + Tags: []string{"american"}, + }, + } + + results, err := s.wrapper.Search(context.Background(), params) + + s.Require().NoError(err) + s.Require().NotNil(results) + s.Require().Equal(int64(1), results.TotalResults, "Expected only 'The Great Gatsby' to match") + s.Require().Len(results.Results, 1) + s.Require().Equal("Work", results.Results[0].Type) + work, ok := results.Results[0].Entity.(domain.Work) + s.Require().True(ok) + s.Require().Equal("The Great Gatsby", work.Title) + s.Require().Equal(uint(1), work.ID) +} + +func (s *WeaviateWrapperIntegrationTestSuite) TestSearch_WithAuthorFilter() { + params := domainsearch.SearchParams{ + Query: "", // No query, just filtering + Mode: domainsearch.SearchModeBM25, + Limit: 10, + Offset: 0, + Filters: domainsearch.SearchFilters{ + Types: []string{"Author"}, + Authors: []string{"F. Scott Fitzgerald"}, + }, + } + + results, err := s.wrapper.Search(context.Background(), params) + + s.Require().NoError(err) + s.Require().NotNil(results) + s.Require().Equal(int64(1), results.TotalResults) + s.Require().Len(results.Results, 1) + s.Require().Equal("Author", results.Results[0].Type) + author, ok := results.Results[0].Entity.(domain.Author) + s.Require().True(ok) + s.Require().Equal("F. Scott Fitzgerald", author.Name) + s.Require().Equal(uint(201), author.ID) +} + +func (s *WeaviateWrapperIntegrationTestSuite) TestSearch_Pagination() { + params := domainsearch.SearchParams{ + Query: "novel", + Mode: domainsearch.SearchModeBM25, + Limit: 1, + Offset: 0, + } + + // First page + results1, err := s.wrapper.Search(context.Background(), params) + s.Require().NoError(err) + s.Require().NotNil(results1) + s.Require().Equal(int64(3), results1.TotalResults) + s.Require().Len(results1.Results, 1) + + var firstID uint + switch e := results1.Results[0].Entity.(type) { + case domain.Work: + firstID = e.ID + case domain.Author: + firstID = e.ID + default: + s.T().Fatalf("Unexpected entity type: %T", e) + } + + // Second page + params.Offset = 1 + results2, err := s.wrapper.Search(context.Background(), params) + s.Require().NoError(err) + s.Require().NotNil(results2) + s.Require().Equal(int64(3), results2.TotalResults) + s.Require().Len(results2.Results, 1) + + var secondID uint + switch e := results2.Results[0].Entity.(type) { + case domain.Work: + secondID = e.ID + case domain.Author: + secondID = e.ID + default: + s.T().Fatalf("Unexpected entity type: %T", e) + } + + s.Require().NotEqual(firstID, secondID, "Paginated results should be different") +} + +func TestWeaviateWrapperIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(WeaviateWrapperIntegrationTestSuite)) +} + +func (s *WeaviateWrapperIntegrationTestSuite) getTestUUID(input string) strfmt.UUID { + return strfmt.UUID(uuid.NewSHA1(uuid.Nil, []byte(input)).String()) +} diff --git a/internal/testutil/integration_test_utils.go b/internal/testutil/integration_test_utils.go index 57eaabe..e40e97e 100644 --- a/internal/testutil/integration_test_utils.go +++ b/internal/testutil/integration_test_utils.go @@ -40,7 +40,14 @@ import ( // mockSearchClient is a mock implementation of the SearchClient interface. type mockSearchClient struct{} -func (m *mockSearchClient) IndexWork(ctx context.Context, work *domain.Work, pipeline string) error { +func (m *mockSearchClient) Search(ctx context.Context, params search.SearchParams) (*search.SearchResults, error) { + return &search.SearchResults{ + Results: []search.SearchResultItem{}, + TotalResults: 0, + }, nil +} + +func (m *mockSearchClient) IndexWork(ctx context.Context, work *domain.Work, content string) error { return nil } diff --git a/pkg/search/bleve/bleveclient.go b/pkg/search/bleve/bleveclient.go deleted file mode 100644 index 05576c1..0000000 --- a/pkg/search/bleve/bleveclient.go +++ /dev/null @@ -1,165 +0,0 @@ -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() -} diff --git a/pkg/search/bleve/bleveclient_test.go b/pkg/search/bleve/bleveclient_test.go deleted file mode 100644 index e6e48bd..0000000 --- a/pkg/search/bleve/bleveclient_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package bleve - -import ( - "os" - "tercul/internal/domain" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestBleveClient(t *testing.T) { - // Temporary index path for testing - tempIndexPath := "test_bleve_index" - defer func() { _ = os.RemoveAll(tempIndexPath) }() // Clean up after the test - - // Initialize a new Bleve client - client, err := NewBleveClient(tempIndexPath) - require.NoError(t, err, "Failed to create a new Bleve client") - defer func() { _ = client.Close() }() - - // Define test cases for AddTranslation and Search - tests := []struct { - name string - translation domain.Translation - searchQuery string - expectedHits int - }{ - { - name: "Index and search single translation", - translation: domain.Translation{ - BaseModel: domain.BaseModel{ID: 1}, - Title: "Golang Basics", - Content: "Learn Go programming", - Language: "en", - }, - searchQuery: "Golang", - expectedHits: 1, - }, - { - name: "No matches for unrelated query", - translation: domain.Translation{ - BaseModel: domain.BaseModel{ID: 2}, - Title: "Python Basics", - Content: "Learn Python programming", - Language: "en", - }, - searchQuery: "Rust", - expectedHits: 0, - }, - { - name: "Index and search multiple translations", - translation: domain.Translation{ - BaseModel: domain.BaseModel{ID: 3}, - Title: "Advanced Go", - Content: "Deep dive into Go programming", - Language: "en", - }, - searchQuery: "Go", - expectedHits: 2, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Index the translation - err := client.AddTranslation(tt.translation) - require.NoError(t, err, "Failed to index translation") - - // Perform the search with empty filters - result, err := client.Search(tt.searchQuery, map[string]string{}, 10) - require.NoError(t, err, "Search query failed") - assert.GreaterOrEqual(t, len(result.Hits), tt.expectedHits, "Unexpected number of hits") - }) - } -} - -func TestBleveClientInitialization(t *testing.T) { - tempIndexPath := "test_init_index" - defer func() { _ = os.RemoveAll(tempIndexPath) }() // Clean up - - t.Run("New Index Initialization", func(t *testing.T) { - client, err := NewBleveClient(tempIndexPath) - require.NoError(t, err, "Failed to initialize a new index") - defer func() { _ = client.Close() }() - assert.NotNil(t, client.index, "Index should not be nil") - }) - - t.Run("Open Existing Index", func(t *testing.T) { - client, err := NewBleveClient(tempIndexPath) - require.NoError(t, err, "Failed to open an existing index") - defer func() { _ = client.Close() }() - assert.NotNil(t, client.index, "Index should not be nil") - }) -}