Code Monkey home page Code Monkey logo

gun-tag's Introduction

gun-tag

Intro

GUN Tag is a plugin for Gun(^0.9.x) using Gun.chain.

If you need to organize your data with tags/labels this might be useful. gun-tag enables you to tag and untag nodes to custom tags/labels. So for everyone who is struggling with deleting data in Gun ( You can't ) this could be a nice alternative.

It will give you the following;

  • tag nodes
  • untag nodes
  • scopetag nodes
  • proptag nodes
  • find intersects (multiple tags)
  • filter nodes

What you could do with gun-tag

Important!

gun-tag alters your nodes by adding extra properties to it. ( But so does Gun )

Setup

Prerequisites

You'll need Gun ^0.9.x

 npm install gun
 npm install gun-tag

Node.js

var Gun = require('gun');
require('gun-tag');

Browser

For the browser, it's much simpler, since your version of gun is exported as a global. Just include it as a script tag, and gun-tag takes care of the rest.

<!-- AFTER you load Gun -->
<script src="node_modules/gun-tag/gun-tag.js"></script>

API

Four methods are exposed for your gun instance;

  • .tag
  • .proptag
  • .untag
  • .tagged ( including filtering and intersects )

gun.get('Bob').tag('programmer')

You can pass .tag() multiple names to index a node under. When called, it will try to read out your current context, index it under each tag, and then place each tag under a master list of every tag ever used. A tag can be a String or an Array (['one','Numbers/two','three'])

gun.put({
  name: 'Bob',
  profession: 'developer'
}).tag([
  'members',
  'javascript developers',
  'gunDB developers',
  'examples'
])

Additional : gun.tag('scope/name')

The same as tag but when you put a '/' in your tagname the tag will be 'scoped'.

Say what?

Suppose you want to tag your Books and Movies. And you want to categorize them in themes also ( 'Fantasy','Comedy',etc), oh and tag them to the author.

gun.get('IT')
   .put({title:'IT',discription:'Some scary stuff about a clown'})
   .tag([
     'Books/Horror',
     'Books/Fantasy',
     'Movies/Horror',
     'Movies/'Fantasy',
     'Authors/King',
     'Fantasy/Book',
     'Horror/Movie']);

Now there are several options to retrieve your data... First of all we can get a list of our 'Book' themes

gun.tagged('Books', data => { console.log(data) } )  
/* data will be...
  {
    Fantasy:{'#':'Books/Fantasy'},
    Horror:{'#':'Books/Horror'}
  }

/*  basicly the same as */
gun.get('Books/TAGS').once(Gun.log)

Note: It is no longer required to add '/TAGS' to the scoped tags.
eg: gun.tagged('Books/TAGS') is the same as gun.tagged('Books').
gun-tag knows its scopes.

To get all Fantasy books

 gun.tagged('Books/Fantasy',cb)  
 // will return all - full - nodes that are tagged to 'Books/Fantasy'

Usefull ?

Well yeah... You could create a selector 'Choose Book theme' and let the user select a theme. Upon selection you present all books belonging to that theme.

gun.get().untag('name')

The same 'rules' as tagging but now the nodes get 'untagged'.

nodes that are untagged will only be filtered out when you use gun.tagged(tag,cb).
Using gun.tagged(tag).once(cb) or gun.get('tagname').once(cb) will NOT leave out untagged nodes.

gun.get().proptag('name')

A special kind of tag. With 'proptag' the provided tag wil be set as a direct property on the node. This can be usefull if you want to quickly check if a node 'is' or 'has' something.

 gun.get('Bob').put({name:'Bob'}).proptag('has paid')
 gun.get('Bob').once(cb) // {name:'Bob','has paid':true}

A proptag can be untagged like any other tag.

  gun.get('Bob').untag('has paid');
  gun.get('Bob').once(cb) // {name:'Bob','has paid':false}

gun.tagged()

When no arguments are provided you get the full tag list.
gun.tagged().once(cb)

gun.tagged(tag, cb)

Provide a tagname and a callback to get all valid members of that tag.
The returned nodes are full objects.

 gun.tagged('gunDb',list => console.log(list) )

Please note that with 'scopedtags' like gun.tag('Books/Fantasy') you can get a list of all subtags under that scope with gun.get(scope +'/TAGS') ( tags is uppercase) or just gun.tagged('Books',cb)

 gun.tagged('Books/Fantasy',cb) // all fantasy books

Deleting nodes

Ah yes... a returning question in gitter and stackoverflow.
The short answer "You can't...not really", there are solutions like 'nulling' your node...
At least with gun-tag you'll have another option.

Let's first create a regular gun.set()

let members = gun.get('members');

// assume we have an Array with member objects

allmembers.forEach( member => {
  // store the member in Gun
  let m = gun.get( Gun.text.random() ).put(member);
  // tag member
  m.tag( ['MEMBERS','MEMBERS/Paid'])
  // put member in `set`
  members.set( m) ;
});

Great, now instead of building a visual list from our set, we build it from our tagged members.

gun.tagged('MEMBERS', list => {
  // list -> create nice member list
})

Let's create an extra page in our app, one that shows the mebers that actually paid there contribution.

gun.tagged('MEMBERS/Paid',  list => {
  // list -> create list with members that paid
})

Oh.. but Bob didn't pay.

gun.get('Bob').untag('MEMBERS/Paid');

So if we would refresh our list...

please read the section about subscribing further down.

gun.tagged('MEMBERS/Paid', list => {
  // list -> all members that 'paid'...not Bob!
});

Once a month ( or so ) we want to kick the members that didn't pay. So we get our ( tagged ) members again and filter the list.

We could use our set ( gun.get('MEMBERS') ) but using the tagged list give you some advantages...
Nodes returned from gun.tagged() are actually tagged and have an extra '_soul' property.

gun.tagged('MEMBERS', list => {
  list.forEach(member=>{
    if(member.tags['MEMBERS/Paid] === 0) {
      // and there is Bob...
      gun.get(member._soul).untag('MEMBER');
    }
  })
})

Now Bob is no longer a member...

gun.tagged('MEMBERS', list => {
  // list -> all members ...no more 'Bob'
})

So there you have it...deleted ? No, but Bob won't show up either.

After a year we want to invite ex-members again.
Luckily we still have our original set()

gun.get('MEMBERS').loadonce(list=>{
  list.forEach(member=>{
    if(member.tags['MEMBERS'] === 0) {
      // Bob !!!  -> Invite him
    }
  })
})

You'll notice i use synclist and loadonce in my examples. synclist ( includes loadonce) is one of my other Gun extensions. You can read more about it here

intersect or filter

An intersect is a list with nodes that are tagged to ALL provided tags. This is a feature of gun-tagged().

We can achieve this by providing an Array to gun.tagged().
Get all Fantasy books, written by (Stephen) King , published in 1988.

 gun.tagged( ['Books/Fantasy','Authors/King','Published/1988'],cb)

Subscribing - a word of advise.

Offcourse it is possible to subscribe to changes on a tag like gun.get('Books/Lent').synclist(cb) and you will be notified when the status of those books changes. But then you'll have to subscibe to 'Books/borrowed' also...But what if you borrowed a book from one friend and loan it to another? You'll end up with two subscriptions on the same book.

my advise

Store all books in a set(), then subscribe to that.

let books = gun.get('BOOKS')
myBooks.forEach(book=>{
  books.set(book)
});
books.synclist( data=> {
  if(data.list) { // all books }
  if( data.node) { // your changed ( tagged maybe ? ) book}
})

Now you can go wild with tagging/untagging/retagging your books. Every change will trigger the synclist() callback.

Disclaimer

gun-tag is born of necessity, and over time became really powerfull if you fully understand it's potential. That being said... tell me what you want to do and - maybe- i can advise you. I'm not much of an email reader so ping me in gitter @Stefdv

Credits

Thanks to Mark Nadal for writing Gun and for helping out whenever i need.

gun-tag's People

Contributors

amark avatar stefdv avatar

Watchers

 avatar  avatar

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.