Code Monkey home page Code Monkey logo

bolt's Introduction

Logo

Bolt

PHP library for communication with graph database over TCP socket with Bolt protocol specification. Bolt protocol was created by Neo4j and documentation is available at https://www.neo4j.com/. This library is aimed to be low level, support all available versions and keep up with protocol messages architecture and specifications.

ko-fi

JetBrains Logo (Main) logo.

image

๐Ÿท๏ธ Version support

We are trying to keep up and this library supports Bolt <= 5.4.

https://www.neo4j.com/docs/bolt/current/bolt-compatibility/

โœ… Requirements

This library keep up with PHP supported versions what means it is at PHP^8.1.

PHP Extensions

  • mbstring
  • sockets (optional) - Required when you use Socket connection class
  • openssl (optional) - Required when you use StreamSocket connection class with enabled SSL

๐Ÿ’พ Installation

You can use composer or download this repository from github and manually implement it.

Composer

Run the following command in your project to install the latest applicable version of the package:

composer require stefanak-michal/bolt

Packagist

Manual

  1. Download source code from github
  2. Unpack
  3. Copy content of src directory into your project

๐Ÿ–ฅ๏ธ Usage

Concept of usage is based on Bolt messages. Bolt messages are mapped 1:1 as protocol methods. Available protocol methods depends on Bolt version. Communication works in pipeline and you can chain multiple Bolt messages before consuming response from the server.

Main Bolt class serves as Factory design pattern and it returns instance of protocol class by requested Bolt version. Basic usage consist of query execution and fetching response which is split in two methods. First message run is for sending queries. Second message pull is for fetching response from executed query on database. Response from database for Bolt message pull always contains n+1 rows because last entry is success message with meta informations.

โ„น๏ธ More info about available Bolt messages: https://www.neo4j.com/docs/bolt/current/bolt/message/

Available methods

Bolt class

Method / Property Description Type Parameters Return
__construct Bolt constructor public IConnection $connection Bolt
setProtocolVersions Set allowed protocol versions for connection public int/float/string ...$v Bolt
setPackStreamVersion Set PackStream version public int $version = 1 Bolt
build Create protocol instance. Method creates connection, executes handshake and do a version request. public AProtocol
$debug Print binary communication (as hex) public static bool

Protocol class

Method / Property Description Parameters
hello Connect to database array $extra
logon Perform authentification array $auth
logoff Log out authentificated user
run Execute query. Response from database are meta informations. string $statement
array $parameters = []
array $extra = []
pull Pull result from executed query array $extra = []
discard Discard result waiting for pull array $extra = []
begin Start transaction array $extra = []
commit Commit transaction
rollback Rollback transaction
reset Send message to reset connection
telemetry int $api
getVersion Get used protocol version
getResponse Get waiting response from server
getResponses Get waiting responses from server
init @see hello
pullAll @see pull
discardAll @see discard

Multiple methods accept argument called $extra. This argument can contain any of key-value by Bolt specification. This argument was extended during Neo4j development which means the content of it changed. You should keep in mind what version you are working with when using this argument. You can read more about extra parameter in Bolt documentation where you can look into your version and bolt message.

โ„น๏ธ Annotation of methods in protocol classes contains direct link to specific version and message from mentioned documentation website.

Authentification

scheme principal credentials
none
basic username password
bearer token
kerberos token

Transactions

Bolt from version 3 supports transactions and protocol contains these methods:

  • begin
  • commit
  • rollback

run executes query in auto-commit transaction if explicit transaction was not open.

Cypher query parameters

Neo4j PHP
Null null
Boolean boolean
Integer integer
Float float
Bytes Bytes class
String string
List array with consecutive numeric keys from 0
Dictionary object or array which is not considered as list
Structure Classes implementing IStructure by protocol version (docs)

List or dictionary can be also provided as instance of class implementing Bolt\packstream\IPackListGenerator or Bolt\PackStream\IPackDictionaryGenerator. This approach helps with memory management while working with big amount of data. To learn more you can check performance test or packer test.

โš ๏ธ Structures Node, Relationship, UnboundRelationship and Path cannot be used as parameter. They are available only as received data from database.

Example

// Choose and create connection class and specify target host and port.
$conn = new \Bolt\connection\Socket('127.0.0.1', 7687);
// Create new Bolt instance and provide connection object.
$bolt = new \Bolt\Bolt($conn);
// Set requested protocol versions ..you can add up to 4 versions
$bolt->setProtocolVersions(5.4);
// Build and get protocol version instance which creates connection and executes handshake.
$protocol = $bolt->build();

// Initialize communication with database
$response = $protocol->hello()->getResponse();
// verify $response for successful initialization

// Login into database
$response = $protocol->logon(['scheme' => 'basic', 'principal' => 'neo4j', 'credentials' => 'neo4j'])->getResponse();
// verify $response for successful login

