npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@brakebein/nest-neo4j

v1.3.0

Published

Neo4j integration for NestJS

Downloads

8

Readme

Nest Neo4j

This repository provides Neo4j integration for NestJS wrapping the official neo4j-driver.

It is a fork of adam-cowley/nest-neo4j with some changes:

  • the queried records are extracted to a simplified format
  • non-standard properties (e.g. dates) are converted
  • updated dependencies

Installation

$ npm install @brakebein/nest-neo4j

Setup

Register the Neo4j Module in your application using the forRoot method, passing the Neo4j connection information as an object. Specific Neo4j settings (refer to neo4j-driver) can be passed via options.

import { Module } from '@nestjs/common';
import { Neo4jModule } from '@brakebein/nest-neo4j';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    Neo4jModule.forRoot({
      scheme: 'neo4j',
      host: 'localhost',
      port: 7687,
      username: 'neo4j',
      password: 'neo',
      options: {
        disableLosslessIntegers: true,
      },
      verifyConnectionTimout: 60000,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Alternatively, you can use forRootAsync to access information dynamically via ConfigService:

import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Neo4jModule } from '@brakebein/nest-neo4j';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    Neo4jModule.forRootAsync({
      useFactory: async (configService: ConfigService) => ({
        scheme: configService.get('NEO4J_SCHEME'),
        host: configService.get('NEO4J_HOST'),
        port: configService.get('NEO4J_PORT'),
        username: configService.get('NEO4J_USERNAME'),
        password: configService.get('NEO4J_PASSWPRD'),
        database: configService.get('NEO4J_DATABASE'),
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

If the Neo4j instance is unavailable, connection is not possible. Driver instantiation will be re-invoked a few times before throwing an error (useful in cases like server startup when node application is live earlier than the Neo4j database instance). Set verifyConnectionTimeout to increase the timeout before throwing an error.

Querying Neo4j

The Neo4jService is @Injectable, so can be passed into any constructor:

import { Neo4jService } from '@brakebein/nest-neo4j';

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly neo4j: Neo4jService,
  ) {}

  @Get()
  async getHello(): Promise<any> {
    const results = await this.neo4j.read(`MATCH (n) RETURN count(n) AS count`);

    return `There are ${results[0].count} nodes in the database`;
  }
}

Methods

The query results returned by neo4j-driver are mapped to objects that correlate with identifiers within the RETURN clause and the node's properties. To access the raw query results, use the respective methods, e.g. .readRaw().

.read(cypher: string, params?: Record<string, any>, database?: string)

A very simple read transaction that expects a cypher statement and (optionally) query parameters.

Returns an array of objects, where the object's property names correlate with identifiers within the RETURN clause.

const query = `
  MATCH (p:Person {name: $name})-[:HAS_ADDRESS]->(add:Address)
  RETURN p.name AS name, add AS address
`;

const params = {
  name: 'Alex'
};

const results = await this.neo4j.read(query, params);
console.log(results);

// console.log(results)
// [
//   {
//     name: 'Alex',
//     address: {
//       ZIP: '10178',
//       number: '1',
//       town: 'Berlin',
//       street: 'Alexanderplatz'
//     }
//   }
// ]

Use .readRaw() to get the raw query results.

.write(cypher: string, params?: Record<string, any>, database?: string)

Very similar to .read() (see for details) except that it expects a cypher statement that modifies the database.

Use .writeRaw() to get the raw query results.

.multipleStatements(statements: { statement: string; parameters: Record<string, any> }[])

Execute multiple cypher queries within one transaction. A fail of one statement will lead to the rollback of the whole transaction.

Returns an array of arrays of objects (similar to .read() or .write()).

const statements = [{
  statement: `CREATE ...`,
  parameters: {}
}, {
  statement: `MATCH ... CREATE (n:Node $map) ...`,
  parameters: { map: { value: 'foo' } }
}];

const results = await this.neo4j.multipleStatements(statements);
// handle results

Use .multipleStatementsRaw() to get an array of the raw query results.

.getDriver()

Get the driver instance to access the full API of neo4j-driver.

.getConfig()

Get configuration as provided with Neoj4Module.

.getReadSession(database?: string)

Acquire a READ session to execute, e.g., explicit transactions.

.getWriteSession(database?: string)

Acquire a WRITE session to execute, e.g., explicit transactions.

.extractRecords<T = any>(data: Record[]): T[]()

Used internally to extract and convert the returned records by neo4j-driver to a more simplified format. It converts non-standard values, like date, time, etc., to strings as well as Neo4j integers, if they are outside the safe range.

Takes an array of Neo4j records Record[] and returns an array of objects.

import { Neo4jService } from './neo4j.service';

const query = `
  MATCH (p:Person {name: "Alex"})-[:HAS_ADDRESS]->(add:Address)
  RETURN p.name AS name, add AS address
`;

// query results returned by neo4j-driver
// {
//   records: [
//     Record {
//       keys: [ 'name', 'address' ],
//       length: 2,
//       _fields: [
//         'Alex',
//         Node {
//           identity: 1,
//           labels: [ 'Address' ],
//           properties: {
//             ZIP: '10178',
//             number: '1',
//             town: 'Berlin',
//             street: 'Alexanderplatz'
//           }
//         }
//       ]
//       _fieldLookup: { name: 0, address: 1 }
//     }
//   ],
//   summary: ResultSummary {...}
// }

extractRecords(queryResults.records);

// simplified records returned by neo4j-request
// {
//   name: 'Alex',
//   address: {
//     ZIP: '10178',
//     number: '1',
//     town: 'Berlin',
//     street: 'Alexanderplatz'
//   }
// }

.removeEmptyArrays<T>(data: T[], arrayKey: string, checkKey: string): T[]

Look for empty arrays returned by Neo4j and clean them, if there is null inside.

Sometimes, if the cypher query contains OPTIONAL MATCH node in combination with collect({key: node.value}) AS values, the resulting array may be filled with one object with null values: [{key: null}]. This method reduces the array to [] by calling removeEmptyArrays(data, 'values', 'key').

const query = `
  MATCH (p:Person {name: "Alex"})-[:HAS_ADDRESS]->(add:Address)
  OPTIONAL MATCH (p)-[:HAS_FRIEND]->(f:Person)-[:HAS_ADDRESS]->(fAddr:Address)
  RETURN p.name AS name,
         add AS address,
         collect({name: f.name, address: fAddr}) AS friends
`;

const results = await this.neo4j.read(query);
console.log(results);

// [
//   {
//     name: 'Alex',
//     address: {
//       ZIP: '10178',
//       number: '1',
//       town: 'Berlin',
//       street: 'Alexanderplatz'
//     },
//     friends: [ { address: null, name: null } ]
//   }
// ]

const resultsCleaned = this.neo4j.removeEmptyArrays(results, 'friends', 'name');
console.log(resultsCleaned);

// [
//   {
//     name: 'Alex',
//     address: {
//       ZIP: '10178',
//       number: '1',
//       town: 'Berlin',
//       street: 'Alexanderplatz'
//     },
//     friends: []
//   }
// ]