├── .gitignore ├── .jslintrc ├── .snyk ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── first.js ├── object │ ├── index.js │ ├── type.js │ └── value.js ├── parse.js ├── predicate │ ├── index.js │ ├── type.js │ └── value.js ├── rules-engine │ ├── flip │ │ └── index.js │ ├── index.js │ ├── parse │ │ ├── first-non-predicate.js │ │ ├── first-predicate.js │ │ ├── last-predicate.js │ │ ├── objects.js │ │ ├── parts.js │ │ ├── predicate.js │ │ ├── reduce-parts.js │ │ ├── reduce-predicates.js │ │ ├── rules.js │ │ └── subjects.js │ └── rules-processor.js ├── subject │ ├── index.js │ ├── type.js │ └── value.js ├── text │ ├── classify.js │ ├── is │ │ └── plural.js │ └── singularize.js └── triple.js ├── logo.png ├── package.json ├── test ├── check.js ├── messenger │ ├── client │ │ ├── 001-client.js │ │ └── index.js │ ├── follow │ │ └── 001-user-follows-user.js │ └── message │ │ ├── 001-user-creates-message.js │ │ ├── 002-user-sends-message.js │ │ ├── 003-user-receives-message.js │ │ └── 004-user-messages-user.js └── unit │ └── flip │ └── 001-flip.js └── wercker.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /.jslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "evil":false, 3 | "indent":2, 4 | "vars":true, 5 | "passfail":false, 6 | "plusplus":false, 7 | "predef": "module,require" 8 | } 9 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | ignore: {} 2 | patch: {} 3 | version: v1 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | - "4" 5 | - "0.12" 6 | - "0.11" 7 | - "0.10" 8 | - "iojs" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 @DEFSTREAM 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## nl3 - Natural Language Triples 2 | 3 |

4 | npm version 6 | 7 | Gemnasium 9 | 10 | build status 12 | 13 | coverage 15 | 16 | coverage 18 | 19 | Known Vulnerabilities 20 | 21 | downloads 22 | 23 |

24 | 25 |

26 | Chat 28 |

29 | 30 | **nl3** is a natural language triple library, used for parsing triples from plain english. 31 | Currently nl3 is best at generating triples from simple short phrases that contain the Subject, Predicate and Object in order. 32 | 33 |

34 | 35 |

