Skip to content

Commit fae56a3

Browse files
authored
# GQL Summary Statuses
This PR introduces GQL compliant status objects to the `ResultSummary`. ⚠️ This feature is still in **PREVIEW**. It might be changed without following the deprecation policy. ## Deprecations `SummaryNotificationPosition` has been deprecated in favor of `SummaryInputPosition`. ## New GQL Status Objects A new property `ResultSummary.gql_status_objects` has been introduced. It returns a sequence of `GqlStatusObject`s. These objects contain information about the execution of the query including notifications as they exist now (see `SummaryNotification`) but also an outcome status like `00000` for "success", `02000` for "no data", and `00001` for "omitted result". Eventually, this API is planned to supersede the current notifications API. The GqlStatusObjects will be presented in the following order: * A "no data" (`02xxx`) has precedence over a warning; * A "warning" (`01xxx`) has precedence over a success. * A "success" (`00xxx`) has precedence over anything informational (`03xxx`). ## Notification Filtering Some status objects are notifications (vendor-specific definition). Those notifications can be identified using `GqlStatusObject.is_notification`. They contain additional information like a `classification`, `severity`, and `position`. Further, those notifications can be configured to be suppressed in the DBMS saving bandwidth not having to send them to the client as well as compute not having to check for those conditions. For this, the following driver config options can be used: * `notifications_min_severity` (already existing) * `notifications_disabled_classifications` (new, same as the existing `notifications_disabled_categories`). If both options are provided, they are merged into a single list of disabled classifications/categories. All filter config options will affect both the current notifications API as well as the notifications among the GQL status objects. ## Polyfilling There are now two APIs to access notifications: the old notifications API and the new GQL status API. To provide an easy migration path the driver will, depending on if the server is GQL aware or not (i.e., recent or old neo4j version), polyfill the API not supported by the server on a best-effort basis. In particular, GQL status objects have a GQLSTATUS code that has not counterpart in non-GQL-aware neo4j versions. Therefore, the driver will fill the GQLSTATUS with either `01N42` for warning notifications and `03N42` for informational notifications. Further, such status codes might be used by the server in the transition phase towards full GQL compliance. They indicate a lack of a final status code and will likely change in the future. A list of all possible GQLSTATUS codes is not yet available as the server-side of this change is still very actively being worked on. ## Example Usage ```python with neo4j.GraphDatabase.driver( URI, auth=AUTH, notifications_disabled_classifications=[ # Remove this filter entry to get an additional notification # about the usage of a cartesian product. neo4j.NotificationClassification.PERFORMANCE ], ) as driver: _, summary, _ = driver.execute_query( "MATCH (n:Foo), (m) RETURN n.nope, m" ) assert isinstance(summary, neo4j.ResultSummary) for status in summary.gql_status_objects: # The GQLSTATUS code of the status object. print("GQLSTATUS:", status.gql_status) # A description of the status for human consumption. print("description:", status.status_description) # Whether the status is a notification and can be filtered. print("is notification:", status.is_notification) # Notification and thus vendor-specific fields. # These fields are only meaningful for notifications. if status.is_notification: # The position in the query that caused the notification. print("position:", status.position) # The notification's classification. # This is the counterpart to `neo4j.NotificationCategory`, # however, the term category is already used for a different # context in the context of GQL print("classification:", status.classification) print("unparsed classification:", status.raw_classification) # The notification's severity. print("severity:", status.severity) print("unparsed severity:", status.raw_severity) # Any raw extra information provided by the DBMS: print("diagnostic record:", status.diagnostic_record) print("=" * 80) ``` Which, when run against an empty 5.19 DBMS, gives the following output ``` GQLSTATUS: 02000 description: note: no data is notification: False diagnostic record: {'OPERATION': '', 'OPERATION_CODE': '0', 'CURRENT_SCHEMA': '/'} ================================================================================ GQLSTATUS: 01N42 description: One of the labels in your query is not available in the database, make sure you didn't misspell it or that the label is available when you run this statement in your application (the missing label name is: Foo) is notification: True position: line: 1, column: 10, offset: 9 classification: NotificationClassification.UNRECOGNIZED unparsed classification: UNRECOGNIZED severity: NotificationSeverity.WARNING unparsed severity: WARNING diagnostic record: {'OPERATION': '', 'OPERATION_CODE': '0', 'CURRENT_SCHEMA': '/', '_classification': 'UNRECOGNIZED', '_severity': 'WARNING', '_position': {'column': 10, 'offset': 9, 'line': 1}} ================================================================================ GQLSTATUS: 01N42 description: One of the property names in your query is not available in the database, make sure you didn't misspell it or that the label is available when you run this statement in your application (the missing property name is: nope) is notification: True position: line: 1, column: 29, offset: 28 classification: NotificationClassification.UNRECOGNIZED unparsed classification: UNRECOGNIZED severity: NotificationSeverity.WARNING unparsed severity: WARNING diagnostic record: {'OPERATION': '', 'OPERATION_CODE': '0', 'CURRENT_SCHEMA': '/', '_classification': 'UNRECOGNIZED', '_severity': 'WARNING', '_position': {'column': 29, 'offset': 28, 'line': 1}} ================================================================================ ```
1 parent 68b8626 commit fae56a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+6243
-717
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
See also https://github.com/neo4j/neo4j-python-driver/wiki for a full changelog.
44

