Skip to main content

· One min read

https://github.com/axa-group/nlp.js

NLP.js

"NLP.js" is a general natural language utilities for nodejs. Currently supporting:

  • Guess the language of a phrase
  • Fast levenshtein distance of two strings
  • Search the best substring of a string with less levenshtein distance to a given pattern.
  • Get stemmers and tokenizers for several languages.
  • Sentiment Analysis for phrases (with negation support).
  • Named Entity Recognition and management, multilanguage, and accepting similar strings, so the introduced text does not need to be exact.
  • Natural Language Processing Classifier, to classify utterance into intents.
  • Natural Language Generation Manager, so from intents and conditions it can generate an answer.
  • NLP Manager: a tool able to manage several languages, the Named Entities for each language, the utterance and intents for the training of the classifier, and for a given utterance return the entity extraction, the intent classification and the sentiment analysis. Also, it is able to maintain a Natural Language Generation Manager for the answers.

· One min read

It is in fact possible to listen to the "expired" type keyevent notification using a subscribed client to the specific channel ( and listening to its message event.

通过 subscribe client 可以监听 __keyevent@{db}__:expired ( db 是你传入的配置 ) 频道来接收过期的事件 ,

const redis = require('redis')
const CONF = {db:3}

let pub, sub

// Activate "notify-keyspace-events" for expired type events
pub = redis.createClient(CONF)
pub.send_command('config', ['set','notify-keyspace-events','Ex'], subscribeExpired)

// Subscribe to the "notify-keyspace-events" channel used for expired type events
function subscribeExpired(e, r){
sub = redis.createClient(CONF)
const expired_subKey = `__keyevent@${CONF.db}__:expired`

sub.subscribe(expired_subKey, function () {
console.log(' [i] Subscribed to "'+expired_subKey+'" event channel : '+r)
sub.on('message', function (chan, msg){
console.log('[expired]', msg)}
)
TestKey()
})
}

//例如,设置一个 key 并设置 10s 超时
function TestKey(){
pub.set('testing', 'redis notify-keyspace-events : expired')
pub.expire('testing', 10)
}

· One min read

DB search sql:

  1. 筛选出拥有 openId 的用户,对这些用户进行按年月日分组,并统计每天新增的用户数
db.user.aggregate(
[
{
$match: { "openId": { $exists: true } }
},
{
$group : {
_id : { month: { $month: "$createDate" }, day: { $dayOfMonth: "$createDate" }, year: { $year: "$createDate" } },
count: { $sum: 1 }
}
}
]
)

· One min read
// "apn": "^2.1.2",
var apn = require('apn');

var service = new apn.Connection(
"passphrase":"secret",
"pfx":"/path/to/cert.p12",
"production":false
});

var apnVoipProvider = new apn.Provider(voipOptions);

apnNotification = new apn.Notification();
apnNotification.badge = 1;
apnNotification.sound = 'msg.mp3';
apnNotification.alert = 'hello';
apnNotification.payload = {
title: notification.title, id: notificationID, path: notification.path,
};

apnVoipProvider.send(apnNotification, token);

· One min read
{
"_id" : "Football",
"ancestors" : [
"Categories",
"Sports and fitness"
],
"parent" : "Sports and fitness"
}

模型定义如下

var mongoose = require('mongoose');

var Category = mongoose.Schema({
_id: String
});

var categorySchema = mongoose.Schema({
ancestors: [Category],
parent: [Category]
});


// Initiate database connection
var db = mongoose.createConnection('mongodb://localhost/Categories');
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
console.log("openDB categories");
});

module.exports.category = db.model('Category', categorySchema);
var categoryModel = require('../models/Category');
var Category = categoryModel.category;

exports.getAncestors = function(req, res) {
if (req.params.id == undefined){res.send("no id specified!"); return;}

Category.findOne({_id: 'Football'}, 'ancestors', function(err, ancestors){
if(err) console.log(err);

res.send(ancestors);
});
}

运行时发生错误

{ message: 'Cast to ObjectId failed for value "Football" at path "_id"',
name: 'CastError',
type: 'ObjectId',
value: 'Football',
path: '_id' }

解决方案

mongoose 默认将 _id 设为 ObjectId 类型。

var categorySchema = mongoose.Schema({
_id: String,
ancestors: [{type: String }],
parent: {type: String}
},{ _id: false });

var Category = mongoose.model( "Category", categorySchema );

· One min read

· One min read

比如我们注册了如下的路由

app.get('/', function (...) { ... });
app.get('/foo/:id', function (...) { ... });
app.post('/foo/:id', function (...) { ... });

然后想要获得的东东是这样的形式:

{
get: [ '/', '/foo/:id' ],
post: [ '/foo/:id' ]
}

该肿么办呢?

在 express 3.x 中

只需要 app.routes 就可以啦