36 | 37 | #### What is a triple? 38 | A triple is a data structure that represents a Subject, Predicate and Object or S P O. 39 | 40 | **More Information** 41 | - https://en.wikipedia.org/wiki/Triplestore 42 | - https://en.wikipedia.org/wiki/Resource_Description_Framework 43 | 44 | #### TLDR; 45 | 46 | ```javascript 47 | var nl3 = require('nl3')({ 48 | /** 49 | * Specifies valid triples in plain english ex: 'Subject Predicate Object'. 50 | * All values will be singularized. 51 | * @type {Array} 52 | */ 53 | grammar: [ 54 | 'users message users' 55 | ], 56 | /** 57 | * Extend your vocabulary by mapping word stems to existing predicates. 58 | * @type {Object} 59 | */ 60 | vocabulary: { 61 | msg: 'message', // user bob msgs user tom 62 | messag: 'message', // user bob messaged user jill 63 | contact: 'message' // user bob contacted user bill 64 | } 65 | }); 66 | ``` 67 | 68 | The client returned is able to parse these queries. 69 | 70 | ```javascript 71 | nl3.parse('user jack msg user jill'); 72 | nl3.parse('user jack msgs user jill'); 73 | nl3.parse('user jack messaged user jill'); 74 | nl3.parse('user jack contacted user jill'); 75 | nl3.parse('user jack contacts user jill'); 76 | ``` 77 | 78 | All of which will have the same output. 79 | 80 | ```javascript 81 | { 82 | subject: { 83 | type: 'user', 84 | value: 'jack' 85 | }, 86 | predicate: { 87 | value: 'message' 88 | }, 89 | object: { 90 | type: 'user', 91 | value: 'jill' 92 | } 93 | } 94 | ``` 95 | 96 | # Installation 97 | 98 | ```shell 99 | $ npm install nl3 --save 100 | ``` 101 | 102 | # Development Scripts 103 | Before running any development scripts, be sure to first install the dev modules. 104 | 105 | ```shell 106 | $ npm install nl3 --save --dev 107 | ``` 108 | 109 | #### Build Documentation 110 | Outputs code documentation files to the `./doc/api` folder. 111 | 112 | ```shell 113 | $ npm run doc 114 | ``` 115 | 116 | #### Static Analysis 117 | Outputs static analysis files to the `./doc/analysis` folder. 118 | 119 | ```shell 120 | $ npm run analyze 121 | ``` 122 | 123 | #### Test + Coverage 124 | Outputs code coverage files to the `./doc/coverage` folder. 125 | 126 | ```shell 127 | $ npm run test 128 | ``` 129 | 130 | **CURRENT COVERAGE REPORT** 131 | 132 | ![codecov.io](https://codecov.io/github/defstream/nl3/branch.svg?branch=master) 133 | 134 | # API 135 | 136 | ### `nl3(options)` 137 | 138 | Create an nl3 instance. 139 | 140 | **parameters:** 141 | - **options** {Object} The options for the nl3 client. 142 | - **options.grammar** {Array} An array of valid grammar in the format of 'S P O'. 143 | - **options.vocabulary** {Array} An object mapping the phonetic root of an object to a predicate. 144 | 145 | 146 | **returns**: a new instance of the nl3 client. 147 | 148 | Example 149 | 150 | ```javascript 151 | var nl3 = require('nl3')({ 152 | /** 153 | * Specify valid triples in plain english ex: 'Subject Predicate Object'. 154 | * The Subject, Predicate and Object will be will be singularized, if presented in any tense. 155 | * @type {Array} 156 | */ 157 | grammar: [ 158 | 'users message users' 159 | ], 160 | /** 161 | * Extend the vocabulary of your predicates by mapping word stems to existing predicates within your grammar. 162 | * @type {Object} 163 | */ 164 | vocabulary: { 165 | msg: 'message', // user bob msgs user tom 166 | messag: 'message', // user bob messaged user jill 167 | contact: 'message' // user bob contacted user bill 168 | } 169 | }); 170 | ``` 171 | 172 | ### `nl3.parse( text )` 173 | 174 | **parameters:** 175 | - **text**: {String} A string containing a S P O phrase in plain english. 176 | **returns**: A triple containing the results of of the parsed Subject Predicate and Object. 177 | 178 | Example 179 | 180 | ```javascript 181 | 182 | var nl3 = require('nl3')({ 183 | grammar: [ 184 | 'users message users' 185 | ], 186 | vocabulary: { 187 | contact: 'message', // user bob contacted user bill 188 | } 189 | }); 190 | 191 | function print (description, triple) { 192 | console.log( 193 | description + ' =', JSON.stringify(triple, null, ' '); 194 | ); 195 | }; 196 | 197 | print( 'user jack contacts user jill', nl3.parse('user jack contacts user jill') ); 198 | 199 | print( 'users who message user jill', nl3.parse('users who message user jill') ); 200 | 201 | ``` 202 | 203 | **returns:** 204 | 205 | ```javascript 206 | 207 | user jack contacts user jill = { 208 | "subject": { 209 | "type": "user", 210 | "value": "jack" 211 | 212 | }, 213 | "predicate": { 214 | "value": "message" 215 | }, 216 | "object": { 217 | "type": "user", 218 | "value": "jill" 219 | } 220 | } 221 | users who message user jill = { 222 | "subject": { 223 | "type": "user" 224 | }, 225 | "predicate": { 226 | "value": "message" 227 | }, 228 | "object": { 229 | "type": "user", 230 | "value": "jill" 231 | } 232 | } 233 | 234 | ``` 235 | 236 | ### vNext 237 | Support for natural random order queries, these are not in (SPO) order, such as messages that user bob created (OSP), created messages by user jill (POS), created by user jill messages (PSO), (SO) user jills messages, (OS) messages for user jill. 238 | 239 | ```javascript 240 | 241 | nl3.parse('messages from user 42'); 242 | nl3.parse('messages by user 32'); 243 | 244 | ``` 245 | 246 | ### The Backlog 247 | - Support for misspelled subjects & objects ( nearest neighbor ) 248 | 249 | ### Discuss 250 | Chat channel: Chat 251 | 252 | Questions or comments can also be posted on the nl3 Github issues page. 253 | 254 | ### Maintainers 255 | Hector Gray (Twitter: @defstream) 256 | 257 | ### Contribute 258 | Pull Requests welcome. Please make sure all tests pass: 259 | 260 | ```shell 261 | $ npm test 262 | ``` 263 | 264 | Please submit Github issues for any feature enhancements, bugs or documentation problems. 265 | 266 | ### License 267 | MIT 268 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 4 | 'use strict'; 5 | 6 | var parse = require('./lib/parse'); 7 | var rules = require('./lib/rules-engine'); 8 | /** @exports nl3 **/ 9 | var nl3 = { 10 | //@function 11 | /** 12 | * @name parse 13 | * @description Returns a triple based on language used. 14 | * @access public 15 | * @returns {Object} 16 | * @example nl3.parse('users who follow user 42') // returns... 17 | * // { 18 | * // subject: { 19 | * // type: 'user', 20 | * // value: undefined 21 | * // }, 22 | * // predicate: { 23 | * // value: 'follow' 24 | * // }, 25 | * // object: { 26 | * // type: 'user', 27 | * // value: '42' 28 | * // } 29 | * // } 30 | */ 31 | parse: parse 32 | }; 33 | 34 | /** 35 | * @param {Object} options The options required for parsing triples 36 | * @param {Object} options.grammar A set of valid triple relations. 37 | * @param {Object} options.vocabulary Extend the known vocabulary by mapping a word stem to a predicate. 38 | * @return {Object} An instance of the nl3 client. 39 | */ 40 | module.exports = function create(options) { 41 | //@info triple rules 42 | var tream = rules({ 43 | //@info defined triples 44 | grammar: options.grammar || [], 45 | //@info an additional set of words that map to our items with our original grammar 46 | vocabulary: options.vocabulary || {} 47 | }); 48 | //@info create & return the new instance 49 | var instance = Object.create(nl3, { 50 | rules: { 51 | enumerable: true, 52 | value: tream 53 | } 54 | }); 55 | 56 | return instance; 57 | }; 58 | -------------------------------------------------------------------------------- /lib/first.js: -------------------------------------------------------------------------------- 1 | /** 2 | * returns the first item in an array 3 | * @param {Array} array The array to return the first item from. 4 | * @return {*} The value of the first item in the array or undefined. 5 | */ 6 | module.exports = function first(array) { 7 | return array[0]; 8 | } 9 | -------------------------------------------------------------------------------- /lib/object/index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var objectType = require('./type'); 6 | var objectValue = require('./value'); 7 | 8 | /** 9 | * Given the parts of speech, this returns an objects type & value. 10 | * @param {Object} parts The parts of speech. 11 | * @return {Object} The parsed objects type & value. 12 | */ 13 | module.exports = function object(parts) { 14 | return { 15 | type: objectType(parts), 16 | value: objectValue(parts) 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/object/type.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var singularize = require('../text/singularize'); 6 | 7 | /** 8 | * Given the parts of speech, this returns an objects type. 9 | * @param {Object} parts The parts of speech. 10 | * @return {String} The object type 11 | */ 12 | module.exports = function objectType(parts) { 13 | return parts.objects.objects[0] || parts.subjects.subjects[0]; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/object/value.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var first = require('../first'); 6 | 7 | /** 8 | * Given the parts of speech, this returns an objects value. 9 | * @param {Object} parts The parts of speech. 10 | * @return {String} The objects value. 11 | */ 12 | module.exports = function objectValue(parts) { 13 | return first( 14 | parts.objects.after.map(first).concat( 15 | parts.objects.before.map(first) 16 | ) 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /lib/parse.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var triple = require('./triple'); 6 | var classify = require('./text/classify'); 7 | 8 | /** 9 | * parse - parses text into a triple. 10 | * @memberof nl3 11 | * @param {String} text The text to parse. 12 | * @return {Object} The parsed triple. 13 | */ 14 | module.exports = function parse(text, callback) { 15 | if (typeof text !== 'string' || !text.trim()) { 16 | var err = new Error( 17 | 'The supplied text could not be parsed into a triple. value: ' + text 18 | ); 19 | if (callback) { 20 | return callback(err); 21 | } 22 | throw err; 23 | } 24 | var classification = classify(text); 25 | var trip = triple( 26 | classification, 27 | this.rules 28 | ); 29 | return this.rules.process({ 30 | triple: trip, 31 | classification: classification 32 | }, 33 | callback 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /lib/predicate/index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var predicateType = require('./type'); 6 | var predicateValue = require('./value'); 7 | 8 | /** 9 | * Given the parts of speech, this returns an predicates type & value. 10 | * @param {Object} parts The parts of speech. 11 | * @return {Object} The parsed predicate. 12 | */ 13 | module.exports = function predicate(parts, predicates) { 14 | return { 15 | type: predicateType(parts), 16 | value: predicateValue(parts, predicates) 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/predicate/type.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | /** 6 | * Given the parts of speech, this returns an predicates type. 7 | * @return {undefined} 8 | */ 9 | module.exports = function predicateType(pars) { 10 | //@info not yet implemented. 11 | return undefined; 12 | }; 13 | -------------------------------------------------------------------------------- /lib/predicate/value.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var singularize = require('../text/singularize'); 6 | 7 | var predicate = require('../rules-engine/parse/predicate') 8 | /** 9 | * Given the parts of speech, this returns an predicates value. 10 | * @param {Object} classification The classification of the text 11 | * @param {Object} rules The known triple rules 12 | * @return {String} The value of the first predicate 13 | */ 14 | module.exports = function predicateValue(parts, predicates) { 15 | return parts && parts.predicate && parts.predicate[0] && predicate( 16 | parts.predicate[0], predicates 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /lib/rules-engine/flip/index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | var lastPredicate = require('../parse/last-predicate'); 5 | /** 6 | * Determines if the "source" triple is valid against the target "rules" 7 | * @access private 8 | * @param {Object} source The source triple to validate. 9 | * @param {Object} target The target rules to validate against. 10 | * @return {Boolean} True if this triple abides by the rules, otherwise false. 11 | */ 12 | function itAbides(source, target) { 13 | var subject = target.subjects[source.subject.type]; 14 | var predicate = subject && subject.predicates && subject.predicates[source.predicate 15 | .value]; 16 | var object = predicate && predicate.objects; 17 | 18 | if (subject && predicate && object.indexOf(source.object.type) > -1) { 19 | return true; 20 | } 21 | return false; 22 | } 23 | 24 | /** 25 | * Flip - Triple Flip 26 | * @type {Object} 27 | */ 28 | module.exports = { 29 | /** 30 | * Returns true if the triple should be flipped, as determined by the rules. Otherwise false. 31 | * @param {Object} triple The triple to analyze 32 | * @param {Object} rules The rules to use during analyzation 33 | * @return {Boolean} True if the triple should be flipped, otherwise false. 34 | */ 35 | able: function flippable(options) { 36 | var triple = options.triple; 37 | var rules = options.rules; 38 | 39 | if (itAbides(triple, rules) === false) { 40 | return itAbides({ 41 | subject: triple.object, 42 | predicate: triple.predicate, 43 | object: triple.subject 44 | }, rules) || itAbides({ 45 | subject: triple.object, 46 | predicate: { 47 | type: undefined, 48 | value: lastPredicate(options.classification, rules), 49 | }, 50 | object: triple.subject 51 | }, rules) || undefined; //@info returns null if this triple is not valid in either direction. 52 | } 53 | return false; 54 | }, 55 | /** 56 | * it - flips the subject and object of a triple 57 | * @param {Object} triple The triple to flip 58 | * @return {Object} The flipped triple 59 | */ 60 | it: function flip(options) { 61 | var triple = options.triple; 62 | return { 63 | subject: triple.object, 64 | predicate: { 65 | type: undefined, 66 | value: lastPredicate(options.classification, options.rules), 67 | }, 68 | object: triple.subject 69 | }; 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /lib/rules-engine/index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var flip = require('./flip'); 6 | var processRules = require('./rules-processor'); 7 | var parse = { 8 | rules: require('./parse/rules') 9 | }; 10 | 11 | /** 12 | * creates an instance of a triple rules engine. 13 | * @param {Object} options The options for this rules engine. 14 | * @param {Object} options.grammar A set of valid triple relations. 15 | * @param {Object} options.vocabulary Extend the known vocabulary by mapping a word stem to a predicate. 16 | * @return {Object) The rules engine. 17 | */ 18 | module.exports = function rulesEngine(options) { 19 | //@info extract the grammar 20 | var grammar = [].concat(options.grammar || []); 21 | 22 | //@info build the rules engine 23 | var rules = grammar.reduce(parse.rules, { 24 | subjects: {}, 25 | predicates: [], 26 | objects: {} 27 | }); 28 | 29 | //@property {Function} process - validates & transforms the triple if necessary. 30 | Object.defineProperty(rules, 'process', { 31 | value: processRules(rules) 32 | }); 33 | 34 | //@property {Object} vocabulary - contains properties that are stem words, which map to defined predicates. 35 | Object.defineProperty(rules, 'vocabulary', { 36 | enumerable: true, 37 | value: options.vocabulary || {} 38 | }); 39 | 40 | //@info return the created engine. 41 | return rules; 42 | }; 43 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/first-non-predicate.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var parsePredicate = require('./predicate'); 6 | 7 | /** 8 | * firstNonPredicate returns the first non predicate in array of strings, the extended vocabulary from the grammar rules are also applied. 9 | * @param {Array} tokens An array of strings from a query. 10 | * @param {Object} rules The grammar rules.. 11 | * @return {String} The first found predicate, or undefined. 12 | */ 13 | module.exports = function firstNonPredicate(tokens, rules) { 14 | var i = 0; 15 | var predicate; 16 | for (i = 0; i < tokens.length; i = i + 1) { 17 | predicate = parsePredicate(tokens[i], rules.vocabulary); 18 | if (!predicate) { 19 | return tokens[i]; 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/first-predicate.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var parsePredicate = require('./predicate'); 6 | 7 | /** 8 | * firstPredicate returns the first predicate from an array of strings, using the vocabulary of the specified grammar rules. 9 | * @param {Object} classification The classification of the text 10 | * @param {Object} rules The known triple rules 11 | * @return {String} The first string that is a predicate from the array of tokens. 12 | */ 13 | module.exports = function firstPredicate(classification, rules) { 14 | var tokens = classification.parts.map(function(part) { 15 | return part[0] 16 | }); 17 | var i = 0; 18 | var predicate; 19 | for (i = 0; i < tokens.length; i = i + 1) { 20 | predicate = parsePredicate(tokens[i], rules.vocabulary); 21 | if (predicate) { 22 | return { 23 | index: i, 24 | value: predicate 25 | }; 26 | } 27 | } 28 | return undefined; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/last-predicate.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var parsePredicate = require('./predicate'); 6 | 7 | /** 8 | * lastPredicate returns the last predicate from an array of strings, using the vocabulary of the specified grammar rules. 9 | * @param {Object} classification The classification of the text 10 | * @param {Object} rules The known triple rules 11 | * @return {String} The last string that is a predicate from the array of tokens. 12 | */ 13 | module.exports = function lastPredicate(classification, rules) { 14 | var tokens = classification.parts.map(function(part) { 15 | return part[0] 16 | }); 17 | var i = 0; 18 | var predicate; 19 | for (i = tokens.length - 1; i < -1; i = i - 1) { 20 | predicate = parsePredicate(tokens[i], rules.vocabulary); 21 | if (predicate) { 22 | return { 23 | index: i, 24 | value: predicate 25 | }; 26 | } 27 | } 28 | return undefined; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/objects.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | /** 6 | * Properly stores the subject, predicate and object, within the ruleset. 7 | * @param {Object} rules The ruleset. 8 | * @param {String} subject The subject to include into the rulset. 9 | * @param {String} predicate The predicate to include into the rulset. 10 | * @param {String} object The object to include into the rulset. 11 | * @return {Object} The updated ruleset. 12 | */ 13 | module.exports = function includeObjects(rules, subject, predicate, object) { 14 | if (!rules.objects[object]) { 15 | rules.objects[object] = { 16 | predicates: {} 17 | }; 18 | } 19 | 20 | var predicates = rules.objects[object].predicates[predicate]; 21 | 22 | if (!predicates) { 23 | predicates = rules.objects[object].predicates[predicate] = { 24 | subjects: [subject] 25 | }; 26 | } 27 | 28 | return rules; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/parts.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | var reduceParts = require('./reduce-parts'); 5 | var firstPredicate = require('./first-predicate'); 6 | /** 7 | * Given classification and a rule set parse the parts of speach. 8 | * @param {Object} classification The classification of the text 9 | * @param {Object} rules The known triple rules 10 | * @return {String} The mapped predicate. 11 | */ 12 | module.exports = function parts(classification, rules) { 13 | var predicate = firstPredicate(classification, rules); 14 | var predicateIndex = predicate && predicate.index || undefined; 15 | var prepositionIndex = classification.parts.reduce(function(result, part) { 16 | if (part[1] === 'IN') { 17 | result.found = true 18 | } 19 | if (!result.found) { 20 | result.index = result.index + 1; 21 | } 22 | return result; 23 | }, { 24 | index: 0, 25 | found: false 26 | }).index; 27 | 28 | var index = predicate && predicate.index 29 | var beforePredicate = index && classification.parts.slice(0, index) || []; 30 | var afterPredicate = index && classification.parts.slice(index + 1) || []; 31 | 32 | return { 33 | objects: reduceParts(afterPredicate, rules), 34 | subjects: reduceParts(beforePredicate, rules), 35 | predicate: predicate && classification.parts[predicate.index] || undefined 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/predicate.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var stemmer = require('stemmer'); 6 | var singularize = require('../../text/singularize'); 7 | 8 | /** 9 | * Given text and a array mapping of predicates, returns the mapped predicate. 10 | * @param {String} text The text to map to a predicate. 11 | * @param {Object} predicates An object whose keys are word stems and values are predicates that have been defined within the grammar. 12 | * @return {String} The mapped predicate. 13 | */ 14 | module.exports = function mapPredicate(text, predicates) { 15 | var predicate = stemmer( 16 | singularize(text) 17 | ); 18 | return predicates && predicates[predicate] || undefined; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/reduce-parts.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var firstPredicate = require('./last-predicate'); 6 | var singularize = require('../../text/singularize'); 7 | /** 8 | * Given an array of strings, identifies valid objects or subjects in the order they occur. 9 | * @param {Array} data An array of tokenized strings. 10 | * @param {Object} rules The known triple rules. 11 | * @return {String} The mapped predicate. 12 | */ 13 | module.exports = function reduceParts(data, rules) { 14 | var parts = { 15 | objects: [], 16 | subjects: [], 17 | before: [], 18 | after: [] 19 | }; 20 | return data && data.reduce(function(result, part) { 21 | var normalizedPart = singularize(part[0]); 22 | var added = false; 23 | if (rules.objects[normalizedPart]) { 24 | result.objects.push(normalizedPart); 25 | added = true; 26 | } 27 | if (rules.subjects[normalizedPart]) { 28 | result.subjects.push(normalizedPart); 29 | added = true; 30 | } 31 | if (!added && part[1] !== 'IN' && part[1][0] !== 'W') { 32 | if (!result.subjects.length && !result.objects.length) { 33 | result.before.push(part); 34 | } else { 35 | result.after.push(part); 36 | } 37 | } 38 | return result; 39 | }, parts) || parts; 40 | }; 41 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/reduce-predicates.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | /** 6 | * reducePredicates reduces the predicates from an array into the grammar rules. 7 | * If a predicate is found to be a member of the grammar rules, it is added as a predicate. 8 | * @param {Object} rules The grammar rules. 9 | * @param {String} predicate The predicate to attempt to parse. 10 | * @return {Object} rules The grammar rules. 11 | */ 12 | module.exports = function reducePredicates(rules, predicate) { 13 | if (rules.predicates.indexOf(predicate) === -1) { 14 | rules.predicates.push(predicate); 15 | } 16 | return rules; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/rules.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var parseSubjects = require('./subjects'); 6 | var parsePredicates = require('./reduce-predicates'); 7 | var parseObjects = require('./objects'); 8 | var singularize = require('../../text/singularize'); 9 | 10 | /** 11 | * parses the grammar rules from a string array. 12 | * @param {Object} ruleset The ruleset to apply the parsed grammar rules to. 13 | * @param {Array} data An array of strings representing the grammar rules. 14 | * @return {Object} The parsed grammar rules. 15 | */ 16 | module.exports = function rules(ruleset, data) { 17 | data = data.toString().split(' '); 18 | 19 | var subject = singularize(data[0]); 20 | var predicate = singularize(data[1]); 21 | var object = singularize(data[2]); 22 | 23 | ruleset = parseSubjects(ruleset, subject, predicate, object); 24 | ruleset = parsePredicates(ruleset, predicate); 25 | ruleset = parseObjects(ruleset, subject, predicate, object); 26 | 27 | return ruleset; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/rules-engine/parse/subjects.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | /** 6 | * Properly stores the subject, predicate and object, within the ruleset. 7 | * @param {Object} rules The ruleset. 8 | * @param {String} subject The subject to include into the rulset. 9 | * @param {String} predicate The predicate to include into the rulset. 10 | * @param {String} object The object to include into the rulset. 11 | * @return {Object} The updated ruleset. 12 | */ 13 | module.exports = function includeSubjects(rules, subject, predicate, object) { 14 | if (!rules.subjects[subject]) { 15 | rules.subjects[subject] = { 16 | predicates: {} 17 | }; 18 | } 19 | 20 | var predicates = rules.subjects[subject].predicates[predicate]; 21 | 22 | if (!predicates) { 23 | predicates = rules.subjects[subject].predicates[predicate] = { 24 | objects: [object] 25 | }; 26 | } 27 | 28 | return rules; 29 | }; 30 | -------------------------------------------------------------------------------- /lib/rules-engine/rules-processor.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var flip = require('./flip'); 6 | 7 | /** 8 | * Evaluates and returns triple, resolves any issues if necessary. An error is thrown if the triple is invalid. 9 | * @param {Object} triple The triple to process 10 | * @param {Function} callback The callback used to handle the result. 11 | */ 12 | module.exports = function rulesProcessor(rules) { 13 | return function processRules(options, callback) { 14 | var triple = options.triple; 15 | var classification = options.classification; 16 | var opts = { 17 | triple: triple, 18 | classification: classification, 19 | rules: rules 20 | }; 21 | var shouldFlip = flip.able(opts); //@info returns null if invalid. 22 | if (shouldFlip === true) { 23 | triple = flip.it(opts); 24 | return (callback && callback(null, triple)) || triple; 25 | } 26 | if (shouldFlip === false) { 27 | return (callback && callback(null, triple)) || triple; 28 | } 29 | //@info if we've received null, then return error. 30 | var err = new Error('Invalid triple:\n' + JSON.stringify(triple, null, 31 | ' ')); 32 | if (callback) { 33 | return callback(err, triple); 34 | } 35 | throw err; 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /lib/subject/index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var subjectType = require('./type'); 6 | var subjectValue = require('./value'); 7 | 8 | /** 9 | * Given the parts of speech, this returns an subjects type & value. 10 | * @param {Object} parts The parts of speech. 11 | * @return {Object} The parsed subjects type & value. 12 | */ 13 | module.exports = function subject(parts) { 14 | return { 15 | type: subjectType(parts), 16 | value: subjectValue(parts) 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/subject/type.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var singularize = require('../text/singularize'); 6 | 7 | /** 8 | * Given the parts of speech, this returns an subjects type. 9 | * @param {Object} parts The parts of speech. 10 | * @return {String} The subject type 11 | */ 12 | module.exports = function subjectType(parts) { 13 | return parts.subjects.subjects[0] || parts.subjects.objects[0]; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/subject/value.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var first = require('../first'); 6 | 7 | /** 8 | * Given the parts of speech, this returns an subjects value. 9 | * @param {Object} parts The parts of speech. 10 | * @return {String} The subjects value. 11 | */ 12 | module.exports = function subjectValue(parts) { 13 | return first( 14 | parts.subjects.after.map(first).concat( 15 | parts.subjects.before.map(first) 16 | ) 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /lib/text/classify.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var pos = require('pos'); 6 | var speakEasy = require('speakeasy-nlp'); 7 | 8 | var lexer = new pos.Lexer(); 9 | var tagger = new pos.Tagger(); 10 | 11 | /** 12 | * Classifys the text within a given query ( identifies, nouns, subjects, verbs etc...). 13 | * @param {String} text The text to classify. 14 | * @return {Object} The text classification. 15 | */ 16 | module.exports = function classify(text) { 17 | //@info classify the text within the text 18 | var classification = speakEasy.classify(text); 19 | classification.parts = tagger.tag( 20 | lexer.lex(text) 21 | ); 22 | classification.text = text.trim(); 23 | classification.value = parse(text); 24 | return classification; 25 | }; 26 | 27 | 28 | function tag(text) { 29 | return tagger.tag( 30 | lexer.lex(text) 31 | ); 32 | } 33 | 34 | function parse(text) { 35 | var parts = tag(text).reduce(function(result, tag) { 36 | if (tag[1] !== 'IN' && (tag[1][tag[1].length - 1] === 'N' || tag[1][0] === 'N') || tag[1] === 'CD') { 37 | if (!result.predicate.length) { 38 | result.subject = result.subject.concat(tag[0]); 39 | } else { 40 | result.object = result.object.concat(tag[0]); 41 | } 42 | } 43 | if (tag[1][0] === 'V') { 44 | if (!result.predicate.length) { 45 | result.predicate = result.predicate.concat(tag[0]); 46 | } 47 | } 48 | return result; 49 | }, { 50 | subject: [], 51 | predicate: [], 52 | object: [] 53 | }); 54 | return format(parts); 55 | } 56 | 57 | 58 | function format(parts) { 59 | 60 | if (parts.subject.length > 2 && !parts.object.length) { 61 | parts.object = parts.subject.splice(0, 1); 62 | } 63 | return { 64 | subject: { 65 | type: parts.subject[0], 66 | value: parts.subject[1] 67 | }, 68 | predicate: { 69 | type: parts.predicate[1], 70 | value: parts.predicate[0] 71 | }, 72 | object: { 73 | type: parts.object[0], 74 | value: parts.object[1] 75 | } 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /lib/text/is/plural.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var singularize = require('../singularize'); 6 | 7 | /** 8 | * Returns true if the text is pluralized. 9 | * @param {String} text 10 | * @return {Boolean} 11 | */ 12 | module.exports = function isPlural(text) { 13 | var textEndsWithS = text[text.length - 1].toLowerCase() === 's'; 14 | var textMatchesSingularForm = text.toLowerCase() !== singularize(text).toLowerCase(); 15 | 16 | if (textMatchesSingularForm || textEndsWithS) { 17 | return true; 18 | } 19 | 20 | return false; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/text/singularize.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var singular = require('pluralize').singular; 6 | 7 | /** 8 | * Given text, returns the singularized form. ie: cats => cat. 9 | * @param {String} text [description] 10 | * @return {String} [description] 11 | */ 12 | module.exports = function singularize(text) { 13 | //@info if text exists, return the singularized form, otherwise return the input. 14 | return (text && singular(text)) || text; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/triple.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require*/ 3 | 'use strict'; 4 | 5 | var object = require('./object'); 6 | var subject = require('./subject'); 7 | var predicate = require('./predicate'); 8 | 9 | var parseParts = require('./rules-engine/parse/parts'); 10 | 11 | /** 12 | * Given a classification and a ruleset - returns a triple. 13 | * @param {Array} grammar A list of possible triples. 14 | * @param {Object} vocabulary Extend the known vocabulary by mapping a word stem to a predicate. 15 | * @return {Object} The parsed triple. 16 | */ 17 | module.exports = function triple(classification, rules) { 18 | var parts = parseParts(classification, rules); 19 | return { 20 | subject: subject(parts), 21 | predicate: predicate(parts, rules.vocabulary), 22 | object: object(parts) 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defstream/nl3/418f58b112b35c9108bb3a8c03332b9f1f45907c/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nl3", 3 | "version": "0.0.3", 4 | "description": "nl3 - natural language triples", 5 | "license": "MIT", 6 | "author": "Hector Gray (https://github.com/defstream)", 7 | "main": "index.js", 8 | "engines": { 9 | "node": ">=0.10" 10 | }, 11 | "scripts": { 12 | "doc": "jsdoc -R ./README.md -r -d ./doc/api ./lib ./index.js", 13 | "test": "snyk test && istanbul cover --dir ./doc/coverage _mocha -- --recursive && cat ./doc/coverage/lcov.info | ./node_modules/codecov/bin/codecov", 14 | "analyze": "plato -d ./doc/analysis -r ./lib ./index.js", 15 | "clean": "rm -rf ./doc", 16 | "build": "npm run clean && npm run doc && npm run test && npm run analyze" 17 | }, 18 | "keywords": [ 19 | "nl3", 20 | "nl", 21 | "l3n", 22 | "lne3", 23 | "n3l", 24 | "natural", 25 | "language", 26 | "triple", 27 | "tripple", 28 | "3", 29 | "nodejs", 30 | "node", 31 | "rdf", 32 | "triples", 33 | "english", 34 | "text", 35 | "graph", 36 | "search", 37 | "javascript" 38 | ], 39 | "dependencies": { 40 | "neo-async": "1.8.2", 41 | "pluralize": "1.2.1", 42 | "pos": "0.3.0", 43 | "speakeasy-nlp": "0.2.2", 44 | "stemmer": "0.1.4" 45 | }, 46 | "devDependencies": { 47 | "chai": "3.5.0", 48 | "snyk": "1.14.3", 49 | "codecov": "1.0.1", 50 | "istanbul": "0.4.3", 51 | "jsdoc": "3.4.0", 52 | "mocha": "2.4.5", 53 | "plato": "1.5.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/check.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var async = require('neo-async'); 6 | var mocha = require('mocha'); 7 | var expect = require('chai').expect; 8 | 9 | function test(triple, expected) { 10 | it('should have subject.type equal to ' + expected.subject.type, function( 11 | done) { 12 | expect(triple.subject.type).to.equal(expected.subject.type); 13 | done(); 14 | }); 15 | it('should have subject.value equal to ' + expected.subject.value, function( 16 | done) { 17 | expect(triple.subject.value).to.equal(expected.subject.value); 18 | done(); 19 | }); 20 | 21 | it('should have predicate.value equal to ' + expected.predicate.value, 22 | function(done) { 23 | expect(triple.predicate.value).to.equal(expected.predicate.value); 24 | done(); 25 | }); 26 | 27 | it('should have predicate.type equal to ' + expected.predicate.type, function( 28 | done) { 29 | expect(triple.predicate.type).to.equal(expected.predicate.type); 30 | done(); 31 | }); 32 | it('should have object.type equal to ' + expected.object.type, function(done) { 33 | expect(triple.object.type).to.equal(expected.object.type); 34 | done(); 35 | }); 36 | it('should have object.value equal to ' + expected.object.value, function( 37 | done) { 38 | expect(triple.object.value).to.equal(expected.object.value); 39 | done(); 40 | }); 41 | }; 42 | 43 | /** 44 | * exports check - validates a triple against another object. 45 | * @param {Array} triples 46 | */ 47 | module.exports = function check(triples) { 48 | return { 49 | against: function(expected) { 50 | async.eachSeries(triples, function(triple, done) { 51 | test(triple, expected); 52 | done(); 53 | }); 54 | } 55 | }; 56 | }; 57 | -------------------------------------------------------------------------------- /test/messenger/client/001-client.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var mocha = require('mocha'); 6 | var assert = require('assert'); 7 | var expect = require('chai').expect; 8 | 9 | var nl3 = new require('../client')(); 10 | var check = require('../../check.js'); 11 | 12 | describe('invalid parse parameters', function() { 13 | it('should fail parsing - no parameters', function() { 14 | expect(function() { 15 | nl3.parse(); 16 | }).to.throw(Error); 17 | }); 18 | it('should fail parsing - undefined', function() { 19 | expect(function() { 20 | nl3.parse(undefined); 21 | }).to.throw(Error); 22 | }); 23 | it('should fail parsing - " "', function() { 24 | expect(function() { 25 | nl3.parse(' '); 26 | }).to.throw(Error); 27 | }); 28 | it('should fail parsing - " "', function() { 29 | expect(function() { 30 | nl3.parse(' '); 31 | }).to.throw(Error); 32 | }); 33 | it('should fail parsing non string - 42', function() { 34 | expect(function() { 35 | nl3.parse(42); 36 | }).to.throw(Error); 37 | }); 38 | it('should fail parsing non string - {}', function() { 39 | expect(function() { 40 | nl3.parse({}); 41 | }).to.throw(Error); 42 | }); 43 | it('should fail parsing invalid triple - dogs hate cats', function() { 44 | expect(function() { 45 | nl3.parse('dog jim hates cat sue'); 46 | }).to.throw(Error); 47 | }); 48 | }); 49 | 50 | 51 | describe('parse + callback', function() { 52 | it('should fail parsing - no parameters', function(done) { 53 | nl3.parse(null, function(err, result) { 54 | expect(err).to.be.instanceOf(Error); 55 | done(); 56 | }); 57 | }); 58 | 59 | it('should fail parsing - invalid triple', function(done) { 60 | nl3.parse('monkey a jumped on bed b', function(err, result) { 61 | expect(err).to.be.instanceOf(Error); 62 | done(); 63 | }); 64 | }); 65 | 66 | it('should pass parsing - valid triple', function(done) { 67 | nl3.parse('user Aaron messaged user Micah', function(err, result) { 68 | assert(expect(err).to.not.exist); 69 | done(); 70 | }); 71 | }); 72 | 73 | it('should fail parsing - reversed triple', function(done) { 74 | nl3.parse('message 32 created user bob', function(err, result) { 75 | expect(err).to.be.instanceOf(Error); 76 | done(); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/messenger/client/index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var client = require('../../../index'); 6 | 7 | module.exports = function createClient() { 8 | return client({ 9 | /** 10 | * The grammar rules for creating valid triples 11 | * @type {Array} 12 | */ 13 | grammar: [ 14 | 'users follow users', 15 | 'users mention content', 16 | 'users create messages', 17 | 'users send messages', 18 | 'users receive messages', 19 | 'users message users' 20 | ], 21 | /** 22 | * Allows you to extend the vocabulary of your predicates mapping phonetic roots to your existing grammar. 23 | * @type {Object} 24 | */ 25 | vocabulary: { 26 | follow: 'follow', // user bob followed user jill 27 | stalk: 'follow', // user bob stalked user tom 28 | watch: 'follow', // user bob watches user bill 29 | creat: 'create', // user bob created message 12 30 | made: 'create', // user bob made mesage 34 31 | wrote: 'create', // user bob wrote mesage 55 32 | send: 'send', // user bob sends message 12 33 | sent: 'send', // user bob sent message 34 34 | mail: 'send', // user bob mailed message 55 35 | retriev: 'receive', // user jill recieved message 12 36 | receiv: 'receive', // user tom received message 34 37 | reciev: 'receive', // user tom recieved message 34 38 | got: 'receive', // user bill got message 55 39 | messag: 'message', // user bob messaged user jill 40 | msg: 'message', // user bob msgd user tom 41 | contact: 'message', // user bob msgd conctacted user bill 42 | } 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /test/messenger/follow/001-user-follows-user.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var mocha = require('mocha'); 6 | var expect = require('chai').expect; 7 | var nl3 = require('../client')(); 8 | var check = require('../../check.js'); 9 | 10 | describe('users following users', function test() { 11 | var queries = [ 12 | nl3.parse('users that follow user 42'), 13 | nl3.parse('users who follow user 42'), 14 | nl3.parse('users following user 42'), 15 | nl3.parse('users followed user 42'), 16 | nl3.parse('users which follow user 42'), 17 | nl3.parse('users stalking user 42'), 18 | nl3.parse('users who stalk user 42'), 19 | nl3.parse('users which stalk user 42'), 20 | nl3.parse('users watching user 42'), 21 | nl3.parse('users who watch user 42'), 22 | nl3.parse('users followed by user 42') 23 | ]; 24 | check(queries).against({ 25 | subject: { 26 | type: 'user', 27 | value: undefined 28 | }, 29 | predicate: { 30 | value: 'follow' 31 | }, 32 | object: { 33 | type: 'user', 34 | value: '42' 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/messenger/message/001-user-creates-message.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var mocha = require('mocha'); 6 | var nl3 = new require('../client')(); 7 | var check = require('../../check.js'); 8 | 9 | describe('users create messages', function() { 10 | var queries = [ 11 | nl3.parse('user bob creates message 42'), 12 | nl3.parse('user bob created message 42'), 13 | nl3.parse('user bob wrote message 42'), 14 | nl3.parse('user bob made message 42') 15 | ]; 16 | check(queries).against({ 17 | subject: { 18 | type: 'user', 19 | value: 'bob' 20 | }, 21 | predicate: { 22 | value: 'create' 23 | }, 24 | object: { 25 | type: 'message', 26 | value: '42' 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/messenger/message/002-user-sends-message.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var mocha = require('mocha'); 6 | var nl3 = require('../client')(); 7 | var check = require('../../check.js'); 8 | 9 | describe('users sends messages', function() { 10 | var queries = [ 11 | nl3.parse('user bob sent message 42'), 12 | nl3.parse('user bob sends message 42'), 13 | nl3.parse('user bob mailed message 42'), 14 | nl3.parse('user bob sended message 42') 15 | ]; 16 | check(queries).against({ 17 | subject: { 18 | type: 'user', 19 | value: 'bob' 20 | }, 21 | predicate: { 22 | value: 'send' 23 | }, 24 | object: { 25 | type: 'message', 26 | value: '42' 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/messenger/message/003-user-receives-message.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var mocha = require('mocha'); 6 | var nl3 = require('../client')(); 7 | var check = require('../../check.js'); 8 | 9 | describe('user receives message', function() { 10 | var queries = [ 11 | nl3.parse('user bob got message 42'), 12 | nl3.parse('user bob received message 42'), 13 | nl3.parse('user bob retrieved message 42'), 14 | nl3.parse('user bob recieved message 42') 15 | ]; 16 | check(queries).against({ 17 | subject: { 18 | type: 'user', 19 | value: 'bob' 20 | }, 21 | predicate: { 22 | value: 'receive' 23 | }, 24 | object: { 25 | type: 'message', 26 | value: '42' 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/messenger/message/004-user-messages-user.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module, require,describe,it*/ 3 | 'use strict'; 4 | 5 | var mocha = require('mocha'); 6 | var nl3 = new require('../client')(); 7 | var check = require('../../check.js'); 8 | 9 | describe('user messages user', function() { 10 | var queries = [ 11 | nl3.parse('user bob msg user jill'), 12 | nl3.parse('user bob msgs user jill'), 13 | nl3.parse('user bob messaged user jill'), 14 | nl3.parse('user bob contacted user jill'), 15 | nl3.parse('user bob contacts user jill') 16 | ]; 17 | check(queries).against({ 18 | subject: { 19 | type: 'user', 20 | value: 'bob' 21 | }, 22 | predicate: { 23 | value: 'message' 24 | }, 25 | object: { 26 | type: 'user', 27 | value: 'jill' 28 | } 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/unit/flip/001-flip.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | /*global module,require,describe,it*/ 3 | 'use strict'; 4 | 5 | var assert = require('assert'); 6 | var mocha = require('mocha'); 7 | var expect = require('chai').expect; 8 | 9 | var flip = require('../../../lib/rules-engine/flip'); 10 | var check = require('../../check.js'); 11 | 12 | describe('Flip.it', function() { 13 | it('should exist', function() { 14 | assert(expect(flip).to.exist); 15 | assert(expect(flip.it).to.exist); 16 | }); 17 | 18 | function opts(triple) { 19 | return { 20 | "classification": { 21 | "owner": "message 32 created user bob", 22 | "subject": "message 32 created user bob", 23 | "tokens": [ 24 | "message", 25 | "32", 26 | "created", 27 | 28 | "user", 29 | "bob", 30 | "." 31 | ], 32 | "verbs": [ 33 | "created" 34 | ], 35 | "nouns": [ 36 | "message", 37 | "user", 38 | "bob" 39 | ], 40 | "adjectives": [], 41 | "parts": [ 42 | [ 43 | "message", 44 | "NN" 45 | ], 46 | [ 47 | "32", 48 | "CD" 49 | ], 50 | [ 51 | "created", 52 | "VBN" 53 | ], 54 | [ 55 | "user", 56 | "NN" 57 | ], 58 | [ 59 | "bob", 60 | "NN" 61 | ] 62 | ], 63 | "text": "message 32 created user bob", 64 | "value": { 65 | "subject": { 66 | "type": "message", 67 | "value": "32" 68 | }, 69 | "predicate": { 70 | "value": "created" 71 | }, 72 | "object": { 73 | "type": "user", 74 | "value": "bob", 75 | } 76 | } 77 | }, 78 | "triple": triple, 79 | "rules": { 80 | "subjects": { 81 | "user": { 82 | "predicates": { 83 | "follow": { 84 | "objects": [ 85 | "user" 86 | ] 87 | }, 88 | "mention": { 89 | "objects": [ 90 | "content" 91 | ] 92 | }, 93 | "create": { 94 | "objects": [ 95 | "message" 96 | ] 97 | }, 98 | "send": { 99 | "objects": [ 100 | "message" 101 | ] 102 | }, 103 | "receive": { 104 | "objects": [ 105 | "message" 106 | ] 107 | }, 108 | "message": { 109 | "objects": [ 110 | "user" 111 | ] 112 | } 113 | } 114 | } 115 | }, 116 | "predicates": [ 117 | "follow", 118 | "mention", 119 | "create", 120 | "send", 121 | "receive", 122 | "message" 123 | ], 124 | "objects": { 125 | "user": { 126 | "predicates": { 127 | "follow": { 128 | "subjects": [ 129 | "user" 130 | ] 131 | }, 132 | "message": { 133 | "subjects": [ 134 | "user" 135 | ] 136 | } 137 | } 138 | }, 139 | "content": { 140 | "predicates": { 141 | "mention": { 142 | "subjects": [ 143 | "user" 144 | ] 145 | } 146 | } 147 | }, 148 | "message": { 149 | "predicates": { 150 | "create": { 151 | "subjects": [ 152 | "user" 153 | ] 154 | }, 155 | "send": { 156 | "subjects": [ 157 | "user" 158 | ] 159 | }, 160 | "receive": { 161 | "subjects": [ 162 | "user" 163 | ] 164 | } 165 | } 166 | } 167 | }, 168 | "vocabulary": { 169 | "follow": "follow", 170 | "stalk": "follow", 171 | "watch": "follow", 172 | "creat": "create", 173 | "made": "create", 174 | "wrote": "create", 175 | "send": "send", 176 | "sent": "send", 177 | "mail": "send", 178 | "retriev": "receive", 179 | "receiv": "receive", 180 | "reciev": "receive", 181 | "got": "receive", 182 | "messag": "message", 183 | "msg": "message", 184 | "contact": "message" 185 | } 186 | } 187 | }; 188 | 189 | } 190 | it('should flip different', function() { 191 | var triple = { 192 | subject: { 193 | type: 'user', 194 | value: 'Bob' 195 | }, 196 | predicate: { 197 | value: 'message' 198 | }, 199 | object: { 200 | type: 'user', 201 | value: 'Bob' 202 | } 203 | }; 204 | 205 | var originalSubject = triple.subject; 206 | var originalObject = triple.object; 207 | var flippedTriple = flip.it(opts(triple)); 208 | 209 | expect(flippedTriple.object).to.equal(originalSubject); 210 | expect(flippedTriple.subject).to.equal(originalObject); 211 | }); 212 | }); 213 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | # This references the default nodejs container from 2 | # the Docker Hub: https://registry.hub.docker.com/_/node/ 3 | # If you want Nodesource's container you would reference nodesource/node 4 | # Read more about containers on our dev center 5 | # http://devcenter.wercker.com/docs/containers/index.html 6 | box: node 7 | # This is the build pipeline. Pipelines are the core of wercker 8 | # Read more about pipelines on our dev center 9 | # http://devcenter.wercker.com/docs/pipelines/index.html 10 | 11 | # You can also use services such as databases. Read more on our dev center: 12 | # http://devcenter.wercker.com/docs/services/index.html 13 | # services: 14 | # - postgres 15 | # http://devcenter.wercker.com/docs/services/postgresql.html 16 | 17 | # - mongodb 18 | # http://devcenter.wercker.com/docs/services/mongodb.html 19 | build: 20 | # The steps that will be executed on build 21 | # Steps make up the actions in your pipeline 22 | # Read more about steps on our dev center: 23 | # http://devcenter.wercker.com/docs/steps/index.html 24 | steps: 25 | # A step that executes `npm install` command 26 | - npm-install 27 | # A step that executes `npm test` command 28 | - npm-test 29 | 30 | # A custom script step, name value is used in the UI 31 | # and the code value contains the command that get executed 32 | - script: 33 | name: echo nodejs information 34 | code: | 35 | echo "node version $(node -v) running" 36 | echo "npm version $(npm -v) running" 37 | --------------------------------------------------------------------------------