# networkx-query
[![Coverage Status](https://img.shields.io/coveralls/geronimo-iia/networkx-query/master.svg)](https://coveralls.io/r/geronimo-iia/networkx-query)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/fe669a02b4aa46b5b1faf619ba2bf382)](https://www.codacy.com/app/geronimo-iia/networkx-query?utm_source=github.com&utm_medium=referral&utm_content=geronimo-iia/networkx-query&utm_campaign=Badge_Grade)[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/geronimo-iia/networkx-query.svg)](https://scrutinizer-ci.com/g/geronimo-iia/networkx-query/?branch=master)
[![PyPI Version](https://img.shields.io/pypi/v/networkx-query.svg)](https://pypi.org/project/networkx-query)
[![PyPI License](https://img.shields.io/pypi/l/networkx-query.svg)](https://pypi.org/project/networkx-query)
Versions following [Semantic Versioning](https://semver.org/)
## Overview
NetworkX Query Tool
See [documentation](https://geronimo-iia.github.io/networkx-query).
## Installation
Install this library directly into an activated virtual environment:
```text
$ pip install networkx-query
```
or add it to your [Poetry](https://poetry.eustace.io/) project:
```text
$ poetry add networkx-query
```
## Usage
### Searching nodes
```python
import networkx as nx
from networkx_query import search_nodes, search_edges
g = nx.DiGraph()
g.add_node(1, product="chocolate")
g.add_node(2, product="milk")
g.add_node(3, product="coat")
g.add_edge(1, 2, action="shake")
g.add_edge(3, 2, action="produce")
for node_id in search_nodes(g, {"==": [("product",), "chocolate"]}):
print(node_id)
>> 1
```
### Searching edges
```python
for edge_id in search_edges(g, {"eq": [("action",), "produce"]}):
print(edge_id)
>> (3, 2)
```
### Searching direct relation ship
With ```search_direct_relationships``` you can made a query which filter edges on their :
- source node attributes
- edge attributes
- target node attributes
With this graph:
```python
import networkx as nx
from networkx_query import search_direct_relationships
g = nx.DiGraph()
for i in range(30):
g.add_node(i, data=i)
for i in range(10, 30):
g.add_edge(i - 10, i, data=i)
```
We can filtering all edges with source node with data < 3:
```python
list(search_direct_relationships(graph=g, source={"lt": ["data", 3]}))
[(0, 10), (1, 11), (2, 12)]
```
We can filtering all edges with:
- source node with data < 8
- edge with data > 15
```python
list(search_direct_relationships(graph=g, source={"lt": ["data", 8]}, edge={"gt": ["data", 15]}))
>> [(6, 16), (7, 17)]
```
We can filtering all edges with:
- source node with data > 9
- edge with data > 15
- target node with data < 22
```python
search_direct_relationships(
graph=g, source={"gt": ["data", 9]}, edge={"gt": ["data", 15]}, target={'lt': ["data", 22]}
)
)
>> [(10, 20), (11, 21)]
```
### search_relationships
With :
```python
g = nx.DiGraph()
g.add_node(1, product="a")
g.add_node(2, product="b")
g.add_node(3, product="c")
g.add_node(4, product="d")
g.add_edge(1, 2)
g.add_edge(1, 3, weight=2)
g.add_edge(1, 4)
g.add_edge(2, 4)
g.add_edge(3, 4)
```
You could find all path with multiple constraints:
```python
list(search_relationships(
g,
{"eq": [("product",), "a"]},
PathCriteria(target={"eq": [("product",), "b"]}),
PathCriteria(target={"eq": [("product",), "d"]}),
))
# output: [[1, 2, 4]]
list(search_relationships(g, {"eq": [("product",), "a"]}, PathCriteria(target={"eq": [("product",), "c"]})))
# outptu: [[1, 3]]
```
or something more complex:
```python
g.add_node(5, product="d")
g.add_node(6, product="d")
g.add_node(7, product="a")
g.add_node(8, product="a")
g.add_edge(7, 5, weight=2)
g.add_edge(7, 6, weight=2)
g.add_edge(8, 5, weight=2)
list(
search_relationships(
g,
{"eq": [("product",), "a"]}, # node 1, 7, 8
PathCriteria(
target={"eq": [("product",), "d"]}, edge={"eq": [("weight",), 2]}
), # edge 1-3, 7-5, 7-6, 8-5 node 4, 5, 6 -> no 1, 3, 4
)
)
# output: [[7, 5], [7, 6], [8, 5]]
list(
search_relationships(
g,
{"eq": [("product",), "a"]}, # node 1, 7, 8
PathCriteria(target={}, edge={"eq": [("weight",), 2]}), # edge 1-3, 7-5, 7-6, 8-5
PathCriteria(target={"eq": [("product",), "d"]}), # node 4, 5, 6 -> no 1, 3, 4
)
)
# output: [[1, 3, 4]]
```
Note the usage of `PathCriteria(target={}, ..` to define a constraint based only on edge. `{}` act as a wildcard.
## API
Actually, we have:
- [search_edges](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_edges)
- [search_nodes](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_nodes)
- [search_direct_relationships](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_direct_relationships)
- [search_relationships](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_relationships)
All this function are based on [prepare_query](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.prepare_query) which return an Evaluator.
Quickly, ```Evaluator``` are function with this signature: (context) -> bool, and ```Context``` is a dictionary like structure (with in and [] methods, and support __contains__ or (__iter__ and __getitem__))
With networkX, node and edge attributes are dictionary like, so implementation of this three methods are very simple.
## Query language
We define a little json query language like [json-query-language](https://github.com/clue/json-query-language/blob/master/SYNTAX.md)
against nodes or edges attributes.
### Expressions
Main expression syntax turn around this:
```
{
operator_name : parameters
}
```
### Basic matching expression
Test if a node/edge has an attribute named "my_property":
```
{
"has" : "my_property"
}
```
Test if a node/edge has an attribute product : { "definition": { "name": xxx }} with xxx equals to "chocolate".
```
{
"eq" : [ ("product", "definition", "name"), "chocolate"]
}
```
The tuple ```("product", "definition", "name")``` is a path in attribut dictionnary.
A Path is a single string or a tuple of string which represente a path in a tree (here a dictionary).
We support this operators:
| Name | Alias | Parameters | Description |
| -------- | :---: | --------------- | ----------------------------------------------------------------------------- |
| has | | Path | Check if path exists in context. |
| contains | | Path, str | Check if an attribut path exists and contains specified value. |
| eq | `==` | Path, Any | Check if an attribut path exists and equals specified value. |
| neq | `!=` | Path, Any | Check if an attribut path did not exists or not equals specified value. |
| gt | `>` | Path, Any | Check if an attribut path exists and greather that specified value. |
| lt | `<` | Path, Any | Check if an attribut path exists and lower that specified value. |
| gte | `>=` | Path, Any | Check if an attribut path exists and greather or equals that specified value. |
| lte | `<=` | Path, Any | Check if an attribut path exists and lower or equals that specified value. |
| in | `:=` | Path, List[Any] | Check if an attribut path exists and attribut value in specified values. |
### Boolean composition of matching expression
We support this operators:
| Name | Alias | Parameters | Description |
| ---- | :---: | ------------- | -------------- |
| and | `&&` | list of query | And operator. |
| or | \|\| | list of query | Or operator. |
| xor | | list of query | xor operator. |
| nxor | | list of query | nxor operator. |
| not | `!` | query | Not operator. |
By default, a list of expressions is equivalent of an "AND" of this expressions.
Example:
```
{
'not': {
'has': ['group']
},
'has': 'application',
'eq': [('_link', 'other', 'weight'), 2]
}
```
is equivalent to:
```
{
'and': [
{
'not': [
{
'has': ['group']
}
]
},
{
'has': ['application']
},
{
'eq': [('_link', 'other', 'weight'), 2]
}
]
}
```
## Wished Features
- add projection expression (a return like statement)
- add join relation ship
Raw data
{
"_id": null,
"home_page": "https://pypi.org/project/networkx_query",
"name": "networkx-query",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<3.12",
"maintainer_email": "",
"keywords": "networkx,graph,query",
"author": "Jerome Guibert",
"author_email": "jguibert@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/56/85/e88a264ab2d2d9cf34d471b2e16552e79f73953c496a672414497ee9d6a9/networkx_query-2.0.0.tar.gz",
"platform": null,
"description": "# networkx-query\n\n[![Coverage Status](https://img.shields.io/coveralls/geronimo-iia/networkx-query/master.svg)](https://coveralls.io/r/geronimo-iia/networkx-query)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/fe669a02b4aa46b5b1faf619ba2bf382)](https://www.codacy.com/app/geronimo-iia/networkx-query?utm_source=github.com&utm_medium=referral&utm_content=geronimo-iia/networkx-query&utm_campaign=Badge_Grade)[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/geronimo-iia/networkx-query.svg)](https://scrutinizer-ci.com/g/geronimo-iia/networkx-query/?branch=master)\n[![PyPI Version](https://img.shields.io/pypi/v/networkx-query.svg)](https://pypi.org/project/networkx-query)\n[![PyPI License](https://img.shields.io/pypi/l/networkx-query.svg)](https://pypi.org/project/networkx-query)\n\nVersions following [Semantic Versioning](https://semver.org/)\n\n## Overview\n\nNetworkX Query Tool\n\nSee [documentation](https://geronimo-iia.github.io/networkx-query).\n\n\n## Installation\n\nInstall this library directly into an activated virtual environment:\n\n```text\n$ pip install networkx-query\n```\n\nor add it to your [Poetry](https://poetry.eustace.io/) project:\n\n```text\n$ poetry add networkx-query\n```\n\n## Usage\n\n### Searching nodes\n\n```python\nimport networkx as nx\nfrom networkx_query import search_nodes, search_edges\n\ng = nx.DiGraph()\ng.add_node(1, product=\"chocolate\")\ng.add_node(2, product=\"milk\")\ng.add_node(3, product=\"coat\")\ng.add_edge(1, 2, action=\"shake\")\ng.add_edge(3, 2, action=\"produce\")\n\n\nfor node_id in search_nodes(g, {\"==\": [(\"product\",), \"chocolate\"]}):\n print(node_id)\n\n>> 1\n```\n\n### Searching edges\n\n```python\nfor edge_id in search_edges(g, {\"eq\": [(\"action\",), \"produce\"]}):\n print(edge_id)\n\n>> (3, 2)\n```\n\n### Searching direct relation ship\n\nWith ```search_direct_relationships``` you can made a query which filter edges on their :\n - source node attributes\n - edge attributes\n - target node attributes\n\nWith this graph:\n\n```python\nimport networkx as nx\nfrom networkx_query import search_direct_relationships\n\ng = nx.DiGraph()\nfor i in range(30):\n g.add_node(i, data=i)\n\nfor i in range(10, 30):\n g.add_edge(i - 10, i, data=i)\n```\n\nWe can filtering all edges with source node with data < 3:\n\n```python\nlist(search_direct_relationships(graph=g, source={\"lt\": [\"data\", 3]}))\n\n[(0, 10), (1, 11), (2, 12)]\n```\n\n\nWe can filtering all edges with:\n - source node with data < 8\n - edge with data > 15\n\n```python\nlist(search_direct_relationships(graph=g, source={\"lt\": [\"data\", 8]}, edge={\"gt\": [\"data\", 15]}))\n\n>> [(6, 16), (7, 17)]\n```\n\nWe can filtering all edges with:\n - source node with data > 9\n - edge with data > 15\n - target node with data < 22\n\n```python\nsearch_direct_relationships(\n graph=g, source={\"gt\": [\"data\", 9]}, edge={\"gt\": [\"data\", 15]}, target={'lt': [\"data\", 22]}\n )\n )\n\n>> [(10, 20), (11, 21)]\n```\n\n\n\n### search_relationships\n\nWith :\n\n```python\n g = nx.DiGraph()\n g.add_node(1, product=\"a\")\n g.add_node(2, product=\"b\")\n g.add_node(3, product=\"c\")\n g.add_node(4, product=\"d\")\n\n g.add_edge(1, 2)\n g.add_edge(1, 3, weight=2)\n g.add_edge(1, 4)\n\n g.add_edge(2, 4)\n g.add_edge(3, 4)\n```\n\n\nYou could find all path with multiple constraints:\n\n```python\n list(search_relationships(\n g,\n {\"eq\": [(\"product\",), \"a\"]},\n PathCriteria(target={\"eq\": [(\"product\",), \"b\"]}),\n PathCriteria(target={\"eq\": [(\"product\",), \"d\"]}),\n )) \n # output: [[1, 2, 4]]\n\n list(search_relationships(g, {\"eq\": [(\"product\",), \"a\"]}, PathCriteria(target={\"eq\": [(\"product\",), \"c\"]})))\n # outptu: [[1, 3]]\n```\n\nor something more complex:\n\n```python\n\n g.add_node(5, product=\"d\")\n g.add_node(6, product=\"d\")\n g.add_node(7, product=\"a\")\n g.add_node(8, product=\"a\")\n\n g.add_edge(7, 5, weight=2)\n g.add_edge(7, 6, weight=2)\n g.add_edge(8, 5, weight=2)\n\n list(\n search_relationships(\n g,\n {\"eq\": [(\"product\",), \"a\"]}, # node 1, 7, 8\n PathCriteria(\n target={\"eq\": [(\"product\",), \"d\"]}, edge={\"eq\": [(\"weight\",), 2]}\n ), # edge 1-3, 7-5, 7-6, 8-5 node 4, 5, 6 -> no 1, 3, 4\n )\n )\n # output: [[7, 5], [7, 6], [8, 5]]\n\n list(\n search_relationships(\n g,\n {\"eq\": [(\"product\",), \"a\"]}, # node 1, 7, 8\n PathCriteria(target={}, edge={\"eq\": [(\"weight\",), 2]}), # edge 1-3, 7-5, 7-6, 8-5\n PathCriteria(target={\"eq\": [(\"product\",), \"d\"]}), # node 4, 5, 6 -> no 1, 3, 4\n )\n )\n # output: [[1, 3, 4]]\n```\n\nNote the usage of `PathCriteria(target={}, ..` to define a constraint based only on edge. `{}` act as a wildcard.\n\n## API\n\nActually, we have:\n\n- [search_edges](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_edges)\n- [search_nodes](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_nodes) \n- [search_direct_relationships](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_direct_relationships) \n- [search_relationships](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.search_relationships) \n\n\nAll this function are based on [prepare_query](https://geronimo-iia.github.io/networkx-query/reference/#networkx_query.prepare_query) which return an Evaluator.\n\nQuickly, ```Evaluator``` are function with this signature: (context) -> bool, and ```Context``` is a dictionary like structure (with in and [] methods, and support __contains__ or (__iter__ and __getitem__))\nWith networkX, node and edge attributes are dictionary like, so implementation of this three methods are very simple.\n\n\n\n## Query language\n\nWe define a little json query language like [json-query-language](https://github.com/clue/json-query-language/blob/master/SYNTAX.md) \nagainst nodes or edges attributes.\n\n\n### Expressions\n\nMain expression syntax turn around this:\n\n```\n{\n operator_name : parameters\n}\n```\n\n### Basic matching expression\n\nTest if a node/edge has an attribute named \"my_property\":\n```\n{\n \"has\" : \"my_property\"\n}\n```\n\n\nTest if a node/edge has an attribute product : { \"definition\": { \"name\": xxx }} with xxx equals to \"chocolate\".\n```\n{\n \"eq\" : [ (\"product\", \"definition\", \"name\"), \"chocolate\"]\n}\n```\n\nThe tuple ```(\"product\", \"definition\", \"name\")``` is a path in attribut dictionnary.\nA Path is a single string or a tuple of string which represente a path in a tree (here a dictionary).\n\nWe support this operators:\n\n| Name | Alias | Parameters | Description |\n| -------- | :---: | --------------- | ----------------------------------------------------------------------------- |\n| has | | Path | Check if path exists in context. |\n| contains | | Path, str | Check if an attribut path exists and contains specified value. |\n| eq | `==` | Path, Any | Check if an attribut path exists and equals specified value. |\n| neq | `!=` | Path, Any | Check if an attribut path did not exists or not equals specified value. |\n| gt | `>` | Path, Any | Check if an attribut path exists and greather that specified value. |\n| lt | `<` | Path, Any | Check if an attribut path exists and lower that specified value. |\n| gte | `>=` | Path, Any | Check if an attribut path exists and greather or equals that specified value. |\n| lte | `<=` | Path, Any | Check if an attribut path exists and lower or equals that specified value. |\n| in | `:=` | Path, List[Any] | Check if an attribut path exists and attribut value in specified values. |\n\n\n### Boolean composition of matching expression\n\nWe support this operators:\n\n| Name | Alias | Parameters | Description |\n| ---- | :---: | ------------- | -------------- |\n| and | `&&` | list of query | And operator. |\n| or | \\|\\| | list of query | Or operator. |\n| xor | | list of query | xor operator. |\n| nxor | | list of query | nxor operator. |\n| not | `!` | query | Not operator. |\n\n\nBy default, a list of expressions is equivalent of an \"AND\" of this expressions.\n\nExample:\n```\n{\n 'not': {\n 'has': ['group']\n },\n 'has': 'application',\n 'eq': [('_link', 'other', 'weight'), 2]\n}\n```\nis equivalent to:\n\n```\n{\n 'and': [\n {\n 'not': [\n {\n 'has': ['group']\n }\n ]\n },\n {\n 'has': ['application']\n },\n {\n 'eq': [('_link', 'other', 'weight'), 2]\n }\n ]\n}\n```\n\n## Wished Features\n\n- add projection expression (a return like statement)\n- add join relation ship \n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "NetworkX Query Tool",
"version": "2.0.0",
"project_urls": {
"Documentation": "https://geronimo-iia.github.io/networkx-query/",
"Homepage": "https://pypi.org/project/networkx_query",
"Repository": "https://github.com/geronimo-iia/networkx-query"
},
"split_keywords": [
"networkx",
"graph",
"query"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "85ea474361cf794dd8addd904ede666a1fdbe72cde5d9120bd2be944ac579d8f",
"md5": "5285cc45b35a8b124953c254f0ef295e",
"sha256": "05cb1a8fd655abbb03d82f794fa81054d480424d6a1d16edbb240b82b56e66f2"
},
"downloads": -1,
"filename": "networkx_query-2.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5285cc45b35a8b124953c254f0ef295e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<3.12",
"size": 12296,
"upload_time": "2023-06-02T14:23:06",
"upload_time_iso_8601": "2023-06-02T14:23:06.165607Z",
"url": "https://files.pythonhosted.org/packages/85/ea/474361cf794dd8addd904ede666a1fdbe72cde5d9120bd2be944ac579d8f/networkx_query-2.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "5685e88a264ab2d2d9cf34d471b2e16552e79f73953c496a672414497ee9d6a9",
"md5": "3eb00c7f9150e23d03513bb5472c0a9c",
"sha256": "f3b98f05460b4a9a7a305857c410712bdc8fb33ceb20cfbf959822853b4be1a0"
},
"downloads": -1,
"filename": "networkx_query-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "3eb00c7f9150e23d03513bb5472c0a9c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<3.12",
"size": 12923,
"upload_time": "2023-06-02T14:23:07",
"upload_time_iso_8601": "2023-06-02T14:23:07.339569Z",
"url": "https://files.pythonhosted.org/packages/56/85/e88a264ab2d2d9cf34d471b2e16552e79f73953c496a672414497ee9d6a9/networkx_query-2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-06-02 14:23:07",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "geronimo-iia",
"github_project": "networkx-query",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "networkx-query"
}