在 express 4.x 中

Applications - built with express()

app._router.stack

Routers - built with express.Router()

router.stack

更多的答案尽在 StackOverflow how-to-get-all-registered-routes-in-express

· One min read

最近在项目中经常发生登录卡死的状况,由于是使用的 passport 单点登录,所以只能一步步排查,加日志。

最后发现是卡在一个 mongodb 查询那里,后来去官网查询后,发现查询超时时间默认是无限大的,所以导致我们看到的现象是 http 无返回,导致 http 超时了 , 因此 nginx 报 502 错误。

找到了 mongoose 文件的Connection部分,在下面可以看到有一些连接参数,其中就有 socketTimeoutMS 和 connectTimeoutMS,点击进入 mongodb 官方文档,可以看到Server 的参数中,socketTimeoutMS 默认是无限大的,这在连接外网 mongodb 时可能导致一些问题,我们需要加上超时参数。

var options = {
server: {
socketOptions: {
autoReconnect: true,
connectTimeoutMS: 10000,
socketTimeoutMS: 10000,
},
},
}

// Good way to make sure mongoose never stops trying to reconnect
mongoose.connect(uri, options);

有待观察后补充


尝试后发现,这个 socketTimeoutsocket 变成时的设置 timeout 是一个效果,闲置超时时间,但是在 linux 下似乎没有什么不正常,在 windwos 上就是启动后,过了这个时间就会抛出一个 timeout 错误。

· One min read

平时直接启动没问题,今天出了这个问题,启动失败,加上配置文件也不行。

D:\redis2.8>redis-server.exe
[8264] 08 Nov 09:28:43.943 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server.exe /path/to/redis.conf
[8264] 08 Nov 09:28:43.948 # Creating Server TCP listening socket *:6379: bind: No such file or directory

Redis 报错:

[6644] 02 Apr 23:11:58.976 # Creating Server TCP listening socket *:6379: bind: No such file or directory

的解决方案如下按顺序输入如下命令就可以连接成功

1. redis-cli.exe
2. shutdown
3. exit
4. redis-server.exe redis.windows.conf

· One min read

Why Yarn

Yarn is Super Fast!!

Yarn caches every package it downloads so it never needs to again. It also parallelizes operations to maximize resource utilization so install times are faster than ever.

Common NPM commands in Yarn

NPM CommandYarn CommandDescription (wherever necessary)
npm installyarn
yarn install
Will install packages listed in the package.json file
npm install pkg-name
npm install --save pkg-name
yarn add pkg-nameBy default Yarn adds the pgk-name to package.json and yarn.lock files
npm install pkg-name@1.0.0yarn add pgk-name@1.0.0
npm install pkg-name --save-devyarn add pkg-name --dev
npm install pkg-name --peeryarn add pkg-name--peer
npm install pkg-name --optionalyarn add --optional
npm install -g pkg-nameyarn global add pkg-nameCareful, yarn add global pkg-name adds packages global and pkg-name locally!
npm updateyarn upgradeNote: It's called upgrade in yarn
npm uninstall pkg-nameyarn remove pkg-name
npm run script-nameyarn run script-name
npm inityarn init
npm packyarn packCreates a compressed gzip archive of the package dependencies
npm linkyarn link
npm outdatedyarn outdated
npm publishyarn publish
npm runyarn run
npm cache cleanyarn cache clean
npm loginyarn login (and logout)
npm testyarn test
npm install --productionyarn --production
npm --versionyarn version
npm infoyarn info

New Commands in Yarn

Yarn CommandDescription
yarn why pkg-name Builds a dependency graph on why this package is being used
yarn cleanFrees up space by removing unnecessary files and folders from dependencies
yarn licenses ls  Inspect the licenses of your dependencies
yarn licenses generate-disclaimer  Automatically create your license dependency disclaimer

References

https://yarnpkg.com/en/docs/


参考

https://github.com/areai51/yarn-cheatsheet

https://shift.infinite.red/npm-vs-yarn-cheat-sheet-8755b092e5cc

· One min read

概念