55
## NEXT RELEASE
6-
- No breaking or major changes.
6+
- `SummaryNotificationPosition` has been deprecated in favor of `SummaryInputPosition`.
77

88

99
## Version 5.17 - 5.21

docs/source/api.rst

+95-6
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,9 @@ Set the minimum severity for notifications the server should send to the client.
686686
Disabling severities allows the server to skip analysis for those, which can speed up query execution.
687687

688688
Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`.ResultSummary.summary_notifications`.
689+
Further, they are surfaced through :attr:`.ResultSummary.gql_status_objects`:
690+
For GQL-aware servers, notifications are a subset of GqlStatusObjects.
691+
See also :attr:`.GqlSatusObject.is_notification`.
689692

690693
:data:`None` will apply the server's default setting.
691694

@@ -706,13 +709,17 @@ Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`
706709

707710
``notifications_disabled_categories``
708711
-------------------------------------
709-
Set categories of notifications the server should not send to the client.
712+
Set categories/classifications of notifications the server should not send to the client.
710713
Disabling categories allows the server to skip analysis for those, which can speed up query execution.
711714

712715
Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`.ResultSummary.summary_notifications`.
716+
Further, they are surfaced (alongside other status objects) through :attr:`.ResultSummary.gql_status_objects`:
717+
See also :attr:`.GqlSatusObject.is_notification`.
713718

714719
:data:`None` will apply the server's default setting.
715720

721+
If specified together with :ref:`driver-notifications-disabled-classifications-ref`, the settings will be merged.
722+
716723
.. Note::
717724
If configured, the server or all servers of the cluster need to support notifications filtering
718725
(server version 5.7 and newer).
@@ -726,6 +733,27 @@ Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`
726733
.. seealso:: :class:`.NotificationDisabledCategory`, session config :ref:`session-notifications-disabled-categories-ref`
727734

728735

736+
.. _driver-notifications-disabled-classifications-ref:
737+
738+
``notifications_disabled_classifications``
739+
------------------------------------------
740+
Identical to :ref:`driver-notifications-disabled-categories-ref`.
741+
742+
This alias is provided for a consistent naming with :attr:`.GqlStatusObject.classification`.
743+
744+
**This is a preview** (see :ref:`filter-warnings-ref`).
745+
It might be changed without following the deprecation policy.
746+
See also
747+
https://github.com/neo4j/neo4j-python-driver/wiki/preview-features
748+
749+
:Type: :data:`None`, :term:`iterable` of :class:`.NotificationDisabledClassification` and/or :class:`str`
750+
:Default: :data:`None`
751+
752+
.. versionadded:: 5.22
753+
754+
.. seealso:: :class:`.NotificationDisabledClassification`, session config :ref:`session-notifications-disabled-classifications-ref`
755+
756+
729757
.. _driver-warn-notification-severity-ref:
730758

731759
``warn_notification_severity``
@@ -1159,11 +1187,14 @@ Set the minimum severity for notifications the server should send to the client.
11591187
Disabling severities allows the server to skip analysis for those, which can speed up query execution.
11601188

11611189
Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`.ResultSummary.summary_notifications`.
1190+
Further, they are surfaced (alongside other status objects) through :attr:`.ResultSummary.gql_status_objects`:
1191+
See also :attr:`.GqlSatusObject.is_notification`.
11621192

