jamesfer / cypher-query-builder

An flexible and intuitive query builder for Neo4j and Cypher.
http://jamesfer.me/cypher-query-builder/index.html
MIT License
104 stars 22 forks source link

Extended typing for queries #172

Closed Bastianowicz closed 3 years ago

Bastianowicz commented 3 years ago

hey as i wrote in discussions i was eager to use typing for all the cypher-methods based on the current state of the query. This means: by calling match you can now define what the graph model looks like that you are querying. Thus you can be sure you only use properties in the further steps that do exist. You can cast the Builder-Instance to another state (i.e. change the type of graphmodel with changeType<N>())

All the tests do run and there is not so much change to the actual logic. I used default types to make sure it is compatible to older versions as well and that you don't have to use these typings if you don't want to. I see that it looks way more complicated in the library due to the extended use of typings but I am convinced that these typings can prevent you from a lot of errors while using the lib.

I still have to write some docs on how to use these optional typings and as well want do add some tests which will be pretty much the same as yours except for the typings. but first i wanted you to have a look and hear your opinion.

Bastianowicz commented 3 years ago

See some usage examples. The benefit is you cannot use unknown properties and code hints are available.

import { node, Query, relation } from './src';

interface User {
  name: string;
}

interface Item {
  name: string;
  price: number;
}

interface KnowRelation {
  trust: number;
}

interface OwnRelation {
  since: Date;
}

interface UserUserItemRelation{
  user1: User;
  user2: User;
  item: Item;
  knows: KnowRelation;
  owns: OwnRelation;
}

interface Unrelated{
  key: boolean;
}

interface UserItemRelation extends Omit<UserUserItemRelation, 'user2'>{}

function matchSingle() {
  const query = new Query<UserUserItemRelation>();
  return query.matchNode<User>('user1', 'User', { name: 'harald' });
}

function matchMultiple() {
  const query = new Query();
  return query.match<UserUserItemRelation, User>(
    [
      node<UserUserItemRelation, User>('user2', 'User', { name: 'john' }),
      relation<UserUserItemRelation, KnowRelation>('out', 'knows', 'Item', { trust:100 }),
      node<UserUserItemRelation, Item>('item', 'k', { price:100 }),
      node<UserUserItemRelation, User>('item', 'k', { name: 'Harry' }),
      node<UserUserItemRelation>('item', 'k', { price : 100 }),
    ],
  ).match<UserItemRelation, User&Item>(
      node<UserItemRelation, User>('user1', 'User', { name: 'john' }),
  );
}

function remove() {
  const query = new Query<UserUserItemRelation>();
  query.remove<User>({ labels: { user2: 'any' }, properties: { user2: 'name' } });
}

function removeProperties() {
  const query = new Query<UserUserItemRelation>();
  query.removeProperties<Item&User>({
    user2: ['name', 'price'],
    item: 'name',
  });
}

function createNode() {
  const query = new Query<UserUserItemRelation>();
  query.createNode<{ unrelated : Unrelated }>('unrelated', 'Unrelated', { key: true });
}

function deleteSth() {
  const query = new Query<UserUserItemRelation>();
  query.delete<Omit<UserUserItemRelation, 'user2'>>('user2').matchNode('user1');
}

function returnSth() {
  const query = new Query<UserUserItemRelation>();
  query.return('user1');
  query.return(['user2', 'item']);
  query.return({ user2 : 'employees' });
  query.return({
    knows: ['name', 'age'],
    item: ['name', 'breed'],
  });
  query.return({
    user2: [{ name: 'personName' }, 'age'],
  });
  query.return({
    user2: {
      name: 'personName',
      age: 'personAge',
    },
  });
}

Unfortunately i have not found a solution for where. I'll have a look into that later