From 04ecd122c15fb4740ffc2ae315c422a1862b0b6d Mon Sep 17 00:00:00 2001 From: Joshua Napoli Date: Mon, 8 Dec 2025 18:25:42 -0500 Subject: [PATCH 1/6] chore: document use with pandas DataFrame --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66405bb..096db4f 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ mygroup/myedge/compressor01/motor/power_kw ### Metric Data -The main content for a metric is a set of points where the metric value changed. These are returned as a Pandas Dataframe with columns for name, time, value_double, value_string. +The main content for a metric is a set of points where the metric value changed. These are returned with columns for name, time, value_double, value_string. To get all of the value changes for all metrics at 10am on 2025-05-14, run: @@ -114,6 +114,17 @@ Example output: [46257 rows x 4 columns] ``` +#### Pandas Data Frames + +Use the `get_metric_arrow` function to efficiently load data into a pandas DataFrame like this: + +```python +import pandas as pd +import pyarrow as pa + +reader = pa.ipc.open_file(cvec.get_metric_arrow(names=["tag1", "tag2"])) +df = reader.read_pandas() +``` ### Adding Metric Data From 300816c4b8e0a1ec766a33e23d87c040930102e1 Mon Sep 17 00:00:00 2001 From: Joshua Napoli Date: Mon, 8 Dec 2025 18:34:40 -0500 Subject: [PATCH 2/6] chore: document the EAV functions --- README.md | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 096db4f..b013619 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ import cvec from datetime import datetime ``` -Construct the CVec client. The host, tenant, and api_key can be given through parameters to the constructor or from the environment variables CVEC_HOST, and CVEC_API_KEY: +Construct the CVec client. The host and api_key can be given through parameters to the constructor or from the environment variables CVEC_HOST and CVEC_API_KEY: ``` cvec = cvec.CVec() @@ -209,9 +209,9 @@ The script automatically: The SDK provides an API client class named `CVec` with the following functions. -## `__init__(?host, ?tenant, ?api_key, ?default_start_at, ?default_end_at)` +## `__init__(?host, ?api_key, ?default_start_at, ?default_end_at)` -Setup the SDK with the given host and API Key. The host and API key are loaded from environment variables CVEC_HOST, CVEC_API_KEY, if they are not given as arguments to the constructor. The `default_start_at` and `default_end_at` can provide a default query time interval for API methods. +Setup the SDK with the given host and API Key. The host and API key are loaded from environment variables CVEC_HOST and CVEC_API_KEY if they are not given as arguments to the constructor. The tenant ID is automatically fetched from the host's `/config` endpoint. The `default_start_at` and `default_end_at` can provide a default query time interval for API methods. ## `get_spans(name, ?start_at, ?end_at, ?limit)` @@ -272,3 +272,114 @@ Fetch actual data values from modeling metrics within a time range in Apache Arr - `end_at`: Optional end time for the query (uses class default if not specified) Returns Arrow IPC format data that can be read using `pyarrow.ipc.open_file()`. + +## `get_eav_tables()` + +Get all EAV (Entity-Attribute-Value) tables for the tenant. EAV tables store semi-structured data where each row represents an entity with flexible attributes. + +Returns a list of `EAVTable` objects, each containing: +- `id`: The table's UUID +- `tenant_id`: The tenant ID +- `name`: Human-readable table name +- `created_at`: When the table was created +- `updated_at`: When the table was last updated + +Example: +```python +tables = cvec.get_eav_tables() +for table in tables: + print(f"{table.name} (id: {table.id})") +``` + +## `get_eav_columns(table_id)` + +Get all columns for a specific EAV table. + +- `table_id`: The UUID of the EAV table + +Returns a list of `EAVColumn` objects, each containing: +- `eav_table_id`: The parent table's UUID +- `eav_column_id`: The column's ID (used for queries) +- `name`: Human-readable column name +- `type`: Data type (`"number"`, `"string"`, or `"boolean"`) +- `created_at`: When the column was created + +Example: +```python +columns = cvec.get_eav_columns("00000000-0000-0000-0000-000000000000") +for column in columns: + print(f" {column.name} ({column.type}, id: {column.eav_column_id})") +``` + +## `select_from_eav(table_name, ?column_names, ?filters)` + +Query pivoted data from EAV tables using human-readable names. This is the recommended method for most use cases as it allows you to work with table and column names instead of UUIDs. + +- `table_name`: Name of the EAV table to query +- `column_names`: Optional list of column names to include. If `None`, all columns are returned. +- `filters`: Optional list of `EAVFilter` objects to filter results + +Each `EAVFilter` must use `column_name` and can specify: +- `column_name`: The column name to filter on (required) +- `numeric_min`: Minimum numeric value (inclusive) +- `numeric_max`: Maximum numeric value (exclusive) +- `string_value`: Exact string value to match +- `boolean_value`: Boolean value to match + +Returns a list of dictionaries (maximum 1000 rows), each representing a row with an `id` field and fields for each requested column. + +Example: +```python +from cvec import CVec, EAVFilter + +# Query with filters +filters = [ + EAVFilter(column_name="Weight", numeric_min=100, numeric_max=200), + EAVFilter(column_name="Status", string_value="ACTIVE"), +] + +rows = cvec.select_from_eav( + table_name="Production Data", + column_names=["Date", "Weight", "Status"], + filters=filters, +) + +for row in rows: + print(f"ID: {row['id']}, Date: {row['Date']}, Weight: {row['Weight']}") +``` + +## `select_from_eav_id(table_id, ?column_ids, ?filters)` + +Query pivoted data from EAV tables using table and column IDs directly. This is a lower-level method for cases where you already have the UUIDs and want to avoid name lookups. + +- `table_id`: UUID of the EAV table to query +- `column_ids`: Optional list of column IDs to include. If `None`, all columns are returned. +- `filters`: Optional list of `EAVFilter` objects to filter results + +Each `EAVFilter` must use `column_id` and can specify: +- `column_id`: The column ID to filter on (required) +- `numeric_min`: Minimum numeric value (inclusive) +- `numeric_max`: Maximum numeric value (exclusive) +- `string_value`: Exact string value to match +- `boolean_value`: Boolean value to match + +Returns a list of dictionaries (maximum 1000 rows), each representing a row with an `id` field and fields for each column ID. + +Example: +```python +from cvec import EAVFilter + +filters = [ + EAVFilter(column_id="MTnaC", numeric_min=100, numeric_max=200), + EAVFilter(column_id="z09PL", string_value="ACTIVE"), +] + +rows = cvec.select_from_eav_id( + table_id="00000000-0000-0000-0000-000000000000", + column_ids=["abcd", "efgh", "ijkl"], + filters=filters, +) + +for row in rows: + print(f"ID: {row['id']}, Values: {row}") +``` From 7f7c399fcb8a64781cd713d2ce20971a39355f29 Mon Sep 17 00:00:00 2001 From: Joshua Napoli Date: Mon, 8 Dec 2025 18:40:56 -0500 Subject: [PATCH 3/6] fix: print complete row Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b013619..719d7d5 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ rows = cvec.select_from_eav( ) for row in rows: - print(f"ID: {row['id']}, Date: {row['Date']}, Weight: {row['Weight']}") + print(row) ``` ## `select_from_eav_id(table_id, ?column_ids, ?filters)` From 414c2f519ceb1a1337b0fa15905b68b9e2630336 Mon Sep 17 00:00:00 2001 From: Joshua Napoli Date: Mon, 8 Dec 2025 18:42:24 -0500 Subject: [PATCH 4/6] fix: align filter and column IDs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 719d7d5..eaa1a7f 100644 --- a/README.md +++ b/README.md @@ -370,8 +370,8 @@ Example: from cvec import EAVFilter filters = [ - EAVFilter(column_id="MTnaC", numeric_min=100, numeric_max=200), - EAVFilter(column_id="z09PL", string_value="ACTIVE"), + EAVFilter(column_id="abcd", numeric_min=100, numeric_max=200), + EAVFilter(column_id="efgh", string_value="ACTIVE"), ] rows = cvec.select_from_eav_id( From b134adf694253f4963ff78aa8be20d694bb6754a Mon Sep 17 00:00:00 2001 From: Joshua Napoli Date: Mon, 8 Dec 2025 22:20:16 -0500 Subject: [PATCH 5/6] fix: clarify list length limit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eaa1a7f..04e57ce 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,7 @@ Each `EAVFilter` must use `column_id` and can specify: - `string_value`: Exact string value to match - `boolean_value`: Boolean value to match -Returns a list of dictionaries (maximum 1000 rows), each representing a row with an `id` field and fields for each column ID. +Returns a list of dictionaries (up to 1000), each representing a row with an `id` field and fields for each column ID. Example: ```python From 52941928f96993de994ca7c1fff991ddb556aca8 Mon Sep 17 00:00:00 2001 From: Joshua Napoli Date: Mon, 8 Dec 2025 22:21:59 -0500 Subject: [PATCH 6/6] fix: clarify description of row fields --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04e57ce..0b058d1 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,7 @@ Each `EAVFilter` must use `column_id` and can specify: - `string_value`: Exact string value to match - `boolean_value`: Boolean value to match -Returns a list of dictionaries (up to 1000), each representing a row with an `id` field and fields for each column ID. +Returns a list of dictionaries (up to 1000), each representing a row. Each row has a field for each column plus an `id` (the "row ID"). Example: ```python