11631193
:data:`None` will apply the driver's configuration setting (:ref:`driver-notifications-min-severity-ref`).
11641194

11651195
.. Note::
1166-
If configured, the server or all servers of the cluster need to support notifications filtering.
1196+
If configured, the server or all servers of the cluster need to support notifications filtering
1197+
(server version 5.7 and newer).
11671198
Otherwise, the driver will raise a :exc:`.ConfigurationError` as soon as it encounters a server that does not.
11681199

11691200
:Type: :data:`None`, :class:`.NotificationMinimumSeverity`, or :class:`str`
@@ -1182,11 +1213,16 @@ Set categories of notifications the server should not send to the client.
11821213
Disabling categories allows the server to skip analysis for those, which can speed up query execution.
11831214

11841215
Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`.ResultSummary.summary_notifications`.
1216+
Further, they are surfaced (alongside other status objects) through :attr:`.ResultSummary.gql_status_objects`:
1217+
See also :attr:`.GqlSatusObject.is_notification`.
11851218

1186-
:data:`None` will apply the driver's configuration setting (:ref:`driver-notifications-min-severity-ref`).
1219+
:data:`None` will apply the driver's configuration setting (:ref:`driver-notifications-disabled-categories-ref`).
1220+
1221+
If specified together with :ref:`session-notifications-disabled-classifications-ref`, the settings will be merged.
11871222

11881223
.. Note::
1189-
If configured, the server or all servers of the cluster need to support notifications filtering.
1224+
If configured, the server or all servers of the cluster need to support notifications filtering
1225+
(server version 5.7 and newer).
11901226
Otherwise, the driver will raise a :exc:`.ConfigurationError` as soon as it encounters a server that does not.
11911227

