A Nessie plugin for filtering and searching graphs — narrow any graph down to a subgraph by applying attribute-based filter expressions, a free-text search, or both at once.
This plugin operates on an already-loaded Nessie Graph object and returns a filtered subgraph. It:
- Applies one or more
FilterExpressionrules against node attributes (e.g.salary > 50000,status == "active") - Optionally runs a free-text search across all attribute names and values
- Filters are applied in order — each step narrows down the surviving nodes
- Retains only the edges whose both endpoints are still in the subgraph
- Registers automatically with the Nessie plugin system via a Python entry point
- Python 3.9 or higher
nessie-api>= 0.1.0
pip install nessie-graph-manipulation-pluginOr install from source:
git clone https://github.com/Nessie-org/nessie-graph-manipulation-plugin.git
cd nessie-graph-manipulation-plugin
pip install -e .The plugin registers itself under the name "GraphManipulationPlugin" and is automatically discovered by Nessie through the nessie_plugins entry point.
from nessie_graph_manipulation_plugin import graph_manipulation_plugin
from nessie_api.models import Action, FilterExpression, FilterOperator
plugin = graph_manipulation_plugin()
subgraph = plugin.handle(
Action("filter_graph", {
"graph": my_graph, # a nessie_api Graph object
"filters": [
FilterExpression("salary", FilterOperator.GT, 50000),
FilterExpression("status", FilterOperator.EQ, "active"),
],
"search": "engineering", # optional free-text search
}),
context=None,
)
print(subgraph)Filters nodes in a graph by attribute expressions and/or a search query, then returns a subgraph containing only the matching nodes and the edges between them.
Payload fields:
| Field | Type | Required | Description |
|---|---|---|---|
graph |
Graph |
Yes | The source Nessie graph to filter |
filters |
list[FilterExpression] |
No | Attribute filter rules (default: [], no filtering) |
search |
str |
No | Free-text query matched against attribute names and values |
A FilterExpression describes a single attribute-level condition:
FilterExpression(attr_name: str, operator: FilterOperator, value: Any)| Operator | Symbol | Example |
|---|---|---|
FilterOperator.EQ |
== |
FilterExpression("status", EQ, "active") |
FilterOperator.NEQ |
!= |
FilterExpression("role", NEQ, "intern") |
FilterOperator.LT |
< |
FilterExpression("age", LT, 30) |
FilterOperator.LTE |
<= |
FilterExpression("score", LTE, 100) |
FilterOperator.GT |
> |
FilterExpression("salary", GT, 50000) |
FilterOperator.GTE |
>= |
FilterExpression("priority", GTE, 2) |
If the filter value type does not match the stored attribute type, the plugin attempts to coerce the filter value to match. If coercion fails, a TypeError is raised with a descriptive message including the node ID and attribute name.
When a search string is provided, a node is retained only if the query (case-insensitive) appears in any attribute name or attribute value. This runs after all filters have been applied.
# Keep only nodes that match the filters AND contain "python" somewhere
Action("filter_graph", {
"graph": my_graph,
"filters": [FilterExpression("level", FilterOperator.EQ, "advanced")],
"search": "python",
})- Start with the set of all node IDs in the source graph.
- Apply each
FilterExpressionin order — each one narrows the set. - If a non-empty
searchstring is given, narrow the set further. - Copy all surviving nodes into a new
Graphwith the same name and graph type. - Copy all edges from the source graph whose source and target are both in the surviving set.
from nessie_api.models import Action, FilterExpression, FilterOperator
from nessie_graph_manipulation_plugin import graph_manipulation_plugin
plugin = graph_manipulation_plugin()
# Keep only nodes where _table == "employees"
result = plugin.handle(
Action("filter_graph", {
"graph": graph,
"filters": [FilterExpression("_table", FilterOperator.EQ, "employees")],
}),
context=None,
)# Employees in department 3 earning more than 70 000
result = plugin.handle(
Action("filter_graph", {
"graph": graph,
"filters": [
FilterExpression("_table", FilterOperator.EQ, "employees"),
FilterExpression("department_id", FilterOperator.EQ, 3),
FilterExpression("salary", FilterOperator.GT, 70000),
],
}),
context=None,
)# All nodes that mention "devops" anywhere in their attributes
result = plugin.handle(
Action("filter_graph", {
"graph": graph,
"search": "devops",
}),
context=None,
)# Active projects that reference "backend"
result = plugin.handle(
Action("filter_graph", {
"graph": graph,
"filters": [FilterExpression("status", FilterOperator.EQ, "active")],
"search": "backend",
}),
context=None,
)git clone https://github.com/Nessie-org/nessie-graph-manipulation-plugin.git
cd nessie-graph-manipulation-plugin
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"nessie-graph-manipulation-plugin/
├── src/
│ └── nessie_graph_manipulation_plugin/
│ ├── __init__.py # Exports graph_manipulation_plugin
│ └── graph_manipulation_plugin.py # Core plugin logic
├── pyproject.toml
└── README.md
Stefan Ilić — stefanilic3001@gmail.com
Issues and contributions welcome at the GitHub repository.