0.3.1 • Published 1 year ago

@seihmd/spinel v0.3.1

Weekly downloads
-
License
MIT
Repository
-
Last release
1 year ago

Spinel

Decorator based OGM for Neo4j.

Currently in beta version with several steps left before v1 release

  • some additional features
  • detailed documentation
  • bug fixes
  • quality implovement

Installing

npm install @seihmd/spinel

Usage

Define entities

// Define Node entity
@NodeEntity('User')
class User {
  @Primary()
  private id: string; // Entity must have one primary property

  @Property()
  private name: string;

  constructor(id: string, name: string) {
    this.id = id;
    this.name = name;
  }
}
// Define Relationship entity
@RelationshipEntity('FOLLOWS')
class Follows {
  @Primary()
  private id: string;

  constructor(id: string) {
    this.id = id;
  }
}

Define graphs

graph TD
    A(follower) -->|follows| B(user)
// Define Node-Relationship-Node graph
@Graph('user<-follows-follower')
class UserAndFollower {
  @GraphNode()
  private user: User;

  @GraphRelationship()
  private follows: Follows;

  @GraphNode()
  private follower: User;

  constructor(user: User, follows: Follows, follower: User) {
    this.user = user;
    this.follows = follows;
    this.follower = follower;
  }
}
graph TD
    A(follower) -->|:FOLLOWS| B(user)
    C(follower) -->|:FOLLOWS| B(user)
@Graph('user')
class UserAndFollowers {
  @GraphNode()
  private user: User;

  @GraphBranch(User, 'user<-[:FOLLOWS]-.')
  private followers: User[];

  constructor(user: User, followers: User[]) {
    this.user = user;
    this.followers = followers;
  }
}
graph TD
    A(:User) -->|:FOLLOWS| B(UserAndFollowers.user)
    C(:User) -->|:FOLLOWS| B(UserAndFollowers.user)
    A(:User) -->|:FOLLOWS| D(FollowedUser.user)
    C(:User) -->|:FOLLOWS| D(FollowedUser.user)
@GraphFragment('-[:FOLLOWS]->user')
class FollowedUser {
  @GraphNode()
  private user: User;

  constructor(user: User) {
    this.user = user;
  }
}

@Graph('user')
class UserAndFollowers {
  @GraphNode()
  private user: User;

  @GraphBranch(User, 'user<-[:FOLLOWS]-(:User)')
  private usersFollowedBySame: FollowedUser[];

  constructor(user: User, usersFollowedBySame: FollowedUser[]) {
    this.user = user;
    this.usersFollowedBySame = usersFollowedBySame;
  }
}

Configure

Run configure() at the application entry point.

import { configure } from '@seihmd/spinel';

configure({
  entities: [User, Follows]
});

By default, Neo4j host, user, and password are read from environment variables:

  • SPINEL_HOST
  • SPINEL_USER
  • SPINEL_PASSWORD

Alternatively, the values can be set explicitly.

configure({
  host: 'neo4j://localhost',
  user: 'neo4j',
  password: 'pass',
  entities: [User, Follows]
});

Querying

QueryDriver

QueryDriver is the entry point for query execution.

const qd = getQueryDriver();

Find

// Find entities
const users = await qd
  .find(User)
  .where("user.id IN :userIds") // In WHERE statement, Use capitalCased Entity name
  .orderBy("u.id", 'ASC')
  .limit(2)
  .buildQuery({
    userIds: ['1', '2']
  })
  .run()   // User[]
;

// Find graphs
const userAndFollowersList = await qd
  .find(UserAndFollowers)
  .where("user.id IN :userIds")
  .orderBy("u.id", 'ASC')
  .limit(2)
  .buildQuery({
    userIds: ['1', '2']
  })
  .run()   // UserAndFollowers[]
;

FindOne

const query = await qd
  .find(User)
  .where("user.id = $userId")
  .buildQuery({
    userId: '1'
  })
  .run()  // User|null
;

Save

const user = new User();
// Save node entity
await qd.save(user);

// Save graph
const userAndFollowers = new UserAndFollowers(user, [new User()]);
await qd.save(userAndFollowers);

Detach

Detach between nodes

// Detach any relationships having direction `user -> user2`.
await qd.detach(user, user2);

// Detach specified relationship having direction `user -> user2`.
await qd.detach(user, user2, 'FOLLOWS');

// Detach specified relationship having direction `user <- user2`.
await qd.detach(user, user2, 'FOLLOWS', '<-');

// Detach specified relationship regardless direction.
await qd.detach(user, user2, 'FOLLOWS', '-');

// Relationship constructor can also be passed.
await qd.detach(user, user2, Follows);

Delete

Delete node

await qd.delete(user);

Delete relationship and graph

.delete() does not support Relationship and Graph. Use .detach() or .detachDelete() instead.

Detach Delete

// DETACH DELETE the node
await qd.detachDelete(user);

// DETACH DELETE entities included in the graph
await qd.detachDelete(userAndFollowers);

Transaction

Use .transactional(). Rollback is executed When callback throws an error.

await qd.transactional(async (qd: QueryDriver) => {
  const userAndFollowers = await qd
    .findOne(UserAndFollowers)
    .where('uhop.id=$userId')
    .buildQuery({
      userId: '1',
    })
    .run();

  userAndFollowers.addFollower(new User());

  await qd.save(userAndFollowers);
});

Manage Constraints and Indexes

Add constraints

Unique node property constraints

@NodeEntity('User', {
  unique: ['name']
})
class User {
  @Primary()
  private id: string;

  @Property()
  private name: string;
}

Node/Relationship property existence constraints

@NodeEntity('User')
class User {
  @Primary()
  private id: string;

  @Property({ notNull: true })
  private name: string;
}

Node key constraints

@NodeEntity('User', {
  keys: [['name', 'address']]
})
class User {
  @Primary()
  private id: string;

  @Property()
  private name: string;

  @Property()
  private address: string;
}

Add Indexes

Currentry supports btree, text and full-text index types.

@NodeEntity('User', {
  indexes: [
    {
      type: 'btree',
      on: ['name'],
    },
    {
      name: 'index_IndexTestNode_arbitrary_name',
      type: 'fulltext',
      on: ['name', 'description'],
    },
  ],
})
class User {
  @Primary()
  private id: string;

  @Property()
  private name: string;

  @Property()
  private description: string;
}

Synchronize with database

await qd.syncConstraints();
0.3.0

1 year ago

0.3.1

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.5

1 year ago

0.1.4

1 year ago

0.1.3

1 year ago

0.1.2

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago