# manticoresearch-client **Repository Path**: ymjake/manticoresearch-client ## Basic Information - **Project Name**: manticoresearch-client - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-26 - **Last Updated**: 2026-03-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: Manticore ## README # ManticoreSearch .NET Client Handcrafted C# SDK mirroring the official PHP client. Every endpoint, table helper, query builder, result wrapper, and admin API is handwritten. ## Features - Table CRUD, bulk, DDL, PQ search/management, cluster/nodes utilities. - Fluent typed DSL (`SearchRequestDescriptor`) + low-level SQL/CLI escape hatch. - Result wrappers (`ResultSet`) with hits + metadata (`took`, `timed_out`, facets, profile). - Pluggable node pools and transport hooks (`ILogger`, `IHttpTransportFactory`). - Serializer customization with `System.Text.Json` context support. - Schema mapping via `TableSchemaBuilder` + `[ManticoreField]` + `[JsonPropertyName]`. - KNN naming aligned to Manticore (`query_vector`, `doc_id`, `query`). ## Model Definition Use `[ManticoreField]` for table schema (type, indexed, stored) and `[JsonPropertyName]` for field name mapping: ```csharp public sealed class MovieDocument { [JsonPropertyName("id")] [ManticoreField("bigint")] public ulong Id { get; set; } [JsonPropertyName("title")] [ManticoreField("text", Indexed = true, Stored = true)] public string Title { get; set; } = string.Empty; [JsonPropertyName("genre")] [ManticoreField("text", Indexed = true, Stored = true)] public string Genre { get; set; } = string.Empty; [JsonPropertyName("director_id")] [ManticoreField("bigint")] public ulong DirectorId { get; set; } [JsonPropertyName("rating")] [ManticoreField("float")] public float Rating { get; set; } } ``` ## Quick Start ```csharp var pool = new SingleManticoreNodePool(new Uri("http://127.0.0.1:9308")); var client = new ManticoreClient(new ManticoreClientSettings(pool)); var table = client.Table("movies"); // Schema is driven by [ManticoreField] annotations var schema = TableSchemaBuilder.FromType(); await table.CreateAsync(schema, silent: true); await table.AddDocumentsAsync([ new MovieDocument { Id = 1, Title = "Interstellar", Genre = "sci-fi", DirectorId = 1, Rating = 8.6f }, new MovieDocument { Id = 2, Title = "Inception", Genre = "thriller", DirectorId = 1, Rating = 8.8f }, ]); var result = await table.SearchAsync(s => s .Match(m => m.Title, "Interstellar") .Range(m => m.Rating, gte: 8) .Limit(5)); foreach (var hit in result.Documents) { Console.WriteLine($"{hit.Source!.Title} rating={hit.Source.Rating}"); } ``` ## Advanced Query Examples ```csharp // Join query with fluent descriptor await moviesTable.SearchAsync(s => s .Match(m => m.Genre, "sci-fi") .Join(j => j .Inner() .Table("directors") .On(m => m.DirectorId, d => d.Id) .Query(q => q.Match(d => d.Country, "US"))) .Sort(sort => sort.Field(m => m.Rating, "desc")) .Limit(10)); // KNN + ScriptFields await table.SearchAsync(s => s .Match(m => m.Genre, "thriller") .ScriptFields(sf => sf.Add("score", "doc['rating'] * params.boost")) .Knn(k => k .Field("embedding") .K(5) .DocumentId(42))); ``` ## Multilingual SQL Sample ```csharp var table = client.Table("documents_idx_sample"); await table.DropAsync(silent: true); var schema = TableSchemaBuilder.FromType(d => d .Column(x => x.Title, "text", opts => opts.Indexed().Stored()) .Column(x => x.Content, "text", opts => opts.Indexed().Stored()) .Column(x => x.Tags, "text", opts => opts.Indexed().Stored())); await client.Tables().CreateAsync( "documents_idx_sample", schema, new Dictionary { ["min_word_len"] = "1", ["min_infix_len"] = "1", ["expand_keywords"] = "1", ["morphology"] = "none", ["index_exact_words"] = "1", ["ngram_len"] = "2", ["ngram_chars"] = "cjk" }, silent: true); // OR tokenization for better recall var matchExpression = string.Join(" | ", tokens.Select(t => $"\"{t}\"")); var sql = $"SELECT id, title, WEIGHT() AS weight FROM `documents_idx_sample` WHERE MATCH('{matchExpression}') ORDER BY weight DESC LIMIT 10"; var response = await client.SqlAsync(sql, rawResponse: false); ``` ## Transport & Authentication ```csharp var uris = new[] { new Uri("https://node1.example.com:9308"), new Uri("https://node2.example.com:9308"), new Uri("https://node3.example.com:9308") }; var pool = new StaticManticoreNodePool(uris); var settings = new ManticoreClientSettings( pool, sourceSerializerFactory: _ => new DefaultManticoreSerializer(MySourceContext.Default)) .Authentication(new BasicAuthentication("elastic", "changeme")) .Proxy("http://proxy.internal:8080"); var client = new ManticoreClient(settings); ``` `MySourceContext` is your `JsonSerializerContext` for Source Generation support: ```csharp [JsonSerializable(typeof(MovieDocument))] [JsonSerializable(typeof(DirectorDocument))] [JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] public partial class MySourceContext : JsonSerializerContext { } ``` > Note: Manticore server itself does not enforce built-in HTTP Basic auth by default. Use `.Authentication(...)` when your deployment has an auth gateway/reverse proxy (Nginx/API gateway) that validates `Authorization` headers before forwarding to Manticore. ## Error Handling The low-level transport maps failures into explicit exception types: - `ManticoreConnectionException`: network/connectivity failures. - `ManticoreTimeoutException`: request timeout. - `ManticoreServerException`: non-success HTTP status (includes `StatusCode` and `ResponseBody`). - `ManticoreSerializationException`: response deserialization failure. ## Release Notes ### [1.3.0] - 2026-03-12 Changed: - `[ManticoreField]` no longer carries `Name` — field name mapping is now handled exclusively by `[JsonPropertyName]`, aligning with Elastic .NET client conventions. - `ExpressionFieldResolver` now reads `[JsonPropertyName]` for field name resolution in queries, sort, and highlight. - `TableSchemaBuilder` now reads `[JsonPropertyName]` for column name resolution. - Join API refactored from `new JoinClause(...)` to fluent `JoinDescriptor` lambda style. - `QueryDescriptor` `Match` and `MatchPhrase` now have string field name overloads for use in Join queries and dynamic field scenarios. - `SortDescriptor` `Field` now has an Expression overload for type-safe sort field resolution. Fixed: - Join query `query_string` replaced with `match` in examples — `query_string` may silently drop short tokens like `"US"` as stopwords. ### [1.2.0] - 2026-02-26 Changed: - Refactored SQL internals into focused components: `SqlText`, `SqlBuilder`, and `DocumentPayload`. - Unified SQL statement composition style across `TableClient`, `ClusterClient`, `TablesAdminClient`, and `NodesClient`. - Aligned KNN payload naming with current Manticore conventions: `query_vector`, `doc_id`, and `query`. Added: - xUnit-based regression tests for KNN serialization, search response shaping, SQL builder formatting, and document payload normalization. - `InternalsVisibleTo` setup for internal-component unit testing. ### [1.1.0] - 2026-02-26 Added: - Initial public package release for .NET 8 / .NET 10 targets.