# pyneo4j-ogm
[![PyPI](https://img.shields.io/pypi/v/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)
[![PyPI - License](https://img.shields.io/pypi/l/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)
[`pyneo4j-ogm`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop) is a asynchronous `Object-Graph-Mapper` for [`Neo4j 5+`](https://neo4j.com/docs/) and [`Python 3.10+`](https://www.python.org/). It is inspired by [`beanie`](https://github.com/roman-right/beanie) and build on top of proven technologies like [`Pydantic 1.10+ and 2+`](https://docs.pydantic.dev/latest/) and the [`Neo4j Python Driver`](https://neo4j.com/docs/api/python-driver/current/index.html). It saves you from writing ever-repeating boilerplate queries and allows you to focus on the `stuff that actually matters`. It is designed to be simple and easy to use, but also flexible and powerful.
## 🎯 Features
[`pyneo4j-ogm`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop) has a lot to offer, including:
- [x] **Fully typed**: pyneo4j-ogm is `fully typed` out of the box.
- [x] **Powerful validation**: Since we use Pydantic under the hood, you can use it's powerful validation and serialization features without any issues.
- [x] **Focus on developer experience**: Designed to be simple to use, pyneo4j-ogm provides features for both simple queries and more `advanced use-cases` while keeping it's API as simple as possible.
- [x] **Build-in migration tooling**: Shipped with simple, yet flexible migration tooling.
- [x] **Fully asynchronous**: Completely asynchronous code, thanks to the `Neo4j Python Driver`.
- [x] **Supports Neo4j 5+**: pyneo4j-ogm supports `Neo4j 5+` and is tested against the latest version of Neo4j.
- [x] **Multi-version Pydantic support**: Both `Pydantic 1.10+` and `2+` fully supported.
## 📣 Announcements
Things to come in the future. Truly exiting stuff! If you have feature requests which you think might improve `pyneo4j-ogm`, feel free to open up a feature request.
- [ ] [MemGraph](https://memgraph.com/) support.
## 📦 Installation
Using [`pip`](https://pip.pypa.io/en/stable/):
```bash
pip install pyneo4j-ogm
```
or when using [`Poetry`](https://python-poetry.org/):
```bash
poetry add pyneo4j-ogm
```
## 🚀 Quickstart
Before we can get going, we have to take care of some things:
- We need to define our models, which will represent the nodes and relationships inside our database.
- We need a database client, which will do the actual work for us.
### Defining our data structures
Since every developer has a coffee addiction one way or another, we are going to use `Coffee` and `Developers` for this guide. So let's start by defining what our data should look like:
```python
from pyneo4j_ogm import (
NodeModel,
RelationshipModel,
RelationshipProperty,
RelationshipPropertyCardinality,
RelationshipPropertyDirection,
WithOptions,
)
from pydantic import Field
from uuid import UUID, uuid4
class Developer(NodeModel):
"""
This class represents a `Developer` node inside the graph. All interactions
with nodes of this type will be handled by this class.
"""
uid: WithOptions(UUID, unique=True) = Field(default_factory=uuid4)
name: str
age: int
coffee: RelationshipProperty["Coffee", "Consumed"] = RelationshipProperty(
target_model="Coffee",
relationship_model="Consumed",
direction=RelationshipPropertyDirection.OUTGOING,
cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,
allow_multiple=True,
)
class Settings:
# Hooks are available for all methods that interact with the database.
post_hooks = {
"coffee.connect": lambda self, *args, **kwargs: print(f"{self.name} chugged another one!")
}
class Coffee(NodeModel):
"""
This class represents a node with the labels `Beverage` and `Hot`. Notice
that the labels of this model are explicitly defined in the `Settings` class.
"""
flavor: str
sugar: bool
milk: bool
developers: RelationshipProperty["Developer", "Consumed"] = RelationshipProperty(
target_model=Developer,
relationship_model="Consumed",
direction=RelationshipPropertyDirection.INCOMING,
cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,
allow_multiple=True,
)
class Settings:
labels = {"Beverage", "Hot"}
class Consumed(RelationshipModel):
"""
Unlike the models above, this class represents a relationship between two
nodes. In this case, it represents the relationship between the `Developer`
and `Coffee` models. Like with node-models, the `Settings` class allows us to
define some configuration for this relationship.
Note that the relationship itself does not define it's start- and end-nodes,
making it reusable for other models as well.
"""
liked: bool
class Settings:
type = "CHUGGED"
```
Until now everything seems pretty standard if you have worked with other ORM's before. But if you haven't, we are going to go over what happened above:
- We defined 2 node models `Developer` and `Coffee`, and a relationship `Consumed`.
- Some models define a special inner `Settings` class. This is used to customize the behavior of our models inside the graph. More on these settings can be found [`here`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#configuration-settings).
- The `WithOptions` function has been used to define `constraints and indexes` (more about them [`here`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#manual-indexing-and-constraints)) on model properties.
### Creating a database client
In pyneo4j-ogm, the real work is done by a database client. One of these bad-boys can be created by initializing a `Pyneo4jClient` instance. But for models to work as expected, we have to let our client know that we want to use them like so:
```python
from pyneo4j_ogm import Pyneo4jClient
async def main():
# We initialize a new `Pyneo4jClient` instance and connect to the database.
client = Pyneo4jClient()
# Replace `<connection-uri-to-database>`, `<username>` and `<password>` with the
# actual values.
await client.connect(uri="<connection-uri-to-database>", auth=("<username>", "<password>"))
# To use our models for running queries later on, we have to register
# them with the client.
# **Note**: You only have to register the models that you want to use
# for queries and you can even skip this step if you want to use the
# `Pyneo4jClient` instance for running raw queries.
await client.register_models([Developer, Coffee, Consumed])
```
### Interacting with the database
Now the fun stuff begins! We are ready to interact with our database. For the sake of this [`quickstart guide`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-quickstart) we are going to keep it nice and simple, but this is just the surface of what pyneo4j-ogm has to offer.
We are going to create a new `Developer` and some `Coffee` and give him something to drink:
```python
# Imagine your models have been defined above...
async def main():
# And your client has been initialized and connected to the database...
# We create a new `Developer` node and the `Coffee` he is going to drink.
john = Developer(name="John", age=25)
await john.create()
cappuccino = Coffee(flavor="Cappuccino", milk=True, sugar=False)
await cappuccino.create()
# Here we create a new relationship between `john` and his `cappuccino`.
# Additionally, we set the `liked` property of the relationship to `True`.
await john.coffee.connect(cappuccino, {"liked": True}) # Will print `John chugged another one!`
```
### Full example
```python
import asyncio
from pyneo4j_ogm import (
NodeModel,
Pyneo4jClient,
RelationshipModel,
RelationshipProperty,
RelationshipPropertyCardinality,
RelationshipPropertyDirection,
WithOptions,
)
from pydantic import Field
from uuid import UUID, uuid4
class Developer(NodeModel):
"""
This class represents a `Developer` node inside the graph. All interaction
with nodes of this type will be handled by this class.
"""
uid: WithOptions(UUID, unique=True) = Field(default_factory=uuid4)
name: str
age: int
coffee: RelationshipProperty["Coffee", "Consumed"] = RelationshipProperty(
target_model="Coffee",
relationship_model="Consumed",
direction=RelationshipPropertyDirection.OUTGOING,
cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,
allow_multiple=True,
)
class Settings:
# Hooks are available for all methods that interact with the database.
post_hooks = {
"coffee.connect": lambda self, *args, **kwargs: print(f"{self.name} chugged another one!")
}
class Coffee(NodeModel):
"""
This class represents a node with the labels `Beverage` and `Hot`. Notice
that the labels of this model are explicitly defined in the `Settings` class.
"""
flavor: str
sugar: bool
milk: bool
developers: RelationshipProperty["Developer", "Consumed"] = RelationshipProperty(
target_model=Developer,
relationship_model="Consumed",
direction=RelationshipPropertyDirection.INCOMING,
cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,
allow_multiple=True,
)
class Settings:
labels = {"Beverage", "Hot"}
class Consumed(RelationshipModel):
"""
Unlike the models above, this class represents a relationship between two
nodes. In this case, it represents the relationship between the `Developer`
and `Coffee` models. Like with node-models, the `Settings` class allows us to
define some settings for this relationship.
Note that the relationship itself does not define it's start- and end-nodes,
making it reusable for other models as well.
"""
liked: bool
class Settings:
type = "CHUGGED"
async def main():
# We initialize a new `Pyneo4jClient` instance and connect to the database.
client = Pyneo4jClient()
await client.connect(uri="<connection-uri-to-database>", auth=("<username>", "<password>"))
# To use our models for running queries later on, we have to register
# them with the client.
# **Note**: You only have to register the models that you want to use
# for queries and you can even skip this step if you want to use the
# `Pyneo4jClient` instance for running raw queries.
await client.register_models([Developer, Coffee, Consumed])
# We create a new `Developer` node and the `Coffee` he is going to drink.
john = Developer(name="John", age=25)
await john.create()
cappuccino = Coffee(flavor="Cappuccino", milk=True, sugar=False)
await cappuccino.create()
# Here we create a new relationship between `john` and his `cappuccino`.
# Additionally, we set the `liked` property of the relationship to `True`.
await john.coffee.connect(cappuccino, {"liked": True}) # Will print `John chugged another one!`
# Be a good boy and close your connections after you are done.
await client.close()
asyncio.run(main())
```
And that's it! You should now see a `Developer` and a `Hot/Beverage` node, connected by a `CONSUMED` relationship. If you want to learn more about the library, you can check out the full [`Documentation`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs).
## 📚 Documentation
In the following we are going to take a closer look at the different parts of `pyneo4j-ogm` and how to use them. We will cover everything pyneo4j-ogm has to offer, from the `Pyneo4jClient` to the `NodeModel` and `RelationshipModel` classes all the way to the `Query filters` and `Auto-fetching relationship-properties`.
### Table of contents
- [pyneo4j-ogm](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#pyneo4j-ogm)
- [🎯 Features](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#features)
- [📣 Announcements](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-announcements)
- [📦 Installation](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-installation)
- [🚀 Quickstart](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-quickstart)
- [Defining our data structures](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-defining-our-data-structures)
- [Creating a database client](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-creating-a-database-client)
- [Interacting with the database](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-interacting-with-the-database)
- [Full example](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-full-example)
- [Running the test suite](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-running-the-test-suite)
- [📚 Documentation](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs)
- [Basic concepts](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Concept.md)
- [A note on Pydantic version support](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Concept.md#a-note-on-pydantic-version-support)
- [Database client](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md)
- [Connecting to the database](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#connecting-to-the-database)
- [Closing an existing connection](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#closing-an-existing-connection)
- [Registering models](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#registering-models)
- [Executing Cypher queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#executing-cypher-queries)
- [Batching cypher queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#batching-cypher-queries)
- [Using bookmarks (Enterprise Edition only)](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#using-bookmarks-enterprise-edition-only)
- [Manual indexing and constraints](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#manual-indexing-and-constraints)
- [Client utilities](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#client-utilities)
- [Models](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md)
- [Indexes, constraints and properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#indexes-constraints-and-properties)
- [Reserved properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#reserved-properties)
- [Configuration settings](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#configuration-settings)
- [NodeModel configuration](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#nodemodel-configuration)
- [RelationshipModel configuration](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#relationshipmodel-configuration)
- [Available methods](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#available-methods)
- [Instance.update()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#instanceupdate)
- [Instance.delete()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#instancedelete)
- [Instance.refresh()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#instancerefresh)
- [Model.find_one()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelfind_one)
- [Model.find_many()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelfind_many)
- [Model.update_one()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelupdate_one)
- [Model.update_many()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelupdate_many)
- [Model.delete_one()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modeldelete_one)
- [Model.delete_many()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modeldelete_many)
- [Model.count()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelcount)
- [NodeModelInstance.create()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#nodemodelinstancecreate)
- [NodeModelInstance.find_connected_nodes()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#nodemodelinstancefind_connected_nodes)
- [RelationshipModelInstance.start_node()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#relationshipmodelinstancestart_node)
- [RelationshipModelInstance.end_node()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#relationshipmodelinstanceend_node)
- [Serializing models](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#serializing-models)
- [Hooks](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#hooks)
- [Pre-hooks](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#pre-hooks)
- [Post-hooks](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#post-hooks)
- [Model settings](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#model-settings)
- [Relationship-properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md)
- [Available methods](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#available-methods)
- [RelationshipProperty.relationships()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyrelationships)
- [RelationshipProperty.connect()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyconnect)
- [RelationshipProperty.disconnect()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertydisconnect)
- [RelationshipProperty.disconnect\_all()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertydisconnect_all)
- [RelationshipProperty.replace()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyreplace)
- [RelationshipProperty.find\_connected\_nodes()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyfind_connected_nodes)
- [Hooks with relationship properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#hooks-with-relationship-properties)
- [Queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md)
- [Filtering queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#filtering-queries)
- [Comparison operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#comparison-operators)
- [String operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#string-operators)
- [List operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#list-operators)
- [Logical operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#logical-operators)
- [Element operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#element-operators)
- [Pattern matching](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#pattern-matching)
- [Multi-hop filters](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#multi-hop-filters)
- [Projections](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#projections)
- [Query options](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#query-options)
- [Auto-fetching relationship-properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#auto-fetching-relationship-properties)
- [Migrations](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md)
- [Initializing migrations for your project](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#initializing-migrations-for-your-project)
- [Creating a new migration](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#creating-a-new-migration)
- [Running migrations](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#running-migrations)
- [Listing migrations](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#listing-migrations)
- [Programmatic usage](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#programmatic-usage)
- [Logging](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Logging.md)
### Running the test suite
To run the test suite, you have to install the development dependencies and run the tests using `pytest`. The tests are located in the `tests` directory. Some tests will require you to have a Neo4j instance running on `localhost:7687` with the credentials (`neo4j:password`). This can easily be done using the provided `docker-compose.yml` file.
```bash
poetry run pytest tests --asyncio-mode=auto -W ignore::DeprecationWarning
```
> **Note:** The `-W ignore::DeprecationWarning` can be omitted but will result in a lot of deprication warnings by Neo4j itself about the usage of the now deprecated `ID`.
As for running the tests with a different pydantic version, you can just install a different pydantic version with the following command:
```bash
poetry add pydantic@<version>
```
Raw data
{
"_id": null,
"home_page": "https://github.com/groc-prog/pyneo4j-ogm",
"name": "pyneo4j-ogm",
"maintainer": "groc-prog",
"docs_url": null,
"requires_python": "<4.0,>=3.10",
"maintainer_email": "marc.troisner@gmail.com",
"keywords": "neo4j, python, orm, ogm, async, asynchronous, database, graph-database, pydantic",
"author": "groc-prog",
"author_email": "marc.troisner@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/05/c9/44a4568fe444440fef2725c8b39bedfc11325960e2375776b2d3d937dacb/pyneo4j_ogm-0.6.0.tar.gz",
"platform": null,
"description": "# pyneo4j-ogm\n\n[![PyPI](https://img.shields.io/pypi/v/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)\n[![PyPI - License](https://img.shields.io/pypi/l/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/pyneo4j-ogm?style=flat-square)](https://pypi.org/project/pyneo4j-ogm/)\n\n[`pyneo4j-ogm`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop) is a asynchronous `Object-Graph-Mapper` for [`Neo4j 5+`](https://neo4j.com/docs/) and [`Python 3.10+`](https://www.python.org/). It is inspired by [`beanie`](https://github.com/roman-right/beanie) and build on top of proven technologies like [`Pydantic 1.10+ and 2+`](https://docs.pydantic.dev/latest/) and the [`Neo4j Python Driver`](https://neo4j.com/docs/api/python-driver/current/index.html). It saves you from writing ever-repeating boilerplate queries and allows you to focus on the `stuff that actually matters`. It is designed to be simple and easy to use, but also flexible and powerful.\n\n## \ud83c\udfaf Features\n\n[`pyneo4j-ogm`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop) has a lot to offer, including:\n\n- [x] **Fully typed**: pyneo4j-ogm is `fully typed` out of the box.\n- [x] **Powerful validation**: Since we use Pydantic under the hood, you can use it's powerful validation and serialization features without any issues.\n- [x] **Focus on developer experience**: Designed to be simple to use, pyneo4j-ogm provides features for both simple queries and more `advanced use-cases` while keeping it's API as simple as possible.\n- [x] **Build-in migration tooling**: Shipped with simple, yet flexible migration tooling.\n- [x] **Fully asynchronous**: Completely asynchronous code, thanks to the `Neo4j Python Driver`.\n- [x] **Supports Neo4j 5+**: pyneo4j-ogm supports `Neo4j 5+` and is tested against the latest version of Neo4j.\n- [x] **Multi-version Pydantic support**: Both `Pydantic 1.10+` and `2+` fully supported.\n\n## \ud83d\udce3 Announcements\n\nThings to come in the future. Truly exiting stuff! If you have feature requests which you think might improve `pyneo4j-ogm`, feel free to open up a feature request.\n\n- [ ] [MemGraph](https://memgraph.com/) support.\n\n## \ud83d\udce6 Installation\n\nUsing [`pip`](https://pip.pypa.io/en/stable/):\n\n```bash\npip install pyneo4j-ogm\n```\n\nor when using [`Poetry`](https://python-poetry.org/):\n\n```bash\npoetry add pyneo4j-ogm\n```\n\n## \ud83d\ude80 Quickstart\n\nBefore we can get going, we have to take care of some things:\n\n- We need to define our models, which will represent the nodes and relationships inside our database.\n- We need a database client, which will do the actual work for us.\n\n### Defining our data structures\n\nSince every developer has a coffee addiction one way or another, we are going to use `Coffee` and `Developers` for this guide. So let's start by defining what our data should look like:\n\n```python\nfrom pyneo4j_ogm import (\n NodeModel,\n RelationshipModel,\n RelationshipProperty,\n RelationshipPropertyCardinality,\n RelationshipPropertyDirection,\n WithOptions,\n)\nfrom pydantic import Field\nfrom uuid import UUID, uuid4\n\n\nclass Developer(NodeModel):\n \"\"\"\n This class represents a `Developer` node inside the graph. All interactions\n with nodes of this type will be handled by this class.\n \"\"\"\n uid: WithOptions(UUID, unique=True) = Field(default_factory=uuid4)\n name: str\n age: int\n\n coffee: RelationshipProperty[\"Coffee\", \"Consumed\"] = RelationshipProperty(\n target_model=\"Coffee\",\n relationship_model=\"Consumed\",\n direction=RelationshipPropertyDirection.OUTGOING,\n cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,\n allow_multiple=True,\n )\n\n class Settings:\n # Hooks are available for all methods that interact with the database.\n post_hooks = {\n \"coffee.connect\": lambda self, *args, **kwargs: print(f\"{self.name} chugged another one!\")\n }\n\n\nclass Coffee(NodeModel):\n \"\"\"\n This class represents a node with the labels `Beverage` and `Hot`. Notice\n that the labels of this model are explicitly defined in the `Settings` class.\n \"\"\"\n flavor: str\n sugar: bool\n milk: bool\n\n developers: RelationshipProperty[\"Developer\", \"Consumed\"] = RelationshipProperty(\n target_model=Developer,\n relationship_model=\"Consumed\",\n direction=RelationshipPropertyDirection.INCOMING,\n cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,\n allow_multiple=True,\n )\n\n class Settings:\n labels = {\"Beverage\", \"Hot\"}\n\nclass Consumed(RelationshipModel):\n \"\"\"\n Unlike the models above, this class represents a relationship between two\n nodes. In this case, it represents the relationship between the `Developer`\n and `Coffee` models. Like with node-models, the `Settings` class allows us to\n define some configuration for this relationship.\n\n Note that the relationship itself does not define it's start- and end-nodes,\n making it reusable for other models as well.\n \"\"\"\n liked: bool\n\n class Settings:\n type = \"CHUGGED\"\n```\n\nUntil now everything seems pretty standard if you have worked with other ORM's before. But if you haven't, we are going to go over what happened above:\n\n- We defined 2 node models `Developer` and `Coffee`, and a relationship `Consumed`.\n- Some models define a special inner `Settings` class. This is used to customize the behavior of our models inside the graph. More on these settings can be found [`here`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#configuration-settings).\n- The `WithOptions` function has been used to define `constraints and indexes` (more about them [`here`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#manual-indexing-and-constraints)) on model properties.\n\n### Creating a database client\n\nIn pyneo4j-ogm, the real work is done by a database client. One of these bad-boys can be created by initializing a `Pyneo4jClient` instance. But for models to work as expected, we have to let our client know that we want to use them like so:\n\n```python\nfrom pyneo4j_ogm import Pyneo4jClient\n\nasync def main():\n # We initialize a new `Pyneo4jClient` instance and connect to the database.\n client = Pyneo4jClient()\n\n # Replace `<connection-uri-to-database>`, `<username>` and `<password>` with the\n # actual values.\n await client.connect(uri=\"<connection-uri-to-database>\", auth=(\"<username>\", \"<password>\"))\n\n # To use our models for running queries later on, we have to register\n # them with the client.\n # **Note**: You only have to register the models that you want to use\n # for queries and you can even skip this step if you want to use the\n # `Pyneo4jClient` instance for running raw queries.\n await client.register_models([Developer, Coffee, Consumed])\n```\n\n### Interacting with the database\n\nNow the fun stuff begins! We are ready to interact with our database. For the sake of this [`quickstart guide`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-quickstart) we are going to keep it nice and simple, but this is just the surface of what pyneo4j-ogm has to offer.\n\nWe are going to create a new `Developer` and some `Coffee` and give him something to drink:\n\n```python\n# Imagine your models have been defined above...\n\nasync def main():\n # And your client has been initialized and connected to the database...\n\n # We create a new `Developer` node and the `Coffee` he is going to drink.\n john = Developer(name=\"John\", age=25)\n await john.create()\n\n cappuccino = Coffee(flavor=\"Cappuccino\", milk=True, sugar=False)\n await cappuccino.create()\n\n # Here we create a new relationship between `john` and his `cappuccino`.\n # Additionally, we set the `liked` property of the relationship to `True`.\n await john.coffee.connect(cappuccino, {\"liked\": True}) # Will print `John chugged another one!`\n```\n\n### Full example\n\n```python\nimport asyncio\nfrom pyneo4j_ogm import (\n NodeModel,\n Pyneo4jClient,\n RelationshipModel,\n RelationshipProperty,\n RelationshipPropertyCardinality,\n RelationshipPropertyDirection,\n WithOptions,\n)\nfrom pydantic import Field\nfrom uuid import UUID, uuid4\n\nclass Developer(NodeModel):\n \"\"\"\n This class represents a `Developer` node inside the graph. All interaction\n with nodes of this type will be handled by this class.\n \"\"\"\n uid: WithOptions(UUID, unique=True) = Field(default_factory=uuid4)\n name: str\n age: int\n\n coffee: RelationshipProperty[\"Coffee\", \"Consumed\"] = RelationshipProperty(\n target_model=\"Coffee\",\n relationship_model=\"Consumed\",\n direction=RelationshipPropertyDirection.OUTGOING,\n cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,\n allow_multiple=True,\n )\n\n class Settings:\n # Hooks are available for all methods that interact with the database.\n post_hooks = {\n \"coffee.connect\": lambda self, *args, **kwargs: print(f\"{self.name} chugged another one!\")\n }\n\n\nclass Coffee(NodeModel):\n \"\"\"\n This class represents a node with the labels `Beverage` and `Hot`. Notice\n that the labels of this model are explicitly defined in the `Settings` class.\n \"\"\"\n flavor: str\n sugar: bool\n milk: bool\n\n developers: RelationshipProperty[\"Developer\", \"Consumed\"] = RelationshipProperty(\n target_model=Developer,\n relationship_model=\"Consumed\",\n direction=RelationshipPropertyDirection.INCOMING,\n cardinality=RelationshipPropertyCardinality.ZERO_OR_MORE,\n allow_multiple=True,\n )\n\n class Settings:\n labels = {\"Beverage\", \"Hot\"}\n\nclass Consumed(RelationshipModel):\n \"\"\"\n Unlike the models above, this class represents a relationship between two\n nodes. In this case, it represents the relationship between the `Developer`\n and `Coffee` models. Like with node-models, the `Settings` class allows us to\n define some settings for this relationship.\n\n Note that the relationship itself does not define it's start- and end-nodes,\n making it reusable for other models as well.\n \"\"\"\n liked: bool\n\n class Settings:\n type = \"CHUGGED\"\n\n\nasync def main():\n # We initialize a new `Pyneo4jClient` instance and connect to the database.\n client = Pyneo4jClient()\n await client.connect(uri=\"<connection-uri-to-database>\", auth=(\"<username>\", \"<password>\"))\n\n # To use our models for running queries later on, we have to register\n # them with the client.\n # **Note**: You only have to register the models that you want to use\n # for queries and you can even skip this step if you want to use the\n # `Pyneo4jClient` instance for running raw queries.\n await client.register_models([Developer, Coffee, Consumed])\n\n # We create a new `Developer` node and the `Coffee` he is going to drink.\n john = Developer(name=\"John\", age=25)\n await john.create()\n\n cappuccino = Coffee(flavor=\"Cappuccino\", milk=True, sugar=False)\n await cappuccino.create()\n\n # Here we create a new relationship between `john` and his `cappuccino`.\n # Additionally, we set the `liked` property of the relationship to `True`.\n await john.coffee.connect(cappuccino, {\"liked\": True}) # Will print `John chugged another one!`\n\n # Be a good boy and close your connections after you are done.\n await client.close()\n\nasyncio.run(main())\n```\n\nAnd that's it! You should now see a `Developer` and a `Hot/Beverage` node, connected by a `CONSUMED` relationship. If you want to learn more about the library, you can check out the full [`Documentation`](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs).\n\n## \ud83d\udcda Documentation\n\nIn the following we are going to take a closer look at the different parts of `pyneo4j-ogm` and how to use them. We will cover everything pyneo4j-ogm has to offer, from the `Pyneo4jClient` to the `NodeModel` and `RelationshipModel` classes all the way to the `Query filters` and `Auto-fetching relationship-properties`.\n\n### Table of contents\n\n- [pyneo4j-ogm](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#pyneo4j-ogm)\n - [\ud83c\udfaf Features](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#features)\n - [\ud83d\udce3 Announcements](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-announcements)\n - [\ud83d\udce6 Installation](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-installation)\n - [\ud83d\ude80 Quickstart](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-quickstart)\n - [Defining our data structures](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-defining-our-data-structures)\n - [Creating a database client](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-creating-a-database-client)\n - [Interacting with the database](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-interacting-with-the-database)\n - [Full example](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-full-example)\n - [Running the test suite](https://github.com/groc-prog/pyneo4j-ogm/blob/develop?tab=readme-ov-file#-running-the-test-suite)\n - [\ud83d\udcda Documentation](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs)\n - [Basic concepts](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Concept.md)\n - [A note on Pydantic version support](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Concept.md#a-note-on-pydantic-version-support)\n - [Database client](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md)\n - [Connecting to the database](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#connecting-to-the-database)\n - [Closing an existing connection](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#closing-an-existing-connection)\n - [Registering models](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#registering-models)\n - [Executing Cypher queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#executing-cypher-queries)\n - [Batching cypher queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#batching-cypher-queries)\n - [Using bookmarks (Enterprise Edition only)](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#using-bookmarks-enterprise-edition-only)\n - [Manual indexing and constraints](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#manual-indexing-and-constraints)\n - [Client utilities](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/DatabaseClient.md#client-utilities)\n - [Models](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md)\n - [Indexes, constraints and properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#indexes-constraints-and-properties)\n - [Reserved properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#reserved-properties)\n - [Configuration settings](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#configuration-settings)\n - [NodeModel configuration](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#nodemodel-configuration)\n - [RelationshipModel configuration](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#relationshipmodel-configuration)\n - [Available methods](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#available-methods)\n - [Instance.update()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#instanceupdate)\n - [Instance.delete()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#instancedelete)\n - [Instance.refresh()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#instancerefresh)\n - [Model.find_one()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelfind_one)\n - [Model.find_many()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelfind_many)\n - [Model.update_one()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelupdate_one)\n - [Model.update_many()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelupdate_many)\n - [Model.delete_one()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modeldelete_one)\n - [Model.delete_many()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modeldelete_many)\n - [Model.count()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#modelcount)\n - [NodeModelInstance.create()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#nodemodelinstancecreate)\n - [NodeModelInstance.find_connected_nodes()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#nodemodelinstancefind_connected_nodes)\n - [RelationshipModelInstance.start_node()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#relationshipmodelinstancestart_node)\n - [RelationshipModelInstance.end_node()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#relationshipmodelinstanceend_node)\n - [Serializing models](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#serializing-models)\n - [Hooks](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#hooks)\n - [Pre-hooks](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#pre-hooks)\n - [Post-hooks](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#post-hooks)\n - [Model settings](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Models.md#model-settings)\n - [Relationship-properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md)\n - [Available methods](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#available-methods)\n - [RelationshipProperty.relationships()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyrelationships)\n - [RelationshipProperty.connect()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyconnect)\n - [RelationshipProperty.disconnect()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertydisconnect)\n - [RelationshipProperty.disconnect\\_all()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertydisconnect_all)\n - [RelationshipProperty.replace()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyreplace)\n - [RelationshipProperty.find\\_connected\\_nodes()](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#relationshippropertyfind_connected_nodes)\n - [Hooks with relationship properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/RelationshipProperty.md#hooks-with-relationship-properties)\n - [Queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md)\n - [Filtering queries](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#filtering-queries)\n - [Comparison operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#comparison-operators)\n - [String operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#string-operators)\n - [List operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#list-operators)\n - [Logical operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#logical-operators)\n - [Element operators](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#element-operators)\n - [Pattern matching](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#pattern-matching)\n - [Multi-hop filters](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#multi-hop-filters)\n - [Projections](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#projections)\n - [Query options](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#query-options)\n - [Auto-fetching relationship-properties](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Query.md#auto-fetching-relationship-properties)\n - [Migrations](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md)\n - [Initializing migrations for your project](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#initializing-migrations-for-your-project)\n - [Creating a new migration](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#creating-a-new-migration)\n - [Running migrations](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#running-migrations)\n - [Listing migrations](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#listing-migrations)\n - [Programmatic usage](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Migrations.md#programmatic-usage)\n - [Logging](https://github.com/groc-prog/pyneo4j-ogm/blob/develop/docs/Logging.md)\n\n### Running the test suite\n\nTo run the test suite, you have to install the development dependencies and run the tests using `pytest`. The tests are located in the `tests` directory. Some tests will require you to have a Neo4j instance running on `localhost:7687` with the credentials (`neo4j:password`). This can easily be done using the provided `docker-compose.yml` file.\n\n```bash\npoetry run pytest tests --asyncio-mode=auto -W ignore::DeprecationWarning\n```\n\n> **Note:** The `-W ignore::DeprecationWarning` can be omitted but will result in a lot of deprication warnings by Neo4j itself about the usage of the now deprecated `ID`.\n\nAs for running the tests with a different pydantic version, you can just install a different pydantic version with the following command:\n\n```bash\npoetry add pydantic@<version>\n```\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Asynchronous Python OGM for Neo4j",
"version": "0.6.0",
"project_urls": {
"Documentation": "https://github.com/groc-prog/pyneo4j-ogm#readme",
"Homepage": "https://github.com/groc-prog/pyneo4j-ogm",
"Repository": "https://github.com/groc-prog/pyneo4j-ogm"
},
"split_keywords": [
"neo4j",
" python",
" orm",
" ogm",
" async",
" asynchronous",
" database",
" graph-database",
" pydantic"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "876703513cee90c2cea295aa21d7c5d0900e636e06a75e650612d4a01fe860f4",
"md5": "71c0d1aa6060e4b8ff3019dab48331fa",
"sha256": "c56722aa787e89e81df7ea9f5517bab0c6820a981be55784475a18f53fda954d"
},
"downloads": -1,
"filename": "pyneo4j_ogm-0.6.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "71c0d1aa6060e4b8ff3019dab48331fa",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.10",
"size": 68299,
"upload_time": "2024-05-23T09:47:32",
"upload_time_iso_8601": "2024-05-23T09:47:32.507640Z",
"url": "https://files.pythonhosted.org/packages/87/67/03513cee90c2cea295aa21d7c5d0900e636e06a75e650612d4a01fe860f4/pyneo4j_ogm-0.6.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "05c944a4568fe444440fef2725c8b39bedfc11325960e2375776b2d3d937dacb",
"md5": "d319543b581c8132b844f21efdaffdf1",
"sha256": "c384b78e187dd7acdf5572942264d07532401c39e5c27f7c9535b0c2fe106e23"
},
"downloads": -1,
"filename": "pyneo4j_ogm-0.6.0.tar.gz",
"has_sig": false,
"md5_digest": "d319543b581c8132b844f21efdaffdf1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.10",
"size": 61652,
"upload_time": "2024-05-23T09:47:34",
"upload_time_iso_8601": "2024-05-23T09:47:34.407304Z",
"url": "https://files.pythonhosted.org/packages/05/c9/44a4568fe444440fef2725c8b39bedfc11325960e2375776b2d3d937dacb/pyneo4j_ogm-0.6.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-05-23 09:47:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "groc-prog",
"github_project": "pyneo4j-ogm",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pyneo4j-ogm"
}