Graph Guide - amark/gun GitHub Wiki

This guide will go over how easy it is to create interconnected data with GUN's graph features by combining key/value, relational, and document based data together. It will also be a great introduction to using pretty much every one of GUN's API methods.

First let's instantiate the database:

gun = GUN();

Then we'll add some people to our database using a simple key/value approach.

alice = gun.get('alice').put({name: 'alice', age: 22});
bob = gun.get('bob').put({name: 'bob', age: 24});
carl = gun.get('carl').put({name: 'carl', age: 16});
dave = gun.get('dave').put({name: 'dave', age: 42});

Note: If no data is found on the key ('alice', etc.) when we .get it, gun will implicitly create and update it upon a .put. This is useful and convenient for most but not all apps.

What if we want to get their data? We can either chain off of the reference directly or get it again:

alice.on(function(node){
  console.log('Subscribed to Alice!', node);
});

gun.get('bob').once(function(node){
  console.log('Bob!', node);
});

Note: GUN is a functional reactive database for streaming event-driven data, gotta love/hate buzzwords - right? This means that .on subscribes to realtime updates, and may get called many times. Meanwhile .once grabs the data once, which is useful for procedural operations.

Now lets add all the people into a set, you can think of this as a table in relational databases or a collection in NoSQL databases.

people = gun.get('people');
people.set(alice);
people.set(bob);
people.set(carl);
people.set(dave);

Note: .get and .put are the core API of gun, everything else is just a convenient utility that wraps around them for specific uses - like .set for inserting records.

It is now easy to iterate through our list of people.

people.map().once(function(person){
  console.log("The person is", person);
});

Note: If .map is given no callback, it simply iterates over each item in the list "as is" - thus acting like a for each loop in javascript. Also, everything is continuously evaluating in GUN, including .map, so it will get called when new items are added as well as when an item is updated. It does not iterate through the whole list again every time, just the changes. This is great for realtime applications.

Next we want to add a startup that some of these people work at. Document oriented data does a perfect job at capturing the hierarchy of a company. Let's do that with the following:

company = gun.get('startup').put({
  name: "hype",
  profitable: false,
  address: {
    street: "123 Hipster Lane",
    city: "San Francisco",
    state: "CA",
    country: "USA"
  }
});

Now let's read it out!

company.once(function(startup){
  console.log("The startup:", startup);
});

Note: The data given in the callback is only 1 layer deep to keep things fast. What you'll see logged on startup.address is not the address itself, but a link to the address. Because documents can be of any depth, GUN only streams out what you need by default, thus optimizing bandwidth.

So what if you want to actually access the city property on the company's address then? .get also lets you traverse into the key/value pairs on sub-objects. Take this for example:

company.get('address').get('city').once(function(value, key){
  console.log("What is the city?", value);
});

Good news! We just found out the company got funding and moved to a new office! Let's go ahead and update it.

gun.get('startup').put({ // or you could do `company.put({` instead.
  funded: true,
  address: {
    street: "999 Expensive Boulevard"
  }
});

Note: GUN saves everything as a partial update, so you do not have to re-save the entire object every time (in fact, this should be avoided)! It automatically merges the updates for you by doing conflict resolution on the data. This lets us update only the pieces we want without worrying about overwriting the whole document.

Documents in isolation are not very useful though. Let's connect things and turn everything into a graph!

employees = company.get('employees');
employees.set(dave);
employees.set(alice);
employees.set(bob);

alice.get('spouse').put(bob);
bob.get('spouse').put(alice);

alice.get('spouse').get('employer').put(company);
alice.get('employer').put(company);

dave.get('kids').set(carl);
carl.get('dad').put(dave);

carl.get('friends').set(alice);
carl.get('friends').set(bob);

Note: We can have 1-1, 1-N, N-N relationships. By default every relationship is a "directed" graph (it only goes in one direction), so if you want bi-directional relationships you must explicitly save the data as being so (like with Dave and his kid, Carl). If you want to have meta information about the relationship, simply create an "edge" node that both properties point to instead. Many graph databases do this by default, but because not all data structures require it, gun leaves it to you to specify.

Finally, let's read some data out. Starting with getting a key/value, then navigating into a document, then mapping over a table, then traversing into one of the columns and printing out all the values!

gun.get('alice').get('spouse').get('employer').get('employees').map().get('name').once(function(data, key){
  console.log("The employee's", key, data);
});

Awesome, now run it all together: https://jsbin.com/jururin/1/edit?js,console (Hit "Run", all logs except for the last one have been commented out).

GUN is that easy! And it all syncs in realtime across devices when you connect to other peers! Imagine what you can build?

Whatever it is (except for banking), we hope you are excited and tackle it with gun! Make sure you join the chat room (everybody is nice and helpful there) and ask questions or complain about bugs/problems. Or if you think your company might be interested in using gun, we have some great partnership plans (as well as some donation options, if you want to personally contribute some yummy meals to our tummy)!