这几篇都是我原来首发在 segmentfault 上的地址:https://segmentfault.com/a/1190000005040834 突然想起来我这个博客冷落了好多年了,也该更新一下,呵呵

前篇

使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(一)

使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(二)

原文第十三步,Express API路由

第一个路由是用来创建角色的

app.post('/api/characters',(req,res,next) => {
let gender = req.body.gender;
let characterName = req.body.name;
let characterIdLookupUrl = 'https://api.eveonline.com/eve/CharacterId.xml.aspx?names=' + characterName; const parser = new xml2js.Parser(); async.waterfall([
function(callback) {
request.get(characterIdLookupUrl,(err,request,xml) => {
if(err) return next(err);
parser.parseString(xml,(err,parsedXml) => {
try {
let characterId = parsedXml.eveapi.result[0].rowset[0].row[0].$.characterID; app.models.character.findOne({ characterId: characterId},(err,model) => {
if(err) return next(err); if(model) {
return res.status(400).send({ message: model.name + ' is alread in the database'});
} callback(err,characterId);
});
} catch(e) {
return res.status(400).send({ message: ' xml Parse Error'});
}
});
});
},
function(characterId) {
let characterInfoUrl = 'https://api.eveonline.com/eve/CharacterInfo.xml.aspx?characterID=' + characterId;
console.log(characterInfoUrl);
request.get({ url: characterInfoUrl },(err,request,xml) => {
if(err) return next(err);
parser.parseString(xml, (err,parsedXml) => {
if (err) return res.send(err);
try{
let name = parsedXml.eveapi.result[0].characterName[0];
let race = parsedXml.eveapi.result[0].race[0];
let bloodline = parsedXml.eveapi.result[0].bloodline[0];
app.models.character.create({
characterId: characterId,
name: name,
race: race,
bloodline: bloodline,
gender: gender
},(err,model) => {
if(err) return next(err);
res.send({ message: characterName + ' has been added successfully!'});
});
} catch (e) {
res.status(404).send({ message: characterName + ' is not a registered citizen of New Eden',error: e.message });
}
});
});
}
]);
});

是不是看起来和原文的基本一模一样,只不过把var 变成了let 匿名函数变成了ES6的'=>'箭头函数,虽然我用的是warterline而原文中用的是mongoose但是包括方法名基本都一样,所以我感觉waterline是在API上最接近mongoose

顺便说一下,我为什么不喜欢mongodb,仅仅是因为有一次我安装了,只往里面写了几条测试数据,按文本算最多几kb,但第二天重启机器的时候,系统提示我,我的/home分区空间不足了(双系统分区分给linux分小了本来就不大),结果一查mongodb 的data文件 有2G多,我不知道什么原因,可能是配置不对还是别的什么原因,反正,当天我就把它删除了,

完成了这个API我们就可以往数据库里添加东西了,不知道哪些用户名可以用?相当简单,反正我用的全是一名人的名字(英文名),外国人也喜欢抢注名字,嘿嘿嘿

原文第十三步,Home组件

基本保持和原文一样,只是用lodash 替换了 underscore

一开始我看到网上介绍lodash是可以无缝替换underscore,中要修改引用就可以,但是我用的版本是4.11.2已经有很多方法不一样了,还去掉了不少方法(没有去关注underscore是不是也在最新版本中有同样的改动)

原文中:

......
import {first, without, findWhere} from 'underscore';
...... var loser = first(without(this.state.characters, findWhere(this.state.characters, { characterId: winner }))).characterId; ......

修改为:

......
import {first, filter} from 'lodash';
...... let loser = first(filter(this.state.characters,item => item.characterId != winner )).characterId;

findWhere 在最新版本的lodash中已经不存正,我用了filter来实现相同功能。

第十四步:Express API 路由(2/2)

GET /api/characters

原文的实现方法

/**
* GET /api/characters
* Returns 2 random characters of the same gender that have not been voted yet.
*/
app.get('/api/characters', function(req, res, next) {
var choices = ['Female', 'Male'];
var randomGender = _.sample(choices); Character.find({ random: { $near: [Math.random(), 0] } })
.where('voted', false)
.where('gender', randomGender)
.limit(2)
.exec(function(err, characters) {
if (err) return next(err); if (characters.length === 2) {
return res.send(characters);
} var oppositeGender = _.first(_.without(choices, randomGender)); Character
.find({ random: { $near: [Math.random(), 0] } })
.where('voted', false)
.where('gender', oppositeGender)
.limit(2)
.exec(function(err, characters) {
if (err) return next(err); if (characters.length === 2) {
return res.send(characters);
} Character.update({}, { $set: { voted: false } }, { multi: true }, function(err) {
if (err) return next(err);
res.send([]);
});
});
});
});

