设为首页 收藏本站
查看: 1069|回复: 0

[经验分享] 使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)

[复制链接]

尚未签到

发表于 2017-12-16 16:40:37 | 显示全部楼层 |阅读模式
这几篇都是我原来首发在 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我们就可以往数据库里添加东西了,不知道哪些用户名可以用?相当简单,反正我用的全是一名人的名字(英文名),外国人也喜欢抢注名字,嘿嘿嘿
DSC0000.jpg


原文第十三步,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))这样的方式返回最大值。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-424749-1-1.html 上篇帖子: mongodb中修改类型 下篇帖子: 【数据库】Mean web开发 04-MongoDB在Linux上的安装及遇到的问题
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表