// Pipeline two messages. One to execute query with parameters and second to pull records.
$protocol
    ->run('RETURN $a AS num, $b AS str', ['a' => 123, 'b' => 'text'])
    ->pull();
    
// Fetch waiting server responses for pipelined messages.
foreach ($protocol->getResponses() as $response) {
    // $response is instance of \Bolt\protocol\Response.
    // First response is SUCCESS message for RUN message.
    // Second response is RECORD message for PULL message.
    // Third response is SUCCESS message for PULL message.
}

Autoload

Directory src contains autoload file which accepts only Bolt library namespaces. Main Bolt namespace points to this directory. If you have installed this project with composer, you have to load vendor/autoload.php.

โ›“๏ธ Connection

Bolt class constructor accepts connection argument. This argument has to be instance of class which implements IConnection interface. Library offers few options.

\Bolt\connection\Socket

This class use php extension sockets and has better memory usage. More informations here: https://www.php.net/manual/en/book.sockets.php

\Bolt\connection\StreamSocket

This class uses php stream functions. Which is a part of php and there is no extensions needed. More informations here: https://www.php.net/manual/en/ref.stream.php

StreamSocket besides of implemented methods from interface has method to configure SSL. SSL option requires php extension openssl. When you want to activate SSL you have to call method setSslContextOptions. This method accept array by php specification available here: https://www.php.net/manual/en/context.ssl.php.

\Bolt\connection\PStreamSocket

This class extends StreamSocket and adds support for persistent connections. Upon reuse of connection remaining buffer is consumed and message RESET is automatically sent. PHP is stateless therefore using this connection class requires storing meta information about active TCP connection. Default storage is \Bolt\helper\FileCache which you can change with method setCache (PSR-16 Simple Cache).

โš ๏ธ If your system reuse persistent connection and meta information about it was lost for some reason, your attemt to connect will end with ConnectionTimeoutException. Repeated attempt to connect will succeed.

๐Ÿ”’ SSL

Neo4j Aura

Connecting to Aura requires encryption which is provided with SSL. To connect to Aura you have to use StreamSocket connection class and enable SSL.

$conn = new \Bolt\connection\StreamSocket('helloworld.databases.neo4j.io');
// enable SSL
$conn->setSslContextOptions([
    'verify_peer' => true
]);
$bolt = new \Bolt\Bolt($conn);

https://www.php.net/manual/en/context.ssl.php

Example on localhost database with self-signed certificate:

$conn = new \Bolt\connection\StreamSocket();
$conn->setSslContextOptions([
    'local_cert'=> 'd:\www\bolt\cert\public_cert.pem',
    'local_pk' => 'd:\www\bolt\cert\private_key.pem',
    'passphrase' => 'password',
    'allow_self_signed' => true,
    'verify_peer' => false,
    'verify_peer_name' => false
]);
$bolt = new \Bolt\Bolt($conn);

๐Ÿ”– You can also take a look at my article on how to implement SSL for Neo4j running on localhost at Neo4j and self signed certificate.

โฑ๏ธ Timeout

Connection class constructor contains $timeout argument. This timeout is for established socket connection. To set up timeout for establishing socket connection itself you have to set ini directive default_socket_timeout.

Setting up ini directive isn't part of connection class because function ini_set can be disabled on production environments for security reasons.

๐Ÿšฆ Server state

Server state is not reported by server but it is evaluated by received response. You can access current state through property $protocol->serverState. This property is updated with every call getResponse(s).

๐Ÿ“Š Analytics

Bolt does collect anonymous analytics data. These data are stored offline (as files in temp directory) and submitted once a day. You can opt out with environment variable BOLT_ANALYTICS_OPTOUT.

Analytics data are public and available at Mixpanel.

๐Ÿ“Œ More solutions

If you need simple class to cover basic functionality you can use: neo4j-bolt-wrapper

When you are in need of enterprise level take a look on: php-client

PDO implementation is available at pdo-bolt

More informations can be found at: https://neo4j.com/developer/php/

โ™ป๏ธ Old versions

If you need support for end-of-life PHP versions, here is a short info list. Not all new features are implement backwards and this readme is updated to latest released version.

  • PHP < 7.4 - v3.x
  • PHP 7.4 - v5.x
  • PHP 8.0 - v6.x

bolt's People

Contributors

jonathan-rowley avatar luutruong avatar stefanak-michal avatar tomswinkels avatar transistive avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bolt's Issues

Routing in a Causal cluster

Did this lib work with routing in a causal cluster?

When I connect and read data it's works, but writing get me an error for trying to write on non leader node.

There's a way to use that?

[Request] Server side routing

We are running Neo4j in cluster, we want use the Neo4j server side routing.

Maybe this is a good feature for this package?

I cant find any documentation how the server side routing works, maybe you have examples?

Refactor reading from connection

If you have problem with receiving data from server. Can it happen you will get only a part of chunk?
what about something like this?