可以看到原文中用{ random: { $near: [Math.random(), 0] } }做为查询条件从而在数据库里取出两条随机的记录返回给页面进行PK,前文说过random的类型在mysql没有类似的,所以我把这个字段删除了。本来mysql,可以用order by rand() 之类的方法但是,waterlinesort(order by rand())不被支持,所以我是把所有符合条件的记录取出来,能过lodashsampleSize方法从所有记录中获取两天随机记录。

app.get('/api/characters', (req,res,next) => {
let choice = ['Female', 'Male'];
let randomGender = _.sample(choice);
//原文中是通过nearby字段来实现随机取值,waterline没有实现mysql order by rand()返回随机记录,所以返回所有结果,用lodash来处理
app.models.character.find()
.where({'voted': false})
.exec((err,characters) => {
if(err) return next(err); //用lodash来取两个随机值
let randomCharacters = _.sampleSize(_.filter(characters,{'gender': randomGender}),2);
if(randomCharacters.length === 2){
//console.log(randomCharacters);
return res.send(randomCharacters);
} //换个性别再试试
let oppsiteGender = _.first(_.without(choice, randomGender));
let oppsiteCharacters = _.sampleSize(_.filter(characters,{'gender': oppsiteGender}),2); if(oppsiteCharacters === 2) {
return res.send(oppsiteCharacters);
}
//没有符合条件的character,就更新voted字段,开始新一轮PK
app.models.character.update({},{'voted': false}).exec((err,characters) => {
if(err) return next(err);
return res.send([]);
}); }); });

在数据量大的情况下,这个的方法性能上肯定会有问题,好在我们只是学习过程,数据量也不大。将就用一下,能实现相同的功能就可以了。

GET /api/characters/search

这个API之前还有两个API,和原文基本一样,所做的修改只是用了ES6的语法,就不浪费篇幅了,可以去我的github

这一个也只是一点mongoosewaterline的一点点小区别

原文中mongoose的模糊查找是用正则来做的,mysql好像也可以,但是warterline中没有找到相关方法(它的文档太简陋了)

所以原文中

