libmygramclient - MygramDB Client Library
Overview
libmygramclient is the C/C++ client SDK bundled with MygramDB. It connects to and queries MygramDB over its TCP protocol, and provides a C++17 API plus a C API for native integrations and language bindings. Installing MygramDB installs the headers, libraries, CMake package, and pkg-config metadata together.
Before you start
This library is a thin client for the MygramDB TCP protocol. Query grammar and result semantics are identical to the server side, so read the Query Syntax Guide alongside this page.
Features
What is RAII?
RAII is a C++ pattern where a resource — a connection, memory — is acquired when an object is constructed and released automatically when it is destroyed. It makes cleanup hard to forget, even with exceptions or early returns.
- Full support for all MygramDB protocol commands
- Modern C++17 API with RAII and type safety
- C API for easy integration with other languages
- Thread-safe connection management
- Both static and shared library builds
- Full Unicode support including 4-byte UTF-8 characters (emojis: 😀🎉👍)
Building
The library is built automatically with MygramDB:
makeThis creates both libmygramclient.a and the platform shared library. CMake exposes them as MygramDB::client_static and MygramDB::client_shared.
Installation
sudo make installThis installs headers under <prefix>/include/mygramdb, libraries under <prefix>/lib (or the platform library directory), a MygramDBClient CMake package, and mygramclient.pc. Set your build tool's prefix or PKG_CONFIG_PATH when installing to a non-default location.
C++ API
Basic Usage
#include <mygramdb/mygramclient.h>
#include <iostream>
using namespace mygramdb::client;
int main() {
// Configure client
ClientConfig config;
config.host = "localhost";
config.port = 11016;
config.timeout_ms = 5000;
// Create client
MygramClient client(config);
// Connect
if (auto connected = client.Connect(); !connected) {
std::cerr << "Connection failed: " << connected.error().message() << std::endl;
return 1;
}
// Use a bare table name on a single-database server; otherwise qualify it.
auto result = client.Search("app_db.articles", "hello world", 100);
if (!result) {
std::cerr << "Search failed: " << result.error().message() << std::endl;
return 1;
}
const auto& resp = *result;
std::cout << "Found " << resp.total_count << " results\n";
for (const auto& doc : resp.results) {
std::cout << " - " << doc.primary_key << "\n";
}
return 0;
}Consume the installed SDK
If linking fails
When libmygramclient is installed outside the standard prefixes, you may need -I and -L at compile time and LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) at run time.
cmake_minimum_required(VERSION 3.15)
project(myapp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(MygramDBClient CONFIG REQUIRED)
add_executable(myapp myapp.cpp)
target_link_libraries(myapp PRIVATE MygramDB::client_static)Build it with the installation prefix when required:
cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/mygramdb-prefix
cmake --build buildUse MygramDB::client_shared when the application must link to the shared library. For a C or C++ build system that uses pkg-config, mygramclient supplies the include directory, library, and private thread dependency:
cc -std=c11 myapp.c $(pkg-config --cflags --libs mygramclient) -o myappAdvanced Search
// Search with AND, NOT, and FILTER
std::vector<std::string> and_terms = {"AI"};
std::vector<std::string> not_terms = {"old"};
std::vector<std::pair<std::string, std::string>> filters = {
{"status", "active"},
{"category", "tech"}
};
auto result = client.Search(
"app_db.articles", // database-qualified table
"technology", // query
50, // limit
0, // offset
and_terms, // AND terms
not_terms, // NOT terms
filters, // filters
"created_at", // order by column
true // descending
);Typed Search Options
Use SearchOptions when a query needs comparison filters, fuzzy matching, highlighting, sorting, or pagination. Use SearchRaw only for a boolean expression you construct deliberately.
Concurrency per connection
MygramClient serializes concurrent commands on its single TCP connection, so it is safe to share — but one instance executes one request at a time. Use several instances when you need throughput, and never call Disconnect() while a request is in flight.
Thinking about connections
Holding one client per worker or thread and reusing it beats reconnecting for every short request. After a disconnect or timeout, construct a fresh MygramClient and reconnect.
SearchOptions options;
options.limit = 50;
options.offset = 20;
options.sort_column = "created_at";
options.sort_desc = true;
options.filters = {{"status", FilterOp::kEqual, "active"}};
options.fuzzy_distance = 1;
options.highlight = HighlightOptions{"<strong>", "</strong>", 200, 3};
options.query_mode = QueryMode::kBoolean;
auto result = client.Search("app_db.articles", "technology OR programming", options);
if (!result) {
std::cerr << result.error().message() << "\n";
}Count Query
auto result = client.Count("app_db.articles", "hello");
if (result) {
std::cout << "Total matches: " << result->count << "\n";
} else {
std::cerr << "Count failed: " << result.error().message() << "\n";
}Facet pagination
Facet returns one page of values in facets. total_count is the number of distinct facet values before offset and limit, so it is suitable for pagination controls.
auto response = client.Facet("app_db.articles", "category", "technology", 10,
{}, {}, {}, 20);
if (response) {
std::cout << response->total_count << " distinct categories\n";
for (const auto& facet : response->facets) {
std::cout << facet.value << ": " << facet.count << "\n";
}
}Get Document
auto result = client.Get("app_db.articles", "12345");
if (result) {
std::cout << "Primary key: " << result->primary_key << "\n";
for (const auto& [key, value] : result->fields) {
std::cout << " " << key << " = " << value << "\n";
}
}Server Info
auto result = client.Info();
if (result) {
std::cout << "Version: " << result->version << "\n";
std::cout << "Documents: " << result->doc_count << "\n";
std::cout << "Uptime: " << result->uptime_seconds << "s\n";
}Administrative authentication
In v1.10, administrative methods use connection-scoped authentication. ClientConfig has no admin_token field: after connecting, send AUTH on that same MygramClient and require the exact OK AUTHENTICATED response before GetConfig/CONFIG, SetVariable, ShowVariables, Cache*, Optimize, Sync*, Dump*, Save, Load, replication, or EnableDebug/DisableDebug. A successful raw send only means a response was received. Authenticate again after every reconnect. Search, count, get, and facet operations do not require this authentication.
#include <cstdlib>
#include <iostream>
#include <string>
const char* token = std::getenv("MYGRAM_API_ADMIN_TOKEN");
if (token == nullptr) {
std::cerr << "MYGRAM_API_ADMIN_TOKEN is not set\n";
return 1;
}
auto auth = client.SendCommand(std::string("AUTH ") + token);
if (!auth) {
std::cerr << "Admin authentication request failed: " << auth.error().message() << "\n";
return 1;
}
if (*auth != "OK AUTHENTICATED") {
std::cerr << "Admin authentication was rejected\n";
return 1;
}
// Do not log the token or the AUTH command.
auto optimized = client.Optimize("app_db.articles");Debug Mode
Debug mode is administrative. This example assumes that the same client was authenticated as in the preceding section.
// Enable debug mode
client.EnableDebug();
auto result = client.Search("app_db.articles", "hello", 10);
if (result && result->debug) {
std::cout << "Query time: " << result->debug->query_time_ms << "ms\n";
std::cout << "Candidates: " << result->debug->candidates << "\n";
}
// Disable debug mode
client.DisableDebug();Search Expression Parser
The library includes a web-style search expression parser that converts Google-like search syntax into MygramDB query format.
Syntax
term1 term2- Multiple terms with implicit AND (both must appear)"phrase"- Quoted phrase (exact match with spaces)+term- Explicitly required term (same as non-prefixed)-term- Excluded term (must NOT appear in results)term1 OR term2- Logical OR between terms(expr)- Grouping with parentheses- Full-width space (
) is supported as a delimiter (useful for Japanese text)
Examples
#include <mygramdb/search_expression.h>
using namespace mygramdb::client;
auto parsed = ParseSearchExpression("\"deep learning\" +(tutorial OR guide) -old");
if (!parsed) {
std::cerr << parsed.error().message() << "\n";
return 1;
}
const auto& expr = *parsed;
// expr.required_terms contains the required terms.
// expr.excluded_terms contains "old".Converting to Query String
// Convert directly to QueryAST-compatible string
auto result = ConvertSearchExpression("+golang -old");
if (result) {
std::string query = *result;
// query = "golang AND NOT old"
// Use with MygramClient
auto search_result = client.SearchRaw("app_db.articles", query, 100);
} else {
std::cerr << result.error().message() << "\n";
}Expression Examples
| Input | Output Query | Description |
|---|---|---|
golang tutorial | golang AND tutorial | Implicit AND - both terms required |
"machine learning" | "machine learning" | Exact phrase search |
golang -old | golang AND NOT old | Must have "golang", must not have "old" |
python OR ruby | (python OR ruby) | Either "python" or "ruby" |
"deep learning" tutorial | "deep learning" AND tutorial | Phrase and term |
golang +(tutorial OR guide) | golang AND (tutorial OR guide) | "golang" AND either "tutorial" or "guide" |
AI machine -learning | AI AND machine AND NOT learning | Must have "AI" and "machine", exclude "learning" |
機械学習 チュートリアル | 機械学習 AND チュートリアル | Full-width space delimiter |
😀 tutorial -😢 | 😀 AND tutorial AND NOT 😢 | Emoji search (4-byte UTF-8) |
Simplified API (Backward Compatible)
For simple use cases without OR/grouping:
auto simplified = SimplifySearchExpression("+golang +tutorial -old");
if (simplified) {
// simplified->main_term == "golang"
// simplified->and_terms contains "tutorial"
// simplified->not_terms contains "old"
}Note: Complex expressions with OR and parentheses will lose their semantic meaning when using SimplifySearchExpression().
C API
Basic Usage
#include <mygramdb/mygramclient_c.h>
#include <stdio.h>
int main() {
// Configure client
MygramClientConfigV2_C config = {
.struct_size = sizeof(config),
.version = MYGRAMCLIENT_CONFIG_V2_VERSION,
.host = "localhost",
.port = 11016,
.timeout_ms = 5000,
.recv_buffer_size = 65536,
.unix_socket_path = NULL,
.dump_save_timeout_ms = 600000,
.max_response_bytes = 64ULL * 1024ULL * 1024ULL,
.connect_timeout_ms = 1000
};
// Create client
MygramClient_C* client = mygramclient_create_v2(&config);
if (!client) {
fprintf(stderr, "Failed to create client\n");
return 1;
}
// Connect
if (mygramclient_connect(client) != 0) {
fprintf(stderr, "Connection failed: %s\n",
mygramclient_get_last_error(client));
mygramclient_destroy(client);
return 1;
}
// Search
MygramSearchResult_C* result = NULL;
if (mygramclient_search(client, "app_db.articles", "hello", 100, 0, &result) == 0) {
printf("Found %llu results (showing %zu):\n",
result->total_count, result->count);
for (size_t i = 0; i < result->count; i++) {
printf(" - %s\n", result->primary_keys[i]);
}
mygramclient_free_search_result(result);
} else {
fprintf(stderr, "Search failed: %s\n",
mygramclient_get_last_error(client));
}
// Cleanup
mygramclient_disconnect(client);
mygramclient_destroy(client);
return 0;
}Compiling C Programs
cc -std=c11 myapp.c $(pkg-config --cflags --libs mygramclient) -o myappAdministrative authentication
In v1.10, administrative authentication is scoped to one connected MygramClient_C. Before administrative methods, send AUTH with mygramclient_send_command and require the exact OK AUTHENTICATED response; a 0 return only means a raw response was received and clears the last error. Use mygramclient_get_last_error only when the return value is nonzero. Repeat it after reconnecting. This applies to the C equivalents of GetConfig/CONFIG, SetVariable, ShowVariables, Cache*, Optimize, Sync*, Dump*, Save, Load, replication, and EnableDebug/DisableDebug, but not to search, count, get, or facet operations. Free the response with mygramclient_free_string and never log the token or command.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* token = getenv("MYGRAM_API_ADMIN_TOKEN");
if (token == NULL) {
fprintf(stderr, "MYGRAM_API_ADMIN_TOKEN is not set\n");
return 1;
}
char* auth_command = malloc(strlen("AUTH ") + strlen(token) + 1);
char* response = NULL;
if (auth_command == NULL) return 1;
sprintf(auth_command, "AUTH %s", token);
int rc = mygramclient_send_command(client, auth_command, &response);
free(auth_command);
if (rc != 0) {
mygramclient_free_string(response);
fprintf(stderr, "Admin authentication request failed: %s\n",
mygramclient_get_last_error(client));
return 1;
}
int authenticated = response != NULL && strcmp(response, "OK AUTHENTICATED") == 0;
mygramclient_free_string(response);
if (!authenticated) {
fprintf(stderr, "Admin authentication was rejected\n");
return 1;
}Advanced Search (C API)
For new code, use MygramSearchOptions_C and mygramclient_search_with_options. Zero-initialize the structure and set struct_size; its typed filters support =, !=, >, >=, <, and <=, and fuzzy_distance is 1 or 2. Set query_mode to MYGRAM_QUERY_BOOLEAN to send a boolean expression instead of a literal query.
MygramClientConfigV2_C is the preferred configuration for new C programs. timeout_ms is the ordinary operation deadline; connect_timeout_ms is the connection deadline and falls back to timeout_ms when zero. max_response_bytes limits a response frame and defaults to 64 MiB when zero. The dedicated dump_*_timeout_ms and optimize_timeout_ms fields use the client defaults when zero or unavailable in an older structure. Set unix_socket_path to use a Unix socket; it takes precedence over TCP host and port.
Both MygramClientConfigV2_C and MygramSearchOptions_C are append-only, size-aware structures. Set struct_size to the size your caller knows. The library ignores later fields and supplies their defaults, which lets an older binary use a newer library safely. V2 configurations also set version to MYGRAMCLIENT_CONFIG_V2_VERSION. The legacy MygramClientConfig_C and mygramclient_create() remain available for ABI compatibility.
MygramFilter_C filters[] = {
{.key = "status", .op = MYGRAM_FILTER_EQ, .value = "active"},
};
MygramSearchOptions_C options = {0};
options.struct_size = sizeof(options);
options.limit = 50;
options.offset = 20;
options.filters = filters;
options.filter_count = 1;
options.query_mode = MYGRAM_QUERY_BOOLEAN;
MygramSearchResultWithHighlights_C* result = NULL;
if (mygramclient_search_with_options(client, "app_db.articles",
"technology OR programming", &options, &result) == 0) {
mygramclient_free_search_result_with_highlights(result);
}Use mygramclient_facet_paged(client, table, column, query, limit, offset, &result) for a facet page. Its result->total_count is the total number of distinct values before pagination; release it with mygramclient_free_facet_result.
C expression diagnostics
The C parser and converter have _ex variants for errors that need to be shown to a user. They initialize output pointers to NULL; on failure, diagnostic receives an allocated message when available. Release every returned string with mygramclient_free_string.
MygramParsedExpression_C* parsed = NULL;
char* diagnostic = NULL;
if (mygramclient_parse_search_expression_ex("+", &parsed, &diagnostic) != 0) {
fprintf(stderr, "%s\n", diagnostic ? diagnostic : "invalid expression");
}
mygramclient_free_parsed_expression(parsed);
mygramclient_free_string(diagnostic);
char* raw_query = NULL;
diagnostic = NULL;
if (mygramclient_convert_search_expression_ex("go OR rust", &raw_query,
&diagnostic) == 0) {
/* raw_query is valid input to mygramclient_search_raw(). */
}
mygramclient_free_string(raw_query);
mygramclient_free_string(diagnostic);Node.js Bindings Example
Using node-gyp with the C API:
// binding.gyp
{
"targets": [{
"target_name": "mygramdb",
"sources": [ "src/mygramdb_node.cpp" ],
"include_dirs": [
"/usr/local/include",
"<!(node -p \"require('node-addon-api').include_dir\")"
],
"libraries": [
"-L/usr/local/lib",
"-lmygramclient"
],
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ]
}]
}// src/mygramdb_node.cpp (simplified example)
#include <napi.h>
#include <mygramdb/mygramclient_c.h>
Napi::Value Search(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Get parameters
std::string table = info[0].As<Napi::String>();
std::string query = info[1].As<Napi::String>();
uint32_t limit = info[2].As<Napi::Number>().Uint32Value();
// Create and connect client
MygramClientConfig_C config{};
config.host = "localhost";
config.port = 11016;
config.timeout_ms = 5000;
config.recv_buffer_size = 65536;
MygramClient_C* client = mygramclient_create(&config);
if (mygramclient_connect(client) != 0) {
Napi::Error::New(env, mygramclient_get_last_error(client)).ThrowAsJavaScriptException();
mygramclient_destroy(client);
return env.Null();
}
// Search
MygramSearchResult_C* result = NULL;
if (mygramclient_search(client, table.c_str(), query.c_str(), limit, 0, &result) != 0) {
Napi::Error::New(env, mygramclient_get_last_error(client)).ThrowAsJavaScriptException();
mygramclient_destroy(client);
return env.Null();
}
// Convert to JavaScript array
Napi::Array jsResults = Napi::Array::New(env, result->count);
for (size_t i = 0; i < result->count; i++) {
jsResults[i] = Napi::String::New(env, result->primary_keys[i]);
}
// Cleanup
mygramclient_free_search_result(result);
mygramclient_disconnect(client);
mygramclient_destroy(client);
return jsResults;
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("search", Napi::Function::New(env, Search));
return exports;
}
NODE_API_MODULE(mygramdb, Init)API Reference
C++ API Classes
ClientConfig
host- Server hostname (default: "127.0.0.1")port- Server port (default: 11016)timeout_ms- Ordinary operation timeout (default: 5000)connect_timeout_ms- Connect timeout;0usestimeout_msrecv_buffer_size- Receive buffer size (default: 65536)max_response_bytes- Maximum response frame;0uses the 64 MiB defaultunix_socket_path- Unix socket path; when set, it takes precedence over TCPdump_save_timeout_ms,dump_load_timeout_ms,dump_verify_timeout_ms,optimize_timeout_ms- Operation-specific deadlines;0usestimeout_ms
SearchResponse
results- Vector of SearchResulttotal_count- Total matching documentsdebug- Optional debug information
Error
code()- Typedmygram::utils::ErrorCode; client failures use the 7000 rangemessage()andcontext()- Error text and optional contextto_string()- Formatted error text including the numeric code
When the server responds with ERROR <code> ..., the client preserves a parseable numeric code in the typed error. A response without a numeric token is reported as kClientServerError.
C API Functions
See mygramclient_c.h for full function documentation.
Key functions:
mygramclient_create_v2()- Create a client with the size/versioned configurationmygramclient_connect()- Connect to servermygramclient_search()- Simple searchmygramclient_search_advanced()- Advanced search with filtersmygramclient_search_with_options()- Typed filter, fuzzy, highlight, sort, and pagination searchmygramclient_count()- Count matchesmygramclient_get()- Get document by keymygramclient_free_*()- Free result structures
Thread Safety
MygramClient serializes concurrent commands on its single TCP connection, so it is safe to share for correctness but executes one request at a time. Use separate instances when you need more throughput. Do not call Disconnect() while another thread has a request in flight.
Error Handling
C++ API
Functions return mygram::utils::Expected<T, Error>. Check the result before accessing it:
auto result = client.Search(...);
if (!result) {
const auto& error = result.error();
std::cerr << error.to_string() << "\n";
if (error.code() == mygram::utils::ErrorCode::kClientTimeout) {
// Reconnect or apply the application's retry policy.
}
} else {
const auto& resp = *result;
// Use resp
}C API
Functions return 0 on success and -1 on error. Use mygramclient_get_last_error() for the message and mygramclient_get_last_error_code() for the numeric MygramDB error code. A successful operation clears the last error state.
License
MIT License (see the LICENSE file)
See Also
- Protocol Reference - TCP command reference
- Query Syntax Guide - Search query grammar
- HTTP API Guide - RESTful JSON API