Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to pass driver instead of database_url #744

Merged
merged 17 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Changelog
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
Version 5.2.0 2023-11
ersion 5.2.0 2023-11
* Add an option to pass your own driver instead of relying on the automatically created one. See set_connection method.
* Add a close_connection method to explicitly close the driver to match Neo4j deprecation.
* Add a DATABASE_NAME config option, available for both auto- and self-managed driver modes.
* Add neomodel_inspect_database script, which inspects an existing database and creates neomodel class definitions for all objects.
* Add support for pandas DataFrame and Series ; numpy Array
* Add relationship uniqueness constraints - for Neo4j >= 5.7
* Add neomodel_inspect_database script, which inspects an existing database and creates neomodel class definitions for all objects.

Version 5.1.2 2023-09
* Raise ValueError on reserved keywords ; add tests #590 #623
Expand Down
14 changes: 0 additions & 14 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,6 @@ Documentation
.. _readthedocs: http://neomodel.readthedocs.org


Upcoming breaking changes notice - >=5.2
========================================

As part of the current quality improvement efforts, we are planning a rework of neomodel's main Database object, which will lead to breaking changes.

The full scope is not drawn out yet, but here are the main points :

* Refactoring standalone methods that depend on the Database singleton into the class itself. See issue https://github.com/neo4j-contrib/neomodel/issues/739

* Adding an option to pass your own driver to neomodel instead of relying on the one that the library creates for you. This will not be a breaking change.

We are aiming to release this in neomodel 5.2


Installation
============

Expand Down
2 changes: 1 addition & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

# General information about the project.
project = __package__
copyright = "2019, " + __author__
copyright = "2023, " + __author__

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down
100 changes: 94 additions & 6 deletions doc/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@ Configuration

This section is covering the Neomodel 'config' module and its variables.

Database
--------
.. _connection_options_doc:

Setting the connection URL::
Connection
----------

There are two ways to define your connection to the database :

1. Provide a Neo4j URL and some options - Driver will be managed by neomodel
2. Create your own Neo4j driver and pass it to neomodel

neomodel-managed (default)
--------------------------

Set the connection URL::

config.DATABASE_URL = 'bolt://neo4j:neo4j@localhost:7687`

Adjust driver configuration::
Adjust driver configuration - these options are only available for this connection method::

config.MAX_CONNECTION_POOL_SIZE = 100 # default
config.CONNECTION_ACQUISITION_TIMEOUT = 60.0 # default
Expand All @@ -22,12 +32,90 @@ Adjust driver configuration::
config.MAX_TRANSACTION_RETRY_TIME = 30.0 # default
config.RESOLVER = None # default
config.TRUST = neo4j.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES # default
config.USER_AGENT = neomodel/vNeo4j.Major.minor # default
config.USER_AGENT = neomodel/v5.2.0 # default

Setting the database name, for neo4j >= 4::
Setting the database name, if different from the default one::

# Using the URL only
config.DATABASE_URL = 'bolt://neo4j:neo4j@localhost:7687/mydb`

# Using config option
config.DATABASE_URL = 'bolt://neo4j:neo4j@localhost:7687`
config.DATABASE_NAME = 'mydb'

self-managed
------------

Create a Neo4j driver::

from neo4j import GraphDatabase
my_driver = GraphDatabase().driver('bolt://localhost:7687', auth=('neo4j', 'password'))
config.DRIVER = my_driver

See the `driver documentation <https://neo4j.com/docs/api/python-driver/current/api.html#graphdatabase>` here.

This mode allows you to use all the available driver options that neomodel doesn't implement, for example auth tokens for SSO.
Note that you have to manage the driver's lifecycle yourself.

However, everything else is still handled by neomodel : sessions, transactions, etc...

NB : Only the synchronous driver will work in this way. The asynchronous driver is not supported yet.

Change/Close the connection
---------------------------

Optionally, you can change the connection at any time by calling ``set_connection``::

from neomodel import db
# Using URL - auto-managed
db.set_connection(url='bolt://neo4j:neo4j@localhost:7687')

# Using self-managed driver
db.set_connection(driver=my_driver)

The new connection url will be applied to the current thread or process.

Since Neo4j version 5, driver auto-close is deprecated. Make sure to close the connection anytime you want to replace it,
as well as at the end of your application's lifecycle by calling ``close_connection``::

from neomodel import db
db.close_connection()

# If you then want a new connection
db.set_connection(url=url)

This will close the Neo4j driver, and clean up everything that neomodel creates for its internal workings.

Protect your credentials
------------------------

You should `avoid setting database access credentials in plain sight <https://
www.ndss-symposium.org/wp-content/uploads/2019/02/ndss2019_04B-3_Meli_paper.pdf>`_. Neo4J defines a number of
`environment variables <https://neo4j.com/developer/kb/how-do-i-authenticate-with-cypher-shell-without-specifying-the-
username-and-password-on-the-command-line/>`_ that are used in its tools and these can be re-used for other applications
too.

These are:

* ``NEO4J_USERNAME``
* ``NEO4J_PASSWORD``
* ``NEO4J_BOLT_URL``

By setting these with (for example): ::

$ export NEO4J_USERNAME=neo4j
$ export NEO4J_PASSWORD=neo4j
$ export NEO4J_BOLT_URL="bolt://$NEO4J_USERNAME:$NEO4J_PASSWORD@localhost:7687"

