node-takorogo
Takorogo to JSON parser for Node.js
Installation
This module is installed via npm:
$ npm install takorogo
Example Usage
var takorogo = ;var rules = takorogo;
Takorogo Syntax
Takorogo uses relations to maps properties to nodes determined by classes and their indices. That's all.
Relations
For example we have a tweet with embedded user object:
var tweet = id: 481101713646960641 user: default_profile_image : false id : 119102990 profile_background_image_url_https : "https://pbs.twimg.com/profile_background_images/444244318/16840333315.png" verified : false /* ... */
In this case we can say that tweet posted by user:
--[POSTED_BY]--> user:User
Or that user posted a tweet:
<--[POSTED]-- user:User
Or user and tweet has a bidirectional relationship:
<--[POSTED|POSTED_BY]--> user:User
Where user
is a property name that contains embedded object and :User
is a name of a class.
You can specify what attribute will be stored in relation passing them to relation in parentheses:
--[PARTICIPATE_IN(score, wins)]--> game:Game
You can specify types for relation attributes:
--[PARTICIPATE_IN(score: Score, wins: Integer)]--> game:Game
Or even destructuring an array property:
entities.urls[] --[REFERS_TO(indices[first, last])]--> :Url
Attention. Currently only plain values are supported.
Relations resolved from keys
Sometimes your document just points to something via key. In this case you can specify how relation should be resolved.
in_reply_to_status_id_str => id_str <--[ REPLIED_WITH|REPLIED_TO ]--> in_reply_to:Tweet
In the last case =>
means that in_reply_to_status_id_str
should be treated as an id_str
key.
For compound keys you can use following syntax:
(longitude, latitude) => (x, y) --[ LOCATED ]--> location:Point
Attributes
Sometimes your document's structure doesn't fit plane key-value nature of node. In this case you can specify properties by paths:
--[CITIZEN_OF]--> place.country:Country
If document's field doesn't match what you want to have in the target node you can rename it:
--[CITIZEN_OF]--> place.country => country:Country
You also can assign types to attributes (user-defined classes are also supported):
def :Person { + firstName :String + dateOfBirth :Date}
Attention: attribute aliasing in types constraints are not supported yet.
Embedded objects
Embedded objects are stored inside the node. Mechanism depends on graph database engine.
Links (anonymous relations)
You can store unstructured embedded object as a link to node:
--> metadata
As for normal relations you can specify classes and arrays for links:
--> tweet:Tweet--> comments:Comment[]
Indices
You can specify unique constraint:
UNIQUE(id)
Takorogo also handles compound constraints:
UNIQUE(firstName, lastName)
Classes & Types
You can refer to already defined class or type by colon notation:
UNIQUE(id:Int)--> tweet:Tweet
In case when you have a collection of instances you can use an array syntax:
--> comments:Comment[]
Unstructured arrays are also supported:
--> things[]
Class Definition
To define class use def
keyword:
def Tweet
You can specify primary key in parentheses:
def HashTag(text)
Compound indexes are also supported:
def Person(firstname, lastname)
And you can use paths to specify properties:
def Citizen(credentials.passport.number)
Sometimes your class's primary key is stored in array. For example we have a location document with unique combinations of longitude and latitude stored in array. For this cases you can write:
def Location(coordinates[longitude, latitude])
Complex Classes
You can define rules for complex classes in curly braces:
def User(passport.id) { --[CHILD_OF]--> father:Person --[REFERS_TO(indices[first, last])]--> entities.url.urls[]:Url}
Enumerations
You can define enumeration mappers for arrays of fixed length:
def Coordinates [ longitude, latitude ]
After array items specified we can treat it as a regular document:
def VendorCoordinates [ longitude, latitude, vendor ] { UNIQUE(longitude, latitude, vendor.id) --[PRODUCED_BY]--> vendor:Vendor }
Inline Class Definition
Simple classes can be defined inside relation rules by appending class name with parentheses:
--[POPULATED_WITH]--> comment:Comment(id)
You can specify indices as in normal declaration:
--[REFERS_TO]--> urls:Url(url)[]
That will create a class Url
with url
as primary key and use it as a map for related nodes.
Comments
Takorogo supports only one line comments (both #
and //
):
# JIRA task def Task { UNIQUE(id) // Primary key --[BLOCKED_BY]-->blocker:Task}
Examples
The following Takorogo script describes tweet structure from Twitter Stream API:
# The whole tweet document returned by Twitter Stream APIdef Tweet { UNIQUE(id_str:String) UNIQUE(id:Int) + location: String <--[POSTED]-- user:User --> metadata <--[ REPLIED_WITH|REPLIED_TO ]--> in_reply_to_status_id_str => in_reply_to:Tweet --[ REFERS_TO(indices[first, last]) ]--> entities.urls[]:Url --[ HAS_TAG(indices[first, last]) ]--> entities.hashtags[]:HashTag(text) --[ AT ]--> coordinates:Location --[ ON ]--> geo:Location --[ PLACED ]--> place:Place} # Just a pointdef Coordinates [ longitude, latitude ] { UNIQUE(longitude: Double, latitude: Double)} def Location(coordinates:Coordinates) // Simply a wrapper for coordinates def Place { --[ BOUNDED_BY ]--> bounding_box.coordinates => bounding_box:Coordinates[]} def User(id_str) { --[REFERS_TO(indices[first, last])]--> entities.url.urls[]:Url}
Browser
Client versions of node-takorogo
can be built from sources or can be found in ./client/
directory of NPM module.
Contribution
You can start from API docs.
I will accept only changes covered by unit tests.
Road map
Support indentation for class rules
def :Person UNIQUE(passport.id) --[CHILD_OF]--> father:Person
Add attribute rewrite in type constraint statements
def :Person { + name.first => firstName :String}