takorogo v0.0.1-19
node-takorogo
Takorogo to JSON parser for Node.js
Installation
This module is installed via npm:
$ npm install takorogoExample Usage
var takorogo = require('takorogo');
var rules = takorogo.parse('user <--[POSTED]-- :User');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:GameYou can specify types for relation attributes:
--[PARTICIPATE_IN(score: Score, wins: Integer)]--> game:GameOr even destructuring an array property:
entities.urls[] --[REFERS_TO(indices[first, last])]--> :UrlAttention. 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:TweetIn 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:PointAttributes
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:
--> metadataAs 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:TweetIn 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 TweetYou 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 API
def 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 point
def 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:PersonAdd attribute rewrite in type constraint statements
def :Person {
+ name.first => firstName :String
}11 years ago