natural 6.10.5 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +21 -0
- package/.github/workflows/node.js.yml +1 -1
- package/.github/workflows/npm-publish.yml +4 -4
- package/example.txt +1 -0
- package/lib/natural/classifiers/classifier.js +0 -1
- package/lib/natural/classifiers/index.d.ts +2 -3
- package/lib/natural/classifiers/logistic_regression_classifier.js +1 -1
- package/lib/natural/util/storage/File.js +21 -0
- package/lib/natural/util/storage/Memcached.js +26 -0
- package/lib/natural/util/storage/MongoDB.js +27 -0
- package/lib/natural/util/storage/Postgres.js +89 -0
- package/lib/natural/util/storage/Redis.js +32 -0
- package/lib/natural/util/storage/StorageBackend.js +77 -0
- package/lib/natural/util/storage/docker-compose.yml +28 -0
- package/package.json +7 -1
package/.env
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Storage backend you want to use
|
|
2
|
+
NATURAL_STORAGE=PG
|
|
3
|
+
# Settings for Postgres
|
|
4
|
+
PG_USER=user
|
|
5
|
+
PG_PASSWORD=password
|
|
6
|
+
PG_DATABASE=naturaldb
|
|
7
|
+
PG_HOST=localhost
|
|
8
|
+
PG_PORT=5432
|
|
9
|
+
PG_TABLE=naturaltable
|
|
10
|
+
# Settings for Redis
|
|
11
|
+
REDIS_HOST=localhost
|
|
12
|
+
REDIS_PORT=6379
|
|
13
|
+
# Settings for Memcached
|
|
14
|
+
MEM_HOST=localhost
|
|
15
|
+
MEM_PORT=11211
|
|
16
|
+
# Settings for MongoDB
|
|
17
|
+
MONGO_HOST=localhost
|
|
18
|
+
MONGO_PORT=27017
|
|
19
|
+
MONGO_DATABASE=naturaldb
|
|
20
|
+
# Path to the folder writing files
|
|
21
|
+
FS_PATH=.
|
|
@@ -11,10 +11,10 @@ jobs:
|
|
|
11
11
|
build:
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
15
|
- uses: actions/setup-node@v4
|
|
16
16
|
with:
|
|
17
|
-
node-version:
|
|
17
|
+
node-version: 20
|
|
18
18
|
- run: npm ci
|
|
19
19
|
- run: npm test
|
|
20
20
|
|
|
@@ -22,10 +22,10 @@ jobs:
|
|
|
22
22
|
needs: build
|
|
23
23
|
runs-on: ubuntu-latest
|
|
24
24
|
steps:
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
26
|
- uses: actions/setup-node@v4
|
|
27
27
|
with:
|
|
28
|
-
node-version:
|
|
28
|
+
node-version: 20
|
|
29
29
|
registry-url: https://registry.npmjs.org/
|
|
30
30
|
- run: npm ci
|
|
31
31
|
- run: git config user.name "$GITHUB_ACTOR"
|
package/example.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"attr1":"val1","attr2":"val2"}
|
|
@@ -72,13 +72,12 @@ declare interface ClassifierOptions {
|
|
|
72
72
|
|
|
73
73
|
declare type ClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: ClassifierBase) => void
|
|
74
74
|
|
|
75
|
-
declare class ClassifierBase {
|
|
75
|
+
declare class ClassifierBase extends events.EventEmitter {
|
|
76
76
|
classifier: ApparatusClassifier
|
|
77
77
|
docs: ClassifierDoc[]
|
|
78
78
|
features: { [key: string]: number | undefined }
|
|
79
79
|
stemmer: Stemmer
|
|
80
80
|
lastAdded: number
|
|
81
|
-
events: events.EventEmitter
|
|
82
81
|
|
|
83
82
|
constructor (classifier: ApparatusClassifier, stemmer?: Stemmer)
|
|
84
83
|
addDocument (text: string | string[], classification: string): void
|
|
@@ -89,7 +88,7 @@ declare class ClassifierBase {
|
|
|
89
88
|
getClassifications (observation: string | string[]): ApparatusClassification[]
|
|
90
89
|
classify (observation: string | string[]): string
|
|
91
90
|
setOptions (options: ClassifierOptions): void
|
|
92
|
-
save (filename: string, callback
|
|
91
|
+
save (filename: string, callback?: ClassifierCallback): void
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
declare type BayesClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: BayesClassifier) => void
|
|
@@ -56,7 +56,7 @@ class LogisticRegressionClassifier extends Classifier {
|
|
|
56
56
|
// needs its matricies to have their widths synced, etc.
|
|
57
57
|
this.lastAdded = 0
|
|
58
58
|
this.classifier = new ApparatusLogisticRegressionClassifier()
|
|
59
|
-
|
|
59
|
+
super.train()
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require('dotenv').config()
|
|
2
|
+
const { v4: uuidv4 } = require('uuid')
|
|
3
|
+
const fs = require('fs').promises
|
|
4
|
+
const path = process.env.FS_PATH
|
|
5
|
+
|
|
6
|
+
// Class for creating file-based storage
|
|
7
|
+
class FilePlugin {
|
|
8
|
+
async store (object, options) {
|
|
9
|
+
const key = uuidv4()
|
|
10
|
+
const data = JSON.stringify(object)
|
|
11
|
+
await fs.writeFile(path + '/' + key + '.json', data)
|
|
12
|
+
return key
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async retrieve (key, options) {
|
|
16
|
+
const data = await fs.readFile(path + '/' + key + '.json', 'utf8')
|
|
17
|
+
return JSON.parse(data)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = FilePlugin
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { v4: uuidv4 } = require('uuid')
|
|
2
|
+
require('dotenv').config()
|
|
3
|
+
const memjs = require('memjs')
|
|
4
|
+
const memCachedConnectionString = process.env.MEM_HOST + ':' +
|
|
5
|
+
process.env.MEM_PORT
|
|
6
|
+
|
|
7
|
+
// Class for creating memcached client including store and retrieve methods
|
|
8
|
+
class MemcachedPlugin {
|
|
9
|
+
constructor () {
|
|
10
|
+
this.client = memjs.Client.create(memCachedConnectionString)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async store (object, options) {
|
|
14
|
+
const key = uuidv4()
|
|
15
|
+
const data = JSON.stringify(object)
|
|
16
|
+
const isStored = await this.client.set(key, data, { expires: 2 })
|
|
17
|
+
return isStored ? key : null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async retrieve (key, options) {
|
|
21
|
+
const result = await this.client.get(key)
|
|
22
|
+
return JSON.parse(result.value.toString())
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = MemcachedPlugin
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require('dotenv').config()
|
|
2
|
+
const mongoose = require('mongoose')
|
|
3
|
+
const mongoDBConnectionString = 'mongodb://' + process.env.MONGO_HOST + ':' +
|
|
4
|
+
process.env.MONGO_PORT + '/' + process.env.MONGO_DATABASE
|
|
5
|
+
|
|
6
|
+
class MongoDBPlugin {
|
|
7
|
+
constructor () {
|
|
8
|
+
mongoose.connect(mongoDBConnectionString, {})
|
|
9
|
+
// We do not enforce a schema with MongoDB
|
|
10
|
+
this.ObjectModel = mongoose.model('ObjectModel', new mongoose.Schema({},
|
|
11
|
+
{ strict: false, versionKey: false }))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async store (object, options) {
|
|
15
|
+
// To make sure the payload is stored as a whole and nog merged with the id object
|
|
16
|
+
const objectToStore = { naturalObject: object }
|
|
17
|
+
const storedObject = await this.ObjectModel.create(objectToStore)
|
|
18
|
+
return storedObject._id
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async retrieve (key, options) {
|
|
22
|
+
const retrievedObject = await this.ObjectModel.findById(key)
|
|
23
|
+
return retrievedObject.naturalObject
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = MongoDBPlugin
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const postgres = require('pg')
|
|
2
|
+
require('dotenv').config()
|
|
3
|
+
|
|
4
|
+
class PostgresPlugin {
|
|
5
|
+
constructor () {
|
|
6
|
+
this.configPostgres()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async configPostgres () {
|
|
10
|
+
// Initialize connection to Postgres
|
|
11
|
+
const client = new postgres.Client({
|
|
12
|
+
user: process.env.PG_USER,
|
|
13
|
+
host: process.env.PG_HOST,
|
|
14
|
+
database: process.env.PG_DATABASE,
|
|
15
|
+
password: process.env.PG_PASSWORD,
|
|
16
|
+
port: process.env.PG_PORT
|
|
17
|
+
})
|
|
18
|
+
this.postgresClient = client
|
|
19
|
+
this.postgresTableName = process.env.PG_TABLE
|
|
20
|
+
// Connect to the database
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
client.connect()
|
|
23
|
+
.then(() => {
|
|
24
|
+
// Call the function to create the table
|
|
25
|
+
createTable(this.postgresTableName)
|
|
26
|
+
resolve()
|
|
27
|
+
})
|
|
28
|
+
.catch(err => {
|
|
29
|
+
console.error('Error connecting to the database:', err)
|
|
30
|
+
reject(err)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Function to create the table
|
|
35
|
+
function createTable (tableName) {
|
|
36
|
+
// We use JSONB (binary JSON format of Postgres) to store objects
|
|
37
|
+
const createTableQuery = `
|
|
38
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
39
|
+
id SERIAL PRIMARY KEY,
|
|
40
|
+
data JSONB
|
|
41
|
+
);
|
|
42
|
+
`
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
client.query(createTableQuery)
|
|
45
|
+
.then(() => {
|
|
46
|
+
resolve()
|
|
47
|
+
})
|
|
48
|
+
.catch(error => {
|
|
49
|
+
reject(error)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Function to insert a JavaScript object by key
|
|
56
|
+
// Returns the id generated by the database
|
|
57
|
+
async store (object, options) {
|
|
58
|
+
try {
|
|
59
|
+
const query = `INSERT INTO ${this.postgresTableName} (data) VALUES ($1) RETURNING id;`
|
|
60
|
+
const result = await this.postgresClient.query(query, [object])
|
|
61
|
+
if (result.rowCount > 0) {
|
|
62
|
+
return result.rows[0].id
|
|
63
|
+
} else {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Error inserting object:', error)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Function to retrieve a JavaScript object by key
|
|
72
|
+
async retrieve (key, options) {
|
|
73
|
+
try {
|
|
74
|
+
const query = `SELECT data FROM ${this.postgresTableName} WHERE id = $1`
|
|
75
|
+
const result = await this.postgresClient.query(query, [key])
|
|
76
|
+
if (result.rows.length > 0) {
|
|
77
|
+
return result.rows[0].data
|
|
78
|
+
} else {
|
|
79
|
+
console.log('No object found with the specified key')
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Error retrieving object:', error)
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = PostgresPlugin
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { v4: uuidv4 } = require('uuid')
|
|
2
|
+
require('dotenv').config()
|
|
3
|
+
const redis = require('redis')
|
|
4
|
+
const redisConnectionString = 'redis://' + process.env.REDIS_HOST + ':' +
|
|
5
|
+
process.env.REDIS_PORT
|
|
6
|
+
|
|
7
|
+
class RedisPlugin {
|
|
8
|
+
constructor () {
|
|
9
|
+
this.configRedis()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async configRedis () {
|
|
13
|
+
this.redisClient = await redis.createClient(redisConnectionString)
|
|
14
|
+
this.redisClient
|
|
15
|
+
.on('error', err => console.log('Redis Client Error', err))
|
|
16
|
+
.connect()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async store (object, options) {
|
|
20
|
+
const key = uuidv4()
|
|
21
|
+
const data = JSON.stringify(object)
|
|
22
|
+
await this.redisClient.set(key, data)
|
|
23
|
+
return key
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async retrieve (key, options) {
|
|
27
|
+
const result = await this.redisClient.get(key)
|
|
28
|
+
return JSON.parse(result)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = RedisPlugin
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const PostgresPlugin = require('./Postgres')
|
|
2
|
+
const MongoDBPlugin = require('./MongoDB')
|
|
3
|
+
const RedisPlugin = require('./Redis')
|
|
4
|
+
const MemcachedPlugin = require('./Memcached')
|
|
5
|
+
const FilePlugin = require('./File')
|
|
6
|
+
|
|
7
|
+
// Enum for different types of storage backends that can be used
|
|
8
|
+
const STORAGE_TYPES = {
|
|
9
|
+
POSTGRES: 'POSTGRES',
|
|
10
|
+
REDIS: 'REDIS',
|
|
11
|
+
MONGODB: 'MONGODB',
|
|
12
|
+
MEMCACHED: 'MEMCACHED',
|
|
13
|
+
FILE: 'FILE'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Standard behaviour of the backend is:
|
|
17
|
+
// - Javascript in and out, so JSON.stringify and JSON.parse is done here.
|
|
18
|
+
// - A key is returned after storing an object.
|
|
19
|
+
// - The key is determined by the storage method, if possible: MongoDB and Postgres can do this.
|
|
20
|
+
// If not, a uuid is created.
|
|
21
|
+
// - File-based storage is no different: a uuid is created and used as filename: uuid.json
|
|
22
|
+
class StorageBackend {
|
|
23
|
+
constructor (storageType, options) {
|
|
24
|
+
if (storageType) {
|
|
25
|
+
this.setStorageType(storageType, options)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Sets the storage type. Valid values are POSTGRES, REDIS, MONGODB, MEMCACHED, FILE
|
|
30
|
+
async setStorageType (storageType) {
|
|
31
|
+
if (!storageType) {
|
|
32
|
+
throw new Error('No storage type specified')
|
|
33
|
+
}
|
|
34
|
+
if (this.storageType) {
|
|
35
|
+
throw new Error('Storage type already set')
|
|
36
|
+
}
|
|
37
|
+
this.storageType = storageType
|
|
38
|
+
switch (storageType) {
|
|
39
|
+
case STORAGE_TYPES.POSTGRES:
|
|
40
|
+
this.client = new PostgresPlugin()
|
|
41
|
+
break
|
|
42
|
+
case STORAGE_TYPES.REDIS:
|
|
43
|
+
this.client = new RedisPlugin()
|
|
44
|
+
break
|
|
45
|
+
case STORAGE_TYPES.MONGODB:
|
|
46
|
+
this.client = new MongoDBPlugin()
|
|
47
|
+
break
|
|
48
|
+
case STORAGE_TYPES.MEMCACHED:
|
|
49
|
+
this.client = new MemcachedPlugin()
|
|
50
|
+
break
|
|
51
|
+
case STORAGE_TYPES.FILE:
|
|
52
|
+
this.client = new FilePlugin()
|
|
53
|
+
break
|
|
54
|
+
default:
|
|
55
|
+
throw new Error('Invalid storage type')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Stores value to the selected storage type.
|
|
60
|
+
async store (object) {
|
|
61
|
+
if (this.storageType && this.client) {
|
|
62
|
+
return await this.client.store(object)
|
|
63
|
+
} else {
|
|
64
|
+
throw new Error('Storage type or client not set')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async retrieve (key) {
|
|
69
|
+
if (this.storageType && this.client) {
|
|
70
|
+
return await this.client.retrieve(key)
|
|
71
|
+
} else {
|
|
72
|
+
throw new Error('Storage type or client not set')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { StorageBackend, STORAGE_TYPES }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
version: '3'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
postgres:
|
|
5
|
+
image: postgres:latest
|
|
6
|
+
environment:
|
|
7
|
+
POSTGRES_DB: naturaldb
|
|
8
|
+
POSTGRES_USER: user
|
|
9
|
+
POSTGRES_PASSWORD: password
|
|
10
|
+
ports:
|
|
11
|
+
- "5432:5432"
|
|
12
|
+
|
|
13
|
+
mongodb:
|
|
14
|
+
image: mongo:latest
|
|
15
|
+
environment:
|
|
16
|
+
MONGO_INITDB_DATABASE: naturaldb
|
|
17
|
+
ports:
|
|
18
|
+
- "27017:27017"
|
|
19
|
+
|
|
20
|
+
redis:
|
|
21
|
+
image: redis:latest
|
|
22
|
+
ports:
|
|
23
|
+
- "6379:6379"
|
|
24
|
+
|
|
25
|
+
memcached:
|
|
26
|
+
image: memcached:latest
|
|
27
|
+
ports:
|
|
28
|
+
- "11211:11211"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natural",
|
|
3
3
|
"description": "General natural language (tokenizing, stemming (English, Russian, Spanish), part-of-speech tagging, sentiment analysis, classification, inflection, phonetics, tfidf, WordNet, jaro-winkler, Levenshtein distance, Dice's Coefficient) facilities for node.",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.11.0",
|
|
5
5
|
"homepage": "https://github.com/NaturalNode/natural",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -14,10 +14,16 @@
|
|
|
14
14
|
"afinn-165": "^1.0.2",
|
|
15
15
|
"afinn-165-financialmarketnews": "^3.0.0",
|
|
16
16
|
"apparatus": "^0.0.10",
|
|
17
|
+
"dotenv": "^16.4.5",
|
|
18
|
+
"memjs": "^1.3.2",
|
|
19
|
+
"mongoose": "^8.2.0",
|
|
20
|
+
"pg": "^8.11.3",
|
|
21
|
+
"redis": "^4.6.13",
|
|
17
22
|
"safe-stable-stringify": "^2.2.0",
|
|
18
23
|
"stopwords-iso": "^1.1.0",
|
|
19
24
|
"sylvester": "^0.0.12",
|
|
20
25
|
"underscore": "^1.9.1",
|
|
26
|
+
"uuid": "^9.0.1",
|
|
21
27
|
"wordnet-db": "^3.1.11"
|
|
22
28
|
},
|
|
23
29
|
"devDependencies": {
|