$msg = '';
while (true) {
  $header = $this->connection->read(2);
  $length = unpack('n', $header)[1] ?? 0; //because 0x00 0x00 end mark is chunk of size 0

  if ($length > 0) {
    $chunk = '';
    $size = mb_strlen($chunk, '8bit');
    while ($size < $length) {
      $chunk .= $this->connection->read($length - $size);
    }
    $msg .= $chunk;
  } else {
    break;
  }
}

not tested.. written in this comment.

Originally posted by @stefanak-michal in #85 (comment)

Also you should read header until you readed length 2.

ErrorException: socket_read(): unable to read from socket [4]: Interrupted system call

I have got some errors:

ErrorException: socket_read(): unable to read from socket [4]: Interrupted system call - (stripped)/vendor/stefanak-michal/bolt/Socket.php:136

#1  (stripped)/vendor/stefanak-michal/bolt/Socket.php(136): socket_read(Resource id #15, 2, 2)
#2  (stripped)/vendor/stefanak-michal/bolt/Socket.php(104): Bolt\Socket::readBuffer(2)
#3 (stripped)/vendor/stefanak-michal/bolt/protocol/V4.php(41): Bolt\Socket::read(Object(Bolt\PackStream\v1\Unpacker))
#4  (stripped)/vendor/stefanak-michal/bolt/protocol/V4.php(21): Bolt\protocol\V4->pull()
#5  (stripped)/vendor/stefanak-michal/bolt/Bolt.php(240): Bolt\protocol\V4->pullAll()
#6  (stripped)/Helper/DB.php(27): Bolt\Bolt->pull()

v5.3

Clipboard01

New field in extra bolt_agent will be required. Fill dictionary with values if they are missing.

Test with php 8

PHP 8 is out. Try this library with this brand new php version.

Neo4j sandbox

Check if sandbox has outside connection available and if, test this library with it.

TLS support

Option to added TLS support in socket_connect.

Missing the return names in the response

When i do a return in the query like RETURN results, access_token in the response where are always numbered arrays and not named arrays.

Maybe better to give always the return name as array name back? Of make some new function like formatted?

fread() is failing for me.

Describe the bug
I had issues using the Bolt protocol with some of my requests. it was intermittent which made it really difficult to troubleshoot. But i did find the issue and the fix.

To Reproduce
I don't know. it worked sometimes and not others.

Expected behavior
I expected my data to come back.

Desktop (please complete the following information):

  • OS: Ubuntu
  • PHP Version 7.4.3
  • Neo4j Version 4.1.3

Additional context

The Fix:
on file /src/connection/StreamSocket.php Line 95
the read() function fails sometimes.
I found this article helpful: https://stackoverflow.com/questions/14664409/fread-a-lot-slower-for-downloads-than-readfile

and then i changed this line:

public function read(int $length = 2048): string
    {
        $res = fread($this->stream, $length); // <-- right here... fread fails a lot and now they recommend stream_get_contents()
        if (empty($res))
            throw new ConnectException('Read error');

        if (Bolt::$debug)
            $this->printHex($res, false);

        return (string)$res;
    }

to this:

public function read(int $length = 2048): string
    {
        $res = stream_get_contents($this->stream, $length);
        if (empty($res))
            throw new ConnectException('Read error');

        if (Bolt::$debug)
            $this->printHex($res, false);

        return (string)$res;
    }

worked like a champ after that.

Timeout does not work

Hi,

One of my neo4j servers had a problem. But the script has a default timeout of 15 but didn't work.

Remove Bolt:error(...) with exception?

Benifit:

  • Clear code reading
  • Easy catch with some specific error

Example with connection refused we can throw an exception like BoltConnectionException or when connect timeout BoltConnectionTimeoutException.

Create debugHandler

With turned on debug it prints everything out directly. Implement custom debugHandler which will be called.

It can check if debugHandler is (bool) true and do like now.

Maybe a Bolt issue

Hi everyone, newbie web developer and Neo4j user here.

We're facing an issue with Neo4j Community Edition hosted in Azure. We've developed an API that uses Laudis Neo4j PHP Client and Bolt library.

When using the API in local development, for instance, through Postman, everything works perfect. But when using the API hosted in Azure, we get HTTP status code 500 most of the time. When performing queries used in the API in Neo4j Browser, it also works.

Inspecting the debug.log file, we've found these errors and warnings:

2021-09-01 02:09:13.893+0000 ERROR [o.n.b.t.p.ProtocolHandshaker] Fatal error occurred during protocol handshaking: [id: 0x9582075a, L:/10.0.0.1:7687 - R:/123.456.789.12:34294]
java.lang.SecurityException: An unencrypted connection attempt was made where encryption is required.
	at org.neo4j.bolt.transport.pipeline.ProtocolHandshaker.assertEncryptedIfRequired(ProtocolHandshaker.java:151) ~[neo4j-bolt-4.2.6.jar:4.2.6]
	at org.neo4j.bolt.transport.pipeline.ProtocolHandshaker.channelRead(ProtocolHandshaker.java:89) ~[neo4j-bolt-4.2.6.jar:4.2.6]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.handlerRemoved(ByteToMessageDecoder.java:253) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:515) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at org.neo4j.bolt.transport.pipeline.AuthenticationTimeoutTracker.channelRead(AuthenticationTimeoutTracker.java:45) [neo4j-bolt-4.2.6.jar:4.2.6]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.63.Final.jar:4.1.63.Final]
	at java.lang.Thread.run(Thread.java:829) [?:?]