They can be accessed from a Python script via the ``environ`` dict of module ``os`` and be used to set the connection
with something like: ::

import os
from neomodel import config

config.DATABASE_URL = os.environ["NEO4J_BOLT_URL"]


Enable automatic index and constraint creation
----------------------------------------------

Expand Down
37 changes: 2 additions & 35 deletions doc/source/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,13 @@ Before executing any neomodel code, set the connection url::
from neomodel import config
config.DATABASE_URL = 'bolt://neo4j:neo4j@localhost:7687' # default

# You can specify a database name: 'bolt://neo4j:neo4j@localhost:7687/mydb'

This must be called early on in your app, if you are using Django the `settings.py` file is ideal.

See the Configuration page (:ref:`connection_options_doc`) for config options.

If you are using your neo4j server for the first time you will need to change the default password.
This can be achieved by visiting the neo4j admin panel (default: ``http://localhost:7474`` ).

You can also change the connection url at any time by calling ``set_connection``::

from neomodel import db
db.set_connection('bolt://neo4j:neo4j@localhost:7687')

The new connection url will be applied to the current thread or process.

In general however, it is better to `avoid setting database access credentials in plain sight <https://
www.ndss-symposium.org/wp-content/uploads/2019/02/ndss2019_04B-3_Meli_paper.pdf>`_. Neo4J defines a number of
`environment variables <https://neo4j.com/developer/kb/how-do-i-authenticate-with-cypher-shell-without-specifying-the-
username-and-password-on-the-command-line/>`_ that are used in its tools and these can be re-used for other applications
too.

These are:

* ``NEO4J_USERNAME``
* ``NEO4J_PASSWORD``
* ``NEO4J_BOLT_URL``

By setting these with (for example): ::

$ export NEO4J_USERNAME=neo4j
$ export NEO4J_PASSWORD=neo4j
$ export NEO4J_BOLT_URL="bolt://$NEO4J_USERNAME:$NEO4J_PASSWORD@localhost:7687"

They can be accessed from a Python script via the ``environ`` dict of module ``os`` and be used to set the connection
with something like: ::

import os
from neomodel import config

config.DATABASE_URL = os.environ["NEO4J_BOLT_URL"]

Querying the graph
==================

Expand Down
8 changes: 8 additions & 0 deletions neomodel/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from ._version import __version__

AUTO_INSTALL_LABELS = False

# Use this to connect with automatically created driver
# The following options are the default ones that will be used as driver config
DATABASE_URL = "bolt://neo4j:foobarbaz@localhost:7687"
FORCE_TIMEZONE = False

Expand All @@ -16,3 +19,8 @@
RESOLVER = None
TRUSTED_CERTIFICATES = neo4j.TrustSystemCAs()
USER_AGENT = f"neomodel/v{__version__}"

# Use this to connect with your self-managed driver instead
# DRIVER = neo4j.GraphDatabase().driver(
# "bolt://localhost:7687", auth=("neo4j", "foobarbaz")
# )
10 changes: 6 additions & 4 deletions neomodel/scripts/neomodel_install_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from __future__ import print_function

import sys
from argparse import ArgumentParser, RawDescriptionHelpFormatter
import textwrap
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from importlib import import_module
from os import environ, path

Expand Down Expand Up @@ -70,14 +70,16 @@ def load_python_module_or_file(name):
def main():
parser = ArgumentParser(
formatter_class=RawDescriptionHelpFormatter,
description=textwrap.dedent("""
description=textwrap.dedent(
"""
Setup indexes and constraints on labels in Neo4j for your neomodel schema.

If a connection URL is not specified, the tool will look up the environment
variable NEO4J_BOLT_URL. If that environment variable is not set, the tool
will attempt to connect to the default URL bolt://neo4j:neo4j@localhost:7687
"""
))
),
)

parser.add_argument(
"apps",
Expand Down Expand Up @@ -107,7 +109,7 @@ def main():

# Connect after to override any code in the module that may set the connection
print(f"Connecting to {bolt_url}")
db.set_connection(bolt_url)
db.set_connection(url=bolt_url)

install_all_labels()

Expand Down
12 changes: 7 additions & 5 deletions neomodel/scripts/neomodel_remove_labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,26 @@
"""
from __future__ import print_function

from argparse import ArgumentParser, RawDescriptionHelpFormatter
import textwrap
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from os import environ

from .. import db, remove_all_labels


def main():
parser = ArgumentParser(
formatter_class=RawDescriptionHelpFormatter,
description=textwrap.dedent("""
formatter_class=RawDescriptionHelpFormatter,
description=textwrap.dedent(
"""
Drop all indexes and constraints on labels from schema in Neo4j database.

If a connection URL is not specified, the tool will look up the environment
variable NEO4J_BOLT_URL. If that environment variable is not set, the tool
will attempt to connect to the default URL bolt://neo4j:neo4j@localhost:7687
"""
))
),
)

parser.add_argument(
"--db",
Expand All @@ -59,7 +61,7 @@ def main():

# Connect after to override any code in the module that may set the connection
print(f"Connecting to {bolt_url}")
db.set_connection(bolt_url)
db.set_connection(url=bolt_url)

remove_all_labels()

Expand Down
Loading