反引号位 (`) 位于键盘的 Tab 键的上方、1 键的左方。注意与单引号 (\') 位于 Enter 键的左方的区别。 在 Linux 中起着命令替换的作用。命令替换是指 shell 能够将一个命令的标准输出插在一个命令行中任何位置。

如下,shell 会执行反引号中的 date 命令,把结果插入到 echo 命令显示的内容中。

[root@localhost sh] $ echo The date is \`date\`
The date is 2011年 03月 14日 星期一 21:15:43 CST

单引号、双引号用于用户把带有空格的字符串赋值给变量事的分界符。

[root@localhost sh] $ str="Today is Monday"
[root@localhost sh] $ echo $str
Today is Monday

如果没有单引号或双引号,shell 会把空格后的字符串解释为命令。

[root@localhost sh] $ str=Today is Monday
bash: is: command not found

单引号和双引号的区别。单引号告诉 shell 忽略所有特殊字符,而双引号忽略大多数,但不包括 $、\、`。

[root@localhost sh] $ testvalue=100
[root@localhost sh] $ echo 'The testvalue is $testvalue'
The testvalue is $testvalue
[root@localhost sh] $ echo "The testvalue is $testvalue"
The testvalue is 100

场景

我在使用 gitlab 时,使用 shell runner 时,将对应的变量放在了一个单引号的命令中,但是其中的变量始终不替换,后来换成双引号就好了,所以查了一下 linux 下单引号双引号的区别。

· One min read

有的小伙伴可能在开发时需要动态加载模块

常规思路,如果模块有固定的父级路径,比较好办:

var foo = require("./dir/"+somevaribale+".js");

这样即可,进行根据条件不同进行加载。

但是也有情况下,路径是完全不可预测的。

比如我们需要加载一个任意的绝对路径,我们可能会这样写。

var module_path= "/dir/"+somevariable+".js";
var foo= require(module_path);

在 node 后端环境,这样是没有问题的。

但是在 webpack 打包的时候,这样就会有警告了: Critical dependencies

这个问题,是源自 webpack 中的require context问题。

在 github 上也有相关的讨论:

我试了这种方式,发现可以,所以分享一下,但是我没有在 electron 环境下尝试。

// webpack.config.js

{
module: {
// require
unknownContextRegExp: /$^/,
unknownContextCritical: false,

// require(expr)
exprContextRegExp: /$^/,
exprContextCritical: false,

// require("prefix" + expr + "surfix")
wrappedContextRegExp: /$^/,
wrappedContextCritical: false
}
}

· One min read

如果自己一个不小心的提交了一些敏感的信息到 git 上面,

而自己的项目有可能被其他人看到,如果查看历史记录还是能看到自己提交那些文件。那么如果彻底从历史记录中的删除这些文件 操作是在 Linux 下面做的 :

git filter-branch --index-filter 'git rm --cached --ignore-unmatch db.properties*' --prune-empty --tag-name-filter cat -- --all
git push origin master --force
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

· One min read

NTS 框架中采用了新的方式记录浏览器的操作历史,因此在某些带 IFRAME 载入内容的项目中不希望这个 IFRAME 的变化影响到整个系统的操作历史,下面给出两种禁止 IFRAME 记录操作历史的解决方案,各有利弊视具体情况选取。

方案一

原理:利用 location 的 replace 方法载入新页面,关于 replace 方法的描述: When a document is replaced, it is also removed from the history object. Moreover, the user interface navigation methods, such as the Back and Forward buttons, will no longer access the URL. 分析:此方案有个前提条件就是父窗体需要有对 iframe 窗体的访问权限,因此此方案一般用于 iframe 载入的内容和父窗体是同域或者同父域的情形。 范例: case 1

第一种方案主要是通过 replace 来将 url 替换而不是 push 到浏览器历史中

document.getElementById('abc').contentWindow.location.replace(url);

方案二

原理:先销毁已有 iframe 再创建一个新的,然后通过新的 iframe 载入新的页面内容 分析:不存在跨域问题,但是会有 iframe 不断创建销毁的额外开销 范例: untitled2.png

第二种方式主要是删除 iframe 节点之后重新创建一个 iframe 元素。

iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.src = url;

11

· One min read

某些场景可能需要动态的获取端口来启动服务,可能服务化场景,不直接硬配置端口。

哈哈,这看来是要为分布式、服务注册、服务发现做准备呢。

/**
* auto port
* @authors luoyjx (yjk99@qq.com)
* @date 2016-10-16 20:42:57
*/
var net = require('net');

var server = net.createServer();
server.on('error', console.log);
server.on('listening', function () {
var port = server.address().port;

server.close(function () {
console.log('auto port: %s', port);
});
}.bind(this));
// tcp 使用端口 0 表示系统分配端口
server.listen(0);

预想的场景

服务在启动的时候,首先去获取一个可用端口,将自己使用此端口启动,再注册到配置管理中,这样即可实现服务注册了。 配合 etcd 即可实现服务发现,不过还木有实践。

· One min read

基本

git add 添加 git commit -m "xxx" 注释提交 git log 提交日志 git log --pretty=oneline 提交日志在一行显示 git reset --hard HEAD~1 回退到上一个提交 git reset --hard 467dcab 回到某个commit git reflog 查看命令历史 git checkout -- file 丢弃工作区修改 git rm 删除一个文件

暂存工作区

git stash 当前工作现场“储藏”起来 git stash pop 恢复并删除stash内容 git stash apply 恢复 git stash drop 删除stash内容 git stash list 列出stash列表

分支管理

git branch 查看分支
git branch `<name>` 创建分支
git checkout `<name>` 切换分支
git checkout -b `<name>` 创建+切换分支
git merge `<name>` 合并某分支到当前分支
git branch -d `<name>` 删除分支
git branch -D name 强行删除分之
git checkout -b dev origin/dev 创建远程分之到本地
git pull 从远程分之拉取
git branch --set-upstream dev origin/dev 指定本地dev分支与远程origin/dev分支的链接

创建分支,修改代码,合并并删除分支过程

git checkout master          切换到主分支
git checkout -b bug-001 创建bug-001分之
git add file 添加文件
git commit -m "fix bug 001" 提交文件并注释
git checkout master 切换到主分支
git merge --no-ff -m "merge bug fix 001" bug-001 bug-001分之合并到当前分支
git branch -d bug-001 删除bug分支

标签管理

git tag 列出标签
git tag v1 打一个新标签
git tag v1 6224937 给某次提交打tag
git tag -a v1 -m "tag desc" 6224937 指定某一标签打tag并注释
git show v1 看到文字说明
git tag -d v1 删除标签
git push origin tagname 推送标签到远程仓库
git push origin --tags 一次推送所有本地未推送标签
git push origin :refs/tags/tagname 删除远程标签

· One min read

查看版本

先查看下自己机器上的 OpenSSL 版本。

openssl version
cd /usr/local/src/
wget http://www.openssl.org/source/openssl-1.0.1g.tar.gz
tar -zxvf openssl-1.0.1g.tar.gz
cd openssl-1.0.1g
./config shared zlib
make && make install

修改历史的OpenSSL文件设置备份

mv /usr/bin/openssl /usr/bin/openssl.old
mv /usr/include/openssl /usr/include/openssl.old

设置软连接使其使用新的OpenSSL版本 刚刚安装的OpenSSL默认安装在/usr/local/ssl

ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/ssl/include/openssl /usr/include/openssl

更新动态链接库数据

echo "/usr/local/ssl/lib" >> /etc/ld.so.conf
ldconfig -v

我们再来看看 OpenSSL 版本信息.

1 2 3

openssl version

OpenSSL 1.0.1g 7 Apr 2014

如果是 1.0.1g,说明你安装正确了

· One min read

常常会碰到这种需求,对列表中的某个元素进行操作,然后改变这个对象的一个属性值。

今天就在这里踩到了坑。也算是对react渲染机制加深的理解。

是这样的,我有一个好友列表,需要点击添加操作来加好友,而这个用户的对象中有一个属性值是isFriend,再添加成功后需要对其进行改变。

当然,通常的思路就是,把这个列表中的这行记录的那个属性值设为true即可,添加按钮就隐藏掉。 untitled1.png

我在reducer中循环匹配,匹配成功的话就isFriend设置为true,但是呢,奇怪的事情发生了,界面并没有像预想的一样隐藏掉添加按钮。

代码片段如下:

    newArr = oldState.contactsList.map(function mapFunc(contact) {
var result = contact;

if (result.id === action.data) {
result.isFriend = true;
}
return result;
});

return Object.assign({}, oldState, {
contactsList: newArr,
});

看起来确实没有什么不对劲的呀,我打开react-dev-tool看组件的值,也确实改变过来了。什么鬼呢?!

后来找了找,给了我启发,于是动手试了试,果然是因为数组中的对象的引用未改变导致未重新渲染。

修正后的代码如下:

    if (result.id === action.data) {
// 不能直接赋值,此处直接赋值,引用不变,被认为对象为改变,则不重新渲染
result = Object.assign({}, result, {isFriend: true});
}

· One min read

文件下载实际上是二进制流的发送,所以在服务端是就向客户端写流。

而在node.js中,读流和写流又可以通过pipe连接起来,那么思路清晰之后,就是Content-type类型的问题了。

代码如下

var fs = require('fs');
var pdf = fs.createReadStream(path);

res.writeHead(200, {
'Content-Type': 'application/force-download',
'Content-Disposition': 'attachment; filename=test.rar'
});

pdf.pipe(res);

· One min read

RE-Build

Build regular expressions with natural language.

由来

你是否遇到过这么复杂的正则表达式呢?

var ipMatch = /(?:(?:1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){3}(?:1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\b/;

怎么办

使用re-build

var ipNumber = RE.group(
RE ("1").then.digit.then.digit
.or ("2").then.oneOf.range("0", "4").then.digit
.or ("25").then.oneOf.range("0", "5")
.or .oneOf.range("1", "9").then.digit
.or .digit
),

ipMatch = RE.matching.exactly(3).group( ipNumber.then(".") )
.then(ipNumber).then.wordBoundary.regex;

安装

通过npm : `

npm install re-build

通过bower

bower install re-build

文档

https://github.com/MaxArt2501/re-build