2021-09-01 02:09:14.246+0000 DEBUG [i.n.h.s.SslHandler] [id: 0xbe948ddf, L:/10.0.0.1:7687 - R:/123.456.789.12:34294] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
2021-09-01 02:09:14.344+0000 DEBUG [o.n.b.t.p.ProtocolHandshaker] Failed Bolt handshake: Bolt versions suggested by client '[org.neo4j.bolt.BoltProtocolVersion@3c2, org.neo4j.bolt.BoltProtocolVersion@3c1, org.neo4j.bolt.BoltProtocolVersion@3c1, org.neo4j.bolt.BoltProtocolVersion@3c1]' are not supported by this server.
2021-09-01 02:24:19.249+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1484, gcTime=0, gcCount=0}
2021-09-01 02:46:03.915+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1499, gcTime=0, gcCount=0}
2021-09-01 04:07:37.441+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1483, gcTime=1484, gcCount=2}
2021-09-01 06:07:34.077+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1492, gcTime=0, gcCount=0}

2021-09-01 08:00:08.712+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1490, gcTime=1511, gcCount=2}
2021-09-01 08:00:10.276+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1463, gcTime=1505, gcCount=3}
2021-09-01 08:00:11.833+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1456, gcTime=1502, gcCount=2}
2021-09-01 08:00:13.387+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1454, gcTime=1506, gcCount=2}
2021-09-01 08:00:14.938+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1451, gcTime=1510, gcCount=2}
2021-09-01 08:00:16.480+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1441, gcTime=1503, gcCount=2}
2021-09-01 08:00:18.040+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1460, gcTime=1522, gcCount=2}
2021-09-01 08:00:19.582+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1441, gcTime=1506, gcCount=2}
2021-09-01 08:00:21.127+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1445, gcTime=1511, gcCount=2}
2021-09-01 08:00:22.690+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1462, gcTime=1514, gcCount=2}
2021-09-01 08:03:19.453+0000 WARN  [o.n.k.i.c.VmPauseMonitorComponent] Detected VM stop-the-world pause: {pauseTime=1450, gcTime=1497, gcCount=2}

Moreover, looking at the server logs, we've found exceptions:

2021-09-07 13:43:54	Error	01.234.567.890	500	GET /endpoint?params HTTP/1.0		Symfony HttpClient/Curl	342 	Accรจs SSL/TLS Apache
2021-09-07 13:44:09	Error	01.234.567.890	AH01071: Got error 'PHP message: PHP Fatal error: Uncaught Bolt\\error\\ConnectException: Read error in /vendor/stefanak-michal/bolt/src/connection/StreamSocket.php:102\nStack trace:\n#0 /vendor/stefanak-michal/bolt/src/protocol/AProtocol.php(94): Bolt\\connection\\StreamSocket->read()\n#1 /vendor/stefanak-michal/bolt/src/protocol/V3.php(72): Bolt\\protocol\\AProtocol->read()\n#2 /vendor/stefanak-michal/bolt/src/Bolt.php(262): Bolt\\protocol\\V3->run()\n#3 /vendor/laudis/neo4j-php-client/src/Bolt/BoltUnmanagedTransaction.php(121): Bolt\\Bolt->run()\n#4 /vendor/laudis/neo4j-php-client/src/Bolt/Session.php(77): Laudis\\Neo4...'				Erreur Apache
2021-09-07 19:11:16	Error	012.34.567.89	400	GET / HTTP/1.0

We've updated both repositories' versions, but it didn't help.

Would anyone have an idea on how to solve this?

Please let me know if I didn't post this issue in the correct repository.
Thank you very much in advance !

Neo4j 4.0 support

I know there is no documentation, but maybe you can look in to the official Neo4j drivers?

Bolt\connection\Socket write method

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior.

Expected behavior
A clear and concise description of what you expected to happen.

Desktop (please complete the following information):

  • OS: [e.g. Win 10 Pro]
  • PHP Version [e.g. 7.1.22]
  • Neo4j Version [e.g. 4.1.7]
  • Bolt Library Version [e.g. v1.2 or master]

Additional context
Add any other context about the problem here.

Add pipelining

Add support for pipelining bolt messages https://www.neo4j.com/docs/bolt/current/bolt/message/#pipelining

Messages may also be pipelined. In other words, clients may send multiple requests eagerly without first waiting for responses. When a failure occurs in this scenario, servers must ignore all subsequent requests until the client has explicitly acknowledged receipt of the failure. This prevents inadvertent execution of queries that may not be valid. More details of this process can be found in the sections below.