11921228
:Type: :data:`None`, :term:`iterable` of :class:`.NotificationDisabledCategory` and/or :class:`str`
@@ -1197,6 +1233,27 @@ Notifications are available via :attr:`.ResultSummary.notifications` and :attr:`
11971233
.. seealso:: :class:`.NotificationDisabledCategory`
11981234

11991235

1236+
.. _session-notifications-disabled-classifications-ref:
1237+
1238+
``notifications_disabled_classifications``
1239+
------------------------------------------
1240+
Identical to :ref:`session-notifications-disabled-categories-ref`.
1241+
1242+
This alias is provided for a consistent naming with :attr:`.GqlStatusObject.classification`.
1243+
1244+
**This is a preview** (see :ref:`filter-warnings-ref`).
1245+
It might be changed without following the deprecation policy.
1246+
See also
1247+
https://github.com/neo4j/neo4j-python-driver/wiki/preview-features
1248+
1249+
:Type: :data:`None`, :term:`iterable` of :class:`.NotificationDisabledClassification` and/or :class:`str`
1250+
:Default: :data:`None`
1251+
1252+
.. versionadded:: 5.22
1253+
1254+
.. seealso:: :class:`.NotificationDisabledClassification`
1255+
1256+
12001257

12011258
***********
12021259
Transaction
@@ -1521,6 +1578,20 @@ ServerInfo
15211578
:members:
15221579

15231580

1581+
GqlStatusObject
1582+
===============
1583+
1584+
.. autoclass:: neo4j.GqlStatusObject()
1585+
:members:
1586+
1587+
1588+
NotificationClassification
1589+
--------------------------
1590+
1591+
.. autoclass:: neo4j.NotificationClassification()
1592+
:members:
1593+
1594+
15241595
SummaryNotification
15251596
===================
15261597

@@ -1542,11 +1613,26 @@ NotificationCategory
15421613
:members:
15431614

15441615

1616+
SummaryInputPosition
1617+
--------------------
1618+
1619+
.. autoclass:: neo4j.SummaryInputPosition()
1620+
:members:
1621+
1622+
15451623
SummaryNotificationPosition
15461624
---------------------------
15471625

1548-
.. autoclass:: neo4j.SummaryNotificationPosition()
1549-
:members:
1626+
.. data:: neo4j.SummaryNotificationPosition
1627+
:annotation: = neo4j.SummaryInputPosition
1628+
1629+
Deprecated alias for :class:`.SummaryInputPosition`.
1630+
1631+
.. versionadded:: 5.7
1632+
1633+
.. versionchanged:: 5.22
1634+
Deprecated in favor of :class:`.SummaryInputPosition`.
1635+
15501636

15511637

15521638

@@ -1869,6 +1955,9 @@ Constants, Enums, Helpers
18691955
:show-inheritance:
18701956
:members:
18711957

1958+
.. autoclass:: neo4j.NotificationDisabledClassification()
1959+
:members:
1960+
18721961
.. autoclass:: neo4j.RoutingControl()
18731962
:show-inheritance:
18741963
:members:

src/neo4j/__init__.py

+42-5
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
# limitations under the License.
1515

1616

17+
import typing as _t
1718
from logging import getLogger as _getLogger
1819

1920
from ._api import (
2021
NotificationCategory,
2122
NotificationDisabledCategory,
23+
NotificationDisabledClassification as _NotificationDisabledClassification,
2224
NotificationMinimumSeverity,
2325
NotificationSeverity,
2426
RoutingControl,
@@ -49,6 +51,7 @@
4951
deprecation_warn as _deprecation_warn,
5052
ExperimentalWarning,
5153
get_user_agent,
54+
preview_warn as _preview_warn,
5255
PreviewWarning,
5356
version as __version__,
5457
)
@@ -67,13 +70,25 @@
6770
)
6871
from ._work import (
6972
EagerResult,
73+
GqlStatusObject as _GqlStatusObject,
74+
NotificationClassification as _NotificationClassification,
7075
Query,
7176
ResultSummary,
7277
SummaryCounters,
78+
SummaryInputPosition,
7379
SummaryNotification,
74-
SummaryNotificationPosition,
7580
unit_of_work,
7681
)
82+
83+
84+
if _t.TYPE_CHECKING:
85+
from ._api import NotificationDisabledClassification
86+
from ._work import (
87+
GqlStatusObject,
88+
NotificationClassification,
89+
SummaryInputPosition as SummaryNotificationPosition
90+
)
91+
7792
from .addressing import (
7893
Address,
7994
IPv4Address,
@@ -122,20 +137,23 @@
122137
"Config",
123138
"custom_auth",
124139
"DEFAULT_DATABASE",
125-
"NotificationDisabledCategory",
126140
"Driver",
127141
"EagerResult",
128142
"ExperimentalWarning",
129143
"get_user_agent",
144+
"GqlStatusObject",
130145
"GraphDatabase",
131146
"IPv4Address",
132147
"IPv6Address",
133148
"kerberos_auth",
134149
"log",
135150
"ManagedTransaction",
136-
"NotificationMinimumSeverity",
137151
"Neo4jDriver",
138152
"NotificationCategory",
153+
"NotificationClassification",
154+
"NotificationDisabledCategory",
155+
"NotificationDisabledClassification",
156+
"NotificationMinimumSeverity",
139157
"NotificationSeverity",
140158
"PoolConfig",
141159
"PreviewWarning",
@@ -149,6 +167,7 @@
149167
"Session",
150168
"SessionConfig",
151169
"SummaryCounters",
170+
"SummaryInputPosition",
152171
"SummaryNotification",
153172
"SummaryNotificationPosition",
154173
"Transaction",
@@ -173,12 +192,30 @@ def __getattr__(name):
173192
"log", "Config", "PoolConfig", "SessionConfig", "WorkspaceConfig"
174193
):
175194
_deprecation_warn(
176-
"Importing {} from neo4j is deprecated without replacement. It's "
177-
"internal and will be removed in a future version."
195+
"Importing {} from neo4j is deprecated without replacement. "
196+
"It's internal and will be removed in a future version."
178197
.format(name),
179198
stack_level=2
180199
)
181200
return globals()[f"_{name}"]
201+
if name == "SummaryNotificationPosition":
202+
_deprecation_warn(
203+
"SummaryNotificationPosition is deprecated. "
204+
"Use SummaryInputPosition instead.",
205+
stack_level=2
206+
)
207+
return SummaryInputPosition
208+
if name in (
209+
"NotificationClassification",
210+
"GqlStatusObject",
211+
"NotificationDisabledClassification",
212+
):
213+
_preview_warn(
214+
f"{name} is part of GQLSTATUS support, "
215+
"which is a preview feature.",
216+
stack_level=2
217+
)
218+
return globals()[f"_{name}"]
182219
raise AttributeError(f"module {__name__} has no attribute {name}")
183220

184221

0 commit comments

Comments
 (0)