app.get('/api/characters/search', function(req, res, next) {
var characterName = new RegExp(req.query.name, 'i'); Character.findOne({ name: characterName }, function(err, character) {
......

我改成了

app.get('/api/characters/search', (req,res,next) => {
app.models.character.findOne({name:{'contains':req.query.name}}, (err,character) => {
.....

通过contains来查找,其实就是like %sometext%的方法来实现

下面还有两个方法修改的地方也大同小异,就不仔细讲了,看代码吧

GET /api/stats

这个是原文最后一个路由了,

原文中用了一串的函数来获取各种统计信息,原作者也讲了可以优化,哪我们就把它优化一下吧

app.get('/api/stats', (req,res,next) => {
let asyncTask = [];
let countColumn = [
{},
{race: 'Amarr'},
{race: 'Caldari'},
{race: 'Gallente'},
{race: 'Minmatar'},
{gender: 'Male'},
{gender: 'Female'}
];
countColumn.forEach(column => {
asyncTask.push( callback => {
app.models.character.count(column,(err,count) => {
callback(err,count);
});
})
}); asyncTask.push(callback =>{
app.models.character.find()
.sum('wins')
.then(results => {
callback(null,results[0].wins);
});
} ); asyncTask.push(callback => {
app.models.character.find()
.sort('wins desc')
.limit(100)
.select('race')
.exec((err,characters) => {
if(err) return next(err); let raceCount = _.countBy(characters,character => character.race);
console.log(raceCount);
let max = _.max(_.values(raceCount));
console.log(max);
let inverted = _.invert(raceCount);
let topRace = inverted[max];
let topCount = raceCount[topRace]; callback(err,{race: topRace, count: topCount});
});
}); asyncTask.push(callback => {
app.models.character.find()
.sort('wins desc')
.limit(100)
.select('bloodline')
.exec((err,characters) => {
if(err) return next(err); let bloodlineCount = _.countBy(characters,character => character.bloodline);
let max = _.max(_.values(bloodlineCount));
let inverted = _.invert(bloodlineCount);
let topBloodline = inverted[max];
let topCount = bloodlineCount[topBloodline]; callback(err,{bloodline: topBloodline, count: topCount});
});
}); async.parallel(asyncTask,(err,results) => {
if(err) return next(err);
res.send({
totalCount: results[0],
amarrCount: results[1],
caldariCount: results[2],
gallenteCount: results[3],
minmatarCount: results[4],
maleCount: results[5],
femaleCount: results[6],
totalVotes: results[7],
leadingRace: results[8],
leadingBloodline:results[9]
});
})
});

我把要统计数据的字段放入一个数组countColumn通过forEach把push到asyncTask,最后两个统计方法不一样的函数,单独push,最后用async.parallel方法执行并获得结果。

underscore的max方法可以从{a:1,b:6,d:2,e:3}返回最大值,但是lodash新版中的不行,只能通过_.max(_.values(bloodlineCount))这样的方式返回最大值。

使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)的更多相关文章

  1. Node+Express+MongoDB + Socket.io搭建实时聊天应用

    Node+Express+MongoDB + Socket.io搭建实时聊天应用 前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战 ...

  2. Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建

    前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...

  3. Node+Express+MongoDB+Socket.io搭建实时聊天应用实战教程(一)--MongoDB入门

    前言 本文并不是网上流传的多少天学会MongoDB那种全面的教程,而意在总结这几天使用MongoDB的心得,给出一个完整的Node+Express+MongoDB+Socket.io搭建实时聊天应用实 ...

  4. 《Node.js+MongoDB+AngularJS Web开发》读书笔记及联想

    总体介绍 <Node.js+MongoDB+AngularJS Web开发>,于2015年6月出版,是一本翻译过来的书,原书名为<Node.js,MongoDB and Angula ...

  5. Node.js 和Socket.IO 实现chat WEBIM

    socket官方:   http://socket.io/  需求:实现WEB IM功能,数据从服务器PUSH  不是PULL  websocket是基于HTML5的新特性,不兼容IE6,7,8 .. ...

  6. 使用Node.js的socket.io模块开发实时web程序

    首发:个人博客,更新&纠错&回复 今天的思维漫游如下:从.net的windows程序开发,摸到nodejs的桌面程序开发,又熟悉了一下nodejs,对“异步”的理解有了上上周对操作系统 ...

  7. node.js和socket.io纯js实现的即时通讯实例分享

    在这个例子中,其实node.js并没有真正起到服务器的作用,因为我们这里可以直接运行client.html文件,而不用输入url请求,当 然,要想输入url请求页面内容还需要加入请求静态文件的代码.这 ...

  8. node.js和socket.io实现im

    im——Instant Messaging 即时通讯 基本技术原理 (1)通过IM服务器登陆或注销 (2)用户A通过列表找到B,用户B获得消息并与之交谈 (3)通过IM服务器指引建立与B单独的通讯通道 ...

  9. [Node.js] 基于Socket.IO 的私聊

    原文地址:http://www.moye.me/2015/01/02/node_socket-io/ 引子 最近听到这么一个问题:Socket.IO 怎么实现私聊?换个提法:怎么定位到人(端),或者说 ...

随机推荐

  1. fastjson生成和解析json数据,序列化和反序列化数据

    本文讲解2点: 1. fastjson生成和解析json数据 (举例:4种常用类型:JavaBean,List<JavaBean>,List<String>,List<M ...

  2. JavaI/O系统

    I/O:(输入/输出)指的是计算机与外部世界,或者一个与计算机其余部分的接口.它对任何计算机系统都非常关键. Java类库中有大量的类,帮助我们从不同的设备读取数据并保存或输出到不同的设备中. 这些类 ...

  3. poj 1080 (LCS变形)

    Human Gene Functions 题意: LCS: 设dp[i][j]为前i,j的最长公共序列长度: dp[i][j] = dp[i-1][j-1]+1;(a[i] == b[j]) dp[i ...

  4. python语法快速入门(1)

    http://www.runoob.com/python/python-tutorial.html Python 是一种解释型语言: 这意味着开发过程中没有了编译这个环节.类似于PHP和Perl语言 ...

  5. Windows 8.1 应用再出发 - 几种更新的控件

    Windows 8.1 除了新增了很多很有用的控件外,还对一些控件做出了更新.接下来我们一起对这些更新的控件一一做出讲解. 1. FlipView 更新 翻转视图控件,在应用中常用作图片等内容的翻页/ ...

  6. BZOJ3175 Tjoi2013 攻击装置(二分图匹配)

    传送门 Description 给定一个01矩阵,其中你可以在0的位置放置攻击装置.每一个攻击装置(x,y)都可以按照"日"字攻击其周围的 8个位置(x-1,y-2),(x-2,y ...

  7. java基础-jdbc——三种方式加载驱动建立连接

    String url = "jdbc:mysql://localhost:3306/student?Unicode=true&characterEncoding=utf-8" ...

  8. C++混合编程之idlcpp教程Lua篇(5)

    上一篇在这 C++混合编程之idlcpp教程Lua篇(4) 第一篇在这 C++混合编程之idlcpp教程(一) 与前面的工程相似,工程LuaTutorial3中,同样加入了三个文件:LuaTutori ...

  9. Java中利用MessageFormat对象实现类似C# string.Format方法格式化

    我们在写C#代码的时候常常会使用到string.Format("待格式化字符串{0},{1},....",参数1,参数2,...),来格式化字符串,特别是拼接字符的时候,这种方式使 ...

  10. 基于阿里云容器服务用docker容器运行ASP.NET 5示例程序

    小试阿里云容器服务 之后,接下来有一个挡不住的小试冲动--用docker容器运行程序.首先想到的程序是 ASP.NET 5示例程序,于是参考msdn博客中的这篇博文 Running ASP.NET 5 ...