dumpResponses

I was thinking about adding new method to AProtocol called dumpResponses. It will serve to dump all pending responses ready to be fetched. Now if you want to do that you have to call something like this:

iterator_to_array($protocol->getResponses(), false);

So with this new method it will be:

$protocol->dumpResponses();

Any thoughts?

Bolt\error\ConnectException when sending large chunks of data.

Describe the bug
When I try to send data of around 100kb, Bolt freezes for a while and then throws a Bolt\error\ConnectException. If the size is around 50kb, the driver works pretty much instantly.

To Reproduce

$bolt = new Bolt(new StreamSocket('<host>'));
$bolt->init('MyClient/1.0', '<user>', '<password>');

$bolt->run('RETURN 1 AS x, ['content' => <Really long string of around 100kb>]);

Expected behavior
The query runs near instantly.

Desktop (please complete the following information):

  • OS: Linux box 5.12.0-1-MANJARO #1 SMP PREEMPT Mon Apr 26 22:09:28 UTC 2021 x86_64 GNU/Linux, but within Docker.
  • PHP Version: 7.4.18
  • Neo4j Version 4.2.5
  • Bolt Library Version v2.1

Additional context
I ran across this issue while working on my web crawlers. You can easily generate a text based on the number of bytes using lorem ipsum

Unable to connect to Aura Hosted Neo4j Database

Describe the bug
I have tried different combinations of connection strings and versions, but can't get it to work.
It seems to be crashing inside the handshake function everytime. Looking at the socket read function it is just never ending with the content just being empty.

To Reproduce
Setup an aura database
Try connecting to it using the bolt protocol

Expected behavior
Connection should be established and handshake successful

Desktop:

  • OS: Ubuntu 20
  • PHP Version 7.4
  • Neo4j Version [e.g. 4.1.7
  • Bolt Library Version 4

Enable SSL settings for Socket

Is your feature request related to a problem? Please describe.
I want to enable the Sockets extension if it is installed for the driver. The problem is that it does not have a setSslContextOptions function like the StreamSocket which means the speedup will be impossible for Aura users as I cannot enable SSL.

Describe the solution you'd like
I would love the same method setSslContextOptions on the Socket for feature parity. It might be best to include in the Interface. What do you think?

Kind regards,

Ghlen

v2.7 - Call to undefined method Bolt\Bolt::setScheme() with NoAuth (NEO4J_AUTH=none)

Describe the bug
Ran composer update, using neo4j-php-client upgraded Bolt to 2.7 which then broke queries with the following error:

PHP Error:  Call to undefined method Bolt\Bolt::setScheme() in /var/www/html/vendor/laudis/neo4j-php-client/src/Authentication/NoAuth.php on line 41

To Reproduce
Connect to Neo4j with Auth set to none (e.g. NEO4J_AUTH=none)

Expected behavior
Connection to be established successfully.

Desktop (please complete the following information):

  • OS: MacOS 12.0.1
  • PHP Version: 8.1.0
  • Neo4j Version 4.4
  • Bolt Library Version v2.7 - ref: a5d7a8e9a5073fa93f185621ff4e8a1c0ddab877

Additional context
N/A

Update wiki

After merge into master before creating new release update wiki to follow new changes.

Class 'Bolt\Bolt' not found

Hi,

awesome projectidea! I am looking very much for a new way to connect PHP to Neo4J 4.x. I tried your driver but it seems I do something wrong, maybe with the installation. When I open the index.php I run into

Fatal error: Uncaught Error: Class 'Bolt\Bolt' not found in C:\xampp\htdocs\Bolt-master\index.php:22

Obviously I missed something in the setup - here is what I've done:

  1. I unzipped the code into my htdocs
  2. I checked my php.ini (XAMPP) and enabled 'extension=sockets' in my php.ini
  3. I run the composer to make sure I have all what is needed with the result:

C:\xampp\htdocs\Bolt-master>composer update Loading composer repositories with package information Updating dependencies (including require-dev) Nothing to install or update Writing lock file Generating autoload files

  1. I tested and it seems the index.page loads the "autoload.php" but crashes then when trying to use class BOLT.

Any idea what I made wrong?
Thank you

Use multiple databases

Since Neo4j 4.0 you can have multiple databases.

How can i use multiple databases with this package?

Read error when writing big random data

Describe the bug
When creating parameters with random data, a standard read error appears. When testing big strings, it works flawlessly, but with big random data is crashes.

To Reproduce

$socket = new \Bolt\connection\StreamSocket('neo4j');
$neo = new \Bolt\Bolt($socket);
$neo->init('MyClient/1.0', 'neo4j', 'test');
$neo->begin();

$neo->run('MERGE (a:label {id:"xyz"})');

$params = [
    'id' => 'xyz',
];

while (true) {
    try {
        // show current length of parameters array
        echo strlen(serialize($params)).PHP_EOL;

        $check = $neo->run('MATCH (a :label {id:$id}) RETURN a', $params);

        $params[base64_encode(random_bytes(32))] = base64_encode(random_bytes(128));
    } catch (Exception $e) {
        echo $e;

        $neo->run('MATCH (a:label {id:"xyz"}) DELETE a');

        exit;
    }
}

Expected behaviour
This script should be able to run for as long as the server or connection does not crash.

Desktop (please complete the following information):

  • OS: Docker version 20.10.8, build 3967b7d28e in Linux xps 5.13.13-1-MANJARO SMP PREEMPT Thu Aug 26 20:34:21 UTC 2021 x86_64 GNU/Linux
  • PHP Version PHP 8.0.7 (cli) (built: Jun 4 2021 18:34:28) ( NTS )
  • Neo4j Version: 4.3.2
  • Bolt Library Version v2.3

Additional context
This problem came to light from this issue in the client: neo4j-php/neo4j-php-client#70.

Structures implementation ideas?

Now the structures are not part of PackStream, which should be. But if we implement Structures into it, it will be problem in future because of versioning.

For better understanding: Right now programmer has to implement logic with \Bolt\structures\Node. But if we move it, he has to change namespace to \Bolt\PackStream\v1\structures\Node. In future if there will be new version of PackStream, there will be need to change it again to \Bolt\PackStream\v2\structures\Node.

That is not ideal. Question is, how to get rid of this dependancy and keep up required accessibility?

  • Option 1 can be to return array instead of structure classes
  • Option 2 which I used in one project but I don't like it, I've implemented magic fnc __toString into structures and made some logic into each one. In complicated structures like Node I return data as json string. Unpacker then casting this classes to string before it returns them.

Examples are needed

Hi!
Do you plan to add any examples of real usage? How to connect, make Cypher queries etc.
Thanks.

Issue with geolocation points

I am using the laudis/neo4j-php-client that uses your Bolt package. I am performing a geolocation search on Neo4j of a point with a set of nodes that each define their border through a set of points. Essentially, I am doing a point-in-polygon search. To speed the search, the first step is to return the set of nodes whose bounding box (defined by a min and a max point) MAY contain the point. I do this with the following Neo4j query:

MATCH (b:Border) WHERE b.min < point({latitude: 42.020110, longitude: -74.084010}) < b.max RETURN b;

This query works fine with the Neo4j Browser and with the laudis/neo4j-php-client using the HTTP protocol. However, with the BOLT protocol, I get the following error:

PHP Fatal error: Uncaught Bolt\error\UnpackException: Structure call for method "unpackFloat" generated unpack error in /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php:194
Stack trace:
#0 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(170): Bolt\PackStream\v1\Unpacker->unpackSpecificStructure()
#1 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(103): Bolt\PackStream\v1\Unpacker->unpackStruct()
#2 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(328): Bolt\PackStream\v1\Unpacker->u()
#3 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(126): Bolt\PackStream\v1\Unpacker->unpackList()
#4 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(222): Bolt\PackStream\v1\Unpacker->u()
#5 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(192): Bolt\PackStream\v1\Unpacker->unpackMap()
#6 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(170): Bolt\PackStream\v1\Unpacker->unpackSpecificStructure()
#7 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(103): Bolt\PackStream\v1\Unpacker->unpackStruct()
#8 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(328): Bolt\PackStream\v1\Unpacker->u()
#9 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(126): Bolt\PackStream\v1\Unpacker->unpackList()
#10 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/PackStream/v1/Unpacker.php(79): Bolt\PackStream\v1\Unpacker->u()
#11 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/protocol/AProtocol.php(103): Bolt\PackStream\v1\Unpacker->unpack()
#12 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/protocol/V4.php(39): Bolt\protocol\AProtocol->read()
#13 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/protocol/V4.php(25): Bolt\protocol\V4->pull()
#14 /home/mydistrict/public_html/neo4j/v0.1/vendor/stefanak-michal/bolt/src/Bolt.php(274): Bolt\protocol\V4->pullAll()
#15 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Network/Bolt/BoltSession.php(130): Bolt\Bolt->pullAll()
#16 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Network/Bolt/BoltSession.php(57): Laudis\Neo4j\Network\Bolt\BoltSession->runStatements()
#17 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Client.php(54): Laudis\Neo4j\Network\Bolt\BoltSession->run()
#18 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Client.php(46): Laudis\Neo4j\Client->runStatements()
#19 /home/mydistrict/public_html/neo4j/v0.1/Application/Models/Neo4jBase.php(336): Laudis\Neo4j\Client->runStatement()
#20 /home/mydistrict/public_html/neo4j/v0.1/Application/Models/District.php(116): Model\Neo4jBase->queryMultiple()
#21 /home/mydistrict/public_html/neo4j/v0.1/tests/district_geolocation.php(22): Model\District->getByGeoLocation()
#22 {main}

Next Laudis\Neo4j\Exception\Neo4jException: Neo4j errors detected. First one with code "" and message "Structure call for method "unpackFloat" generated unpack error" in /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Network/Bolt/BoltSession.php:132
Stack trace:
#0 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Network/Bolt/BoltSession.php(57): Laudis\Neo4j\Network\Bolt\BoltSession->runStatements()
#1 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Client.php(54): Laudis\Neo4j\Network\Bolt\BoltSession->run()
#2 /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Client.php(46): Laudis\Neo4j\Client->runStatements()
#3 /home/mydistrict/public_html/neo4j/v0.1/Application/Models/Neo4jBase.php(336): Laudis\Neo4j\Client->runStatement()
#4 /home/mydistrict/public_html/neo4j/v0.1/Application/Models/District.php(116): Model\Neo4jBase->queryMultiple()
#5 /home/mydistrict/public_html/neo4j/v0.1/tests/district_geolocation.php(22): Model\District->getByGeoLocation()
#6 {main}
thrown in /home/mydistrict/public_html/neo4j/v0.1/vendor/laudis/neo4j-php-client/src/Network/Bolt/BoltSession.php on line 132

To Reproduce
Execute the above Neo4j query using the laudis/neo4j-php-driver configured to use BOLT.

Expected behavior
Return of the nodes - does work properly with the HTTP protocol.

Desktop (please complete the following information):

  • OS: Ubuntu 20.04
  • PHP Version 8.0.2
  • Neo4j Version 4.1.6
  • Bolt Library Version master

Additional context
Add any other context about the problem here.

Improve MessageException for v3

Is your feature request related to a problem? Please describe.
It is a shame the message and code during failure gets jumbled together as a single message. This makes it hard to extract the code and message again.

Describe the solution you'd like
Add getServerErrorMessage and getServerErrorCode methods to the class. The class should then have a constructor looking like this:

public function __construct(string $serverMessage, string $severCode) {
    parent::__construct($serverMessage . '(' . $serverCode . ')');
}

This solution would not break backwards compatibility and provide a clean way for other libraries to keep the server code and message separated.

Describe alternatives you've considered
The alternative as a library implementer right now is to extract the errorcode with a regex from the getMessage method, which is error-prone and cannot guarantee correctness.

$bolt->pull() returns private objects

Hello there, when I run this code,

$res = $bolt->run('
	MATCH ()-->() RETURN count(*)
');

This is the output of print_r($bolt->pull()); :

Array
(
    [0] => Array
        (
            [0] => 7000000
        )

    [1] => Array
        (
            [bookmark] => FB:kcwQh+8j7SWhTdq4HJXlCqJs0j6Q
            [type] => r
            [t_last] => 0
            [db] => neo4j
        )

)

I can access the result by $bolt->pull()[0][0];

However when I run this query:

MATCH p=shortestPath(
	(u1:User {userID:"270350200"})-[:follows*]->(u2:User {userID:"17911468618"})
)
RETURN p

This is the output of print_r($bolt->pull()); :

Array
(
    [0] => Array
        (
            [0] => Bolt\structures\Path Object
                (
                    [nodes:Bolt\structures\Path:private] => Array
                        (
                            [0] => Bolt\structures\Node Object
                                (
                                    [id:Bolt\structures\Node:private] => 1000015
                                    [labels:Bolt\structures\Node:private] => Array
                                        (
                                            [0] => User
                                        )

                                    [properties:Bolt\structures\Node:private] => Array
                                        (
                                            [userID] => 270350200
                                        )

                                )

                            [1] => Bolt\structures\Node Object
                                (
                                    [id:Bolt\structures\Node:private] => 1000527
                                    [labels:Bolt\structures\Node:private] => Array
                                        (
                                            [0] => User
                                        )

                                    [properties:Bolt\structures\Node:private] => Array
                                        (
                                            [userID] => 11126166
                                        )

                                )

                            [2] => Bolt\structures\Node Object
                                (
                                    [id:Bolt\structures\Node:private] => 1007260
                                    [labels:Bolt\structures\Node:private] => Array
                                        (
                                            [0] => User
                                        )

                                    [properties:Bolt\structures\Node:private] => Array
                                        (
                                            [userID] => 2463846399
                                        )

                                )

                            [3] => Bolt\structures\Node Object
                                (
                                    [id:Bolt\structures\Node:private] => 1000095
                                    [labels:Bolt\structures\Node:private] => Array
                                        (
                                            [0] => User
                                        )

                                    [properties:Bolt\structures\Node:private] => Array
                                        (
                                            [userID] => 1043081670
                                        )

                                )

                            [4] => Bolt\structures\Node Object
                                (
                                    [id:Bolt\structures\Node:private] => 1000007
                                    [labels:Bolt\structures\Node:private] => Array
                                        (
                                            [0] => User
                                        )

                                    [properties:Bolt\structures\Node:private] => Array
                                        (
                                            [userID] => 17911468618
                                        )

                                )

                        )

                    [rels:Bolt\structures\Path:private] => Array
                        (
                            [0] => Bolt\structures\UnboundRelationship Object
                                (
                                    [id:Bolt\structures\UnboundRelationship:private] => 1105
                                    [type:Bolt\structures\UnboundRelationship:private] => follows
                                    [properties:Bolt\structures\UnboundRelationship:private] => Array
                                        (
                                        )

                                )

                            [1] => Bolt\structures\UnboundRelationship Object
                                (
                                    [id:Bolt\structures\UnboundRelationship:private] => 147354
                                    [type:Bolt\structures\UnboundRelationship:private] => follows
                                    [properties:Bolt\structures\UnboundRelationship:private] => Array
                                        (
                                        )

                                )

                            [2] => Bolt\structures\UnboundRelationship Object
                                (
                                    [id:Bolt\structures\UnboundRelationship:private] => 2769516
                                    [type:Bolt\structures\UnboundRelationship:private] => follows
                                    [properties:Bolt\structures\UnboundRelationship:private] => Array
                                        (
                                        )

                                )

                            [3] => Bolt\structures\UnboundRelationship Object
                                (
                                    [id:Bolt\structures\UnboundRelationship:private] => 8225
                                    [type:Bolt\structures\UnboundRelationship:private] => follows
                                    [properties:Bolt\structures\UnboundRelationship:private] => Array
                                        (
                                        )

                                )

                        )

                    [ids:Bolt\structures\Path:private] => Array
                        (
                            [0] => 1
                            [1] => 1
                            [2] => 2
                            [3] => 2
                            [4] => 3
                            [5] => 3
                            [6] => 4
                            [7] => 4
                        )

                )

        )

    [1] => Array
        (
            [bookmark] => FB:kcwQh+8j7SWhTdq4HJXlCqJs0j6Q
            [type] => r
            [t_last] => 1
            [db] => neo4j
            [notifications] => Array
                (
                    [0] => Array
                        (
                            [severity] => WARNING
                            [description] => Using shortest path with an unbounded pattern will likely result in long execution times. It is recommended to use an upper limit to the number of node hops in your pattern.
                            [code] => Neo.ClientNotification.Statement.UnboundedVariableLengthPatternWarning
                            [position] => Array
                                (
                                    [column] => 42
                                    [offset] => 64
                                    [line] => 3
                                )

                            [title] => The provided pattern is unbounded, consider adding an upper limit to the number of node hops.
                        )

                )

        )

)

I cant access the results neither $bolt->pull()[0][0]->nodes , $bolt->pull()[0][0]->rels , $bolt->pull()[0][0]->ids
they are all private variables, how can I access the result of that query?

Thank you.

MATCH part of Cypher query ignored?

Describe the bug
I am trying to retrieve data from my neo4j database via Laravel test commands run trough a terminal, but it seems that the MATCH part of my Cypher query is ignored

To Reproduce

$conn = new \Bolt\connection\Socket(env('NEO_DOCKER'), 7687);
$bolt = new \Bolt\Bolt($conn);
$protocol = $bolt->setProtocolVersions(3)->build();
$protocol->hello(\Bolt\helpers\Auth::basic(env('NEO_USER'), env('NEO_PASS')));   
$result = $protocol->run("MATCH (p:Personne {matr: 7007}) RETURN p.nom")->pullAll()->getResponse()->getContent();

var_dump($result);

> php artisan app:test-command
array(2) {
["t_first"]=>
int(4)
["fields"]=>
array(1) {
[0]=>
string(5) "p.nom"
}
}

Expected behavior
Retrieve p.nom actual content in the results, not the string "p.nom"

Desktop (please complete the following information):
(- OS: Win 11 pro)

  • Docker-compose configuration
  • PHP Version 8.2.6
  • Neo4j Version 3.5.35
  • Bolt Library Version 3

The connection part seems fine/does not return errors, but am I missing something in the config or querying?
Thanks

PS: an upgrade of the neo4j database is scheduled but not immediately

cypher map with integer keys does not work

Describe the bug
The case came up when integrating testkit with the driver. Apparently a map in cypher can have integer keys. I currently cannot find a way to do it without producing a read error. (my best guess would be to use a stdClass object with integer variable names)

To Reproduce

// standard bolt creation logic ...

$stdClass = new stdClass();
$val = 0;
$stdClass->$val = 'x';
$val = 1;
$stdClass->$val = 'y';
$bolt->run('RETURN $x AS x', ['x' => $stdClass]);

Expected behavior
That it returns a map or object of the form: {0: 'x', 1: 'y'}

Desktop (please complete the following information):

  • OS: [Linux box 5.14.10-1-MANJARO #1
  • PHP Version: 8.0.11
  • Neo4j Version: 4.3.6
  • Bolt Library Version: master

Additional context
Add any other context about the problem here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.