Giter VIP home page Giter VIP logo

blog's People

Contributors

huanglong6828 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

shiftwinting

blog's Issues

Vue后台管理系统之 视图、按钮、路由权限

需求

后台管理系统关系到各种权限相关,是最复杂也是最重要的最近基础的一块。
一般权限程度由浅到深:浅程度为路由权限,做到最简单的控制路由用户只能访问对应的页面,深程度为按钮,视图权限,用户都可以查看公共页面数据,却没有操作权限或者管理员视图不能查看。了解清楚自己想做到的程度后我们就由浅到深配置权限吧。

前端的权限控制实质上就是用于展示,让操作变得更加友好,真正的安全实际上是由后端控制的

由浅——路由权限

首先我们了解清楚,路由权限由前端or后端来控制。如果后端返回路由请跳过当前块。
当前模块学习 vue-element-admin 开源项目,再在基础上加自己需要的东西。

  1. 创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。
  2. 当用户登录后,获取用roles/types,将roles/types和路由表/按钮视图表进行权限作比较,生成最终用户可访问的路由表和按钮视图表。
  3. 调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由,和使用自定义指令 v-permission控制按钮视图
  4. 使用vuex管理路由表和按钮视图表。

router.js

// router.js
import Vue from 'vue';
import Router from 'vue-router';

import Login from '../views/login/';
const dashboard = resolve => require(['../views/dashboard/index'], resolve);
//使用了vue-routerd的[Lazy Loading Routes](https://router.vuejs.org/en/advanced/lazy-loading.html)

//所有权限通用路由表 
//如首页和登录页和一些不用权限的公用页面
export const routerMap = [
    //hidden为自定义属性,侧边栏那章会纤细解释
    {
        path: '/login',
        component: Login,
        hidden: true
    },
    {
        path: '/',
        component: Layout,
        redirect: '/dashboard',
        name: '首页',
        children: [{
            path: 'dashboard',
            component: dashboard
        }]
    },
]

//实例化vue的时候只挂载routerMap
export default new Router({
    routes: routerMap
});

//异步挂载的路由
//动态需要根据权限加载的路由表 
//maintenance 代表维保单位typeid(1) m1(主管理员) m2(子管理员) m3(普通员工)
//property 代表使用单位typeid(2) p1(主管理员) p2(子管理员) p3(普通员工)
//government 代表质检单位typeid(3) g1(主管理员) g2(子管理员) g3(普通员工)
export const asyncRouterMap = [{
        path: '/Permission',
        component: Layout,
        redirect: '/Permission/index',
        children: [{
            path: 'index',
            component: Permission,
            name: 'Permission',
            meta: {
                roles: ['m1', 'p1', 'g1'],
                types: ['government', 'property', 'maintenance']
            }
        }]
    },
    {
        path: '*',
        redirect: '/404',
        hidden: true
    }
];

这里我们根据vue-router官方推荐的方法通过meta标签来标示改页面能访问的权限有哪些。

如meta: { roles: ['m1', 'p1', 'g1' ] }表示该页面只有维保单位和使用单位的主管理员才能有资格进入。

注意事项:
这里有一个需要非常注意的地方就是404页面一定要最后加载,如果放在constantRouterMap一同声明了404,后面的所以页面都会被拦截到404,详细的问题见addRoutes when you've got a wildcard route for 404s does not work

router.beforeEach

结合router.beforeEach处理路由权限,此代码包含按钮权限。

// permission.js
router.beforeEach((to, from, next) => {
  if (store.getters.token) { // 判断是否有token
    if (to.path === '/login') {
      next({
        path: '/'
      });
    } else {
      if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
        store.dispatch('GetInfo').then(res => { // 拉取info
          //区别当前用户类型和用户权限匹配符合的路由和按钮视图等
          let type, userRoleId
          if (res.data.typeid == 1) type = 'maintenance'
          if (res.data.typeid == 2) type = 'property'
          if (res.data.typeid == 3) type = 'government'
          userRoleId = type[0] + res.data.userRoleId

          const obj = {
            roles: [userRoleId],
            types: [type]
          }
          store.dispatch('GenerateRoutes', obj).then(() => { // 根据权限生成可访问的路由表
            router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
          })
        }).catch(err => {
          console.log(err);
        });
      } else {
        next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
      next();
    } else {
      next('/login'); // 否则全部重定向到登录页
    }
  }
});

Vuex——permission

通过用户的权限和之前在router.js里面asyncRouterMap的每一个页面所需要的权限做匹配,最后返回一个该用户能够访问路由有哪些。此代码包含了按钮和视图权限。

import { asyncRouterMap, routerMap } from '@/router'
import { constantButtonPermission } from '@/utils/buttonPermission'
/**
 * 通过meta.role判断是否与当前用户权限匹配
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.indexOf(role) >= 0)
  } else {
    return true //此处为true因为递归,首先要保证父级router的一直存在
  }
}

/**
 * 通过meta.type判断当前端
 * @param types
 * @param route
 */
function hasTypes(types, route) {
  if (route.meta && route.meta.types) {
    return types.some(type => route.meta.types.indexOf(type) >= 0)
  } else {
    return true
  }
}

/**
 * 递归过滤异步路由表,返回符合用户角色权限的路由表
 * @param asyncRouterMap
 * @param roles
 * @param types
 */
function filterAsyncRouter(asyncRouterMap, roles, types) {
  const accessedRouters = asyncRouterMap.filter(route => {
    if (hasPermission(roles, route) && hasTypes(types, route)) {
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children, roles, types)
      }
      return true
    }
    return false
  })
  return accessedRouters
}
/**
 * 过滤按钮视图,返回符合用户按钮和视图
 * @param constantButtonPermission
 * @param roles
 * @param types
 */
function filterButton(constantButtonPermission, roles, types) {
  const accessedButton = []
  constantButtonPermission.filter(Button => {
    if (hasPermission(roles, Button) && hasTypes(types, Button)) {
      accessedButton.push(Button.name)
      return true
    }
    return false
  })
  return accessedButton
}
/**
 * 返回所有用户按钮和视图
 * @param constantButtonPermission
 */
function AllButton(constantButtonPermission) {
  const AllPermission = {}
  for (let i = 0; i < constantButtonPermission.length; i++) {
    AllPermission[constantButtonPermission[i].name] = constantButtonPermission[i].name
  }
  return AllPermission
}
const permission = {
  state: {
    routers: routerMap,
    addRouters: [],
    AllPermission: AllButton(constantButtonPermission),
    constantButton: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = routerMap.concat(routers)
    },
    SET_BUTTON_PERMISSIONS: (state, buttonPermissions) => {
      state.constantButton = buttonPermissions
    },
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        const roles = data.roles
        const types = data.types
        let accessedRouters, accessedButtons
        //  当前if判断是某个权限拥有所有菜单 则不需要判断,现在每个权限不同 则需要每次判断
        // if (roles.indexOf('1') >= 0 && types.indexOf('government') >= 0) {
        // accessedRouters = asyncRouterMap
        // } else {
        accessedRouters = filterAsyncRouter(asyncRouterMap, roles, types)
        // }
        accessedButtons = filterButton(constantButtonPermission, roles, types)
        commit('SET_ROUTERS', accessedRouters)
        commit('SET_BUTTON_PERMISSIONS', accessedButtons)
        resolve()
      })
    }
  }
}

export default permission

现在当前用户的权限菜单就获取到了,剩下的菜单渲染就靠自己去实现了。

到深——按钮、视图权限

上面的代码已经判断了用户类型和用户角色,当下我们需要一张按钮和视图数据表。判断按钮是否显示的逻辑和路由基本一致。

buttonPermission

//配置符合当权限的数据表
export const constantButtonPermission = [
{
  name: 'person_details',
  title: '管理员详情',
  meta: {
    roles: ['m1', 'm2', 'm3'],
    types: ['maintenance']
  }
},
{
  name: 'elevator_delete',
  title: '删除按钮',
  meta: {
    roles: ['m1', 'm2', ],
    types: ['maintenance']
  }
}, ]

自定义指令

v-if的响应特性是把双刃剑,因为判断表达式在运行过程中会频繁触发,但实际上在一个用户会话周期内其权限并不会发生变化,因此如果只需要校验权限的话,用v-if会产生大量不必要的运算,这种情况只需在视图载入时校验一次即可,可以通过自定义指令实现:

Vue.directive('permission', {
  bind(el, binding) {
    setTimeout(() => {
      if (!store.state.permission.constantButton.includes(binding.value)) {
        el.parentNode.removeChild(el);
      }
    }, 0)
  }
});

// 传入当前按钮或者视图,自动匹配是否支持当前用户
v-permission='$store.state.permission.AllPermission.person_details'

总结

vue后台管理系统的路由、按钮、视图权限就配置完成。因为本人公司需求有多种用户类型和不同类型有不同权限,结合优秀的@花裤衩的文章,所以很多东西直接复制添加了自己需要的东西,因为他描述很详细本人就不在解释。
代码分享提供大家学习交流,有问题欢迎大家提问。

express+socket.io=通讯服务器搭建(一)

安装

首先假定你已经安装了 Node.js,接下来为你的应用创建一个目录,然后安装express-generator应用骨架

$ mkdir node-demo
$ npm install express-generator -g //mac需要加sudo

express -h

$ express -h
  用法: express [options] [dir]
  选项:

    -h, --help          输出使用信息
    -V, --version       输出版本号
    -e, --ejs           添加ejs引擎支持(默认为jade)
        --hbs           添加handlebars引擎支持
    -H, --hogan         添加hogan.js引擎支持
    -c, --css <engine>  添加stylesheet <engine> 支持 (less|stylus|compass|sass) (默认为CSS)
        --git           添加.gitignore
    -f, --force         非空目录上的 force

express-generator 生成应用骨架

$ express node-demo

执行此命令后会有以下代码

warning: the default view engine will not be jade in future releases
warning: use `--view=jade' or `--help' for additional options

//警告:默认的视图引擎在未来版本中不会是JADE
//警告:使用“-VIEW = JADE”或“帮助”选项

//这里我们重新生成骨架默认为ejs模板 使用以下命令

//express --view=ejs blog
//--view=模板引擎的名称,有很多种
//blog为项目文件夹名称

$ express --view=ejs node-demo  使用ejs模板引擎
// 出现以下信息,安装指示 一步一步进行
   create : node-demo/
   create : node-demo/public/
   create : node-demo/public/javascripts/
   create : node-demo/public/images/
   create : node-demo/public/stylesheets/
   create : node-demo/public/stylesheets/style.css
   create : node-demo/routes/
   create : node-demo/routes/index.js
   create : node-demo/routes/users.js
   create : node-demo/views/
   create : node-demo/views/error.jade
   create : node-demo/views/index.jade
   create : node-demo/views/layout.jade
   create : node-demo/app.js
   create : node-demo/package.json
   create : node-demo/bin/
   create : node-demo/bin/www

   change directory:
     $ cd node-demo

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=node-demo:* npm start

运行express

$ npm start 

//浏览器输入 http://localhost:3000/
//即可看到

//Express
//Welcome to Express

项目文件分析

项目创建成功之后,生成四个文件夹和两个文件,

  1. app.js
    • 这是它的初始形式,这个模块还要继续导出给bin文件夹下的www文件使用
  2. 配置信息文件packetage.json
  3. bin是项目的启动文件,配置以什么方式启动项目,默认 npm start
    • www文件:这里拥有着http服务器的基本配置
  4. public是项目的静态文件,放置js css img等文件
  5. routes是项目的路由信息文件,控制地址路由
  6. views是视图文件,放置模板文件ejs或jade等(其实就相当于html形式文件啦~)
  7. express这样的MVC框架模式,是一个Web项目的基本构成。

进化项目——连接数据库

$ npm install mysql --save

根目录sql文件夹 里面新建sqlConfig.js,内容如下:

// 引入mysql

var mysql = require('mysql');
var pool = mysql.createPool({
  host: "localhost", //这是数据库的地址
  port: "2000",
  user: "root", //需要用户的名字
  password: "12345", //用户密码 ,如果你没有密码,直接双引号就是
  database: "huang" //数据库名字
});

/**
 * @param {*} sql sql语句
 * @param {*} callback 回调函数
 */
function query(sql, callback) {
  pool.getConnection(function (err, connection) {
    connection.query(sql, function (err, rows) {
      callback(err, rows);
      connection.release(); //释放链接
    });
  });
}

exports.query = query;

routes文件夹下面新建admin_user.js对应 admin_user表模块 内容如下:

var express = require('express');
var router = express.Router();

//引入数据库包
var sql = require("../sql/sqlConfig.js");
/**
 * 查询列表页
 */
router.get('/', function (req, res, next) {
  sql.query('select * from admin_user', function (err, rows) {
    if (err) {
      console.log(err)
      res.render('index.ejs', { title: 'Express', datas: [] });
    } else {
      res.render('index.ejs', { title: 'Express', datas: rows });
    }
  })
});

module.exports = router;

sql文件夹 继续新建 AdminUserSQL.js 内容如下:

//admin_user表 sql语句
var AdminUserSQL = {
  queryAll: 'SELECT * FROM admin_user',
};

module.exports = AdminUserSQL;

进入app.js,加入一下代码:

//引入users模块
var AdminUsersRouter = require('./routes/admin_user');
app.use('/get-admin-users', AdminUsersRouter);

启动

http://localhost:3000/get-admin-users

Express
Welcome to Express

userName	姓名
admin	超级管理员

总结

node.js配合express连接mysql获取admin_user表的简单demo完成。
通讯服务器搭建第一步完成,下一步我们将配合socket.io完成消息推送。欢迎大家评论交流。
源码见github

微信小程序之wepy自动化架构搭建(fly+wepy-plugin-replace)

前言

本文章秉着自动化工程项目的**搭建的,基础架子完全按照wepy官网搭建,在基础上增加配置达到自动化项目。新增动flxio拦截器自动处理接口,新增根据环境变量来改变运行时的参数。

Fly.js

小程序拦截器个人不是很满意,在网上就寻找到了fly.js 感谢作者大大@wendux

简介

一个支持所有JavaScript运行环境的基于Promise的、支持请求转发、强大的http请求库。可以让您在多个端上尽可能大限度的实现代码复用,它有如下特点:

  • 提供统一的 Promise API。
  • 浏览器环境下,轻量且非常轻量 。
  • 支持多种JavaScript 运行环境
  • 支持请求/响应拦截器。
  • 自动转换 JSON 数据。
  • 支持切换底层 Http Engine,可轻松适配各种运行环境。
  • 浏览器端支持全局Ajax拦截 。
  • H5页面内嵌到原生 APP 中时,支持将 http 请求转发到 Native。支持直接请求图片

安装/配置使用

npm install flyio --save

新增staticEnv.js 配置接口种类

const base = "/api-login"
const common = "/api-common"
const elevator = "/api-elevator"
const workorder = "/api-workorder"
const device = "/api-device"
const authcontract = "/api-auth-contract"
const contract = "/api-contract"
const events = "/api-events"
export {
  base,
  common,
  elevator,
  workorder,
  device,
  authcontract,
  contract,
  events
}

新增错误代码判断errorCode.js

import Tips from './Tips';

export function errorCodeHandler(errorcode, message) {
  switch (errorcode) {
  case 13001:
    Tips.alert('注册时未填写公司信息!');
    break;
  case 31001:
    Tips.alert('公司待审核!');
    break;
  case 13004:
    Tips.alert('公司待审核!');
    break;
  case 13005:
    Tips.alert('公司待审核!');
    break;
    //登录超时errorCodeHandler
  case 12009:
    Tips.alert(message);
    wx.navigateTo({
      url: '/pages/login/index'
    });
    break;
  case 3008:
    Tips.alert(message);
    wx.navigateTo({
      url: '/pages/login/index'
    });
    break;
  default:
    if (message) {
      Tips.alert(message);
    }
  }
}

新增http.js


import Fly from 'flyio/dist/npm/wx' //npm引入方式
import { base, common, elevator, workorder, device, authcontract, contract, events } from '../api/staticEnv'
import Tips from './Tips';
import { errorCodeHandler } from './errorCode'
//创建fly实例
const fly = new Fly()

//配置请求baseURL
fly.config.baseURL = BASE_API

//添加请求拦截器
fly.interceptors.request.use((config) => {
  Tips.loading();
  // 判断是否存在token,如果存在的话,则每个http header都加上token
  const token = wx.getStorageSync('token')
  if (token) {
    config.headers.tokenStr = token;
  }
  // 统一添加接口种类  "http://api.p1.ettda.com/api-login"
  switch (config.proxy) {
  case 'base':
    config.url = base + config.url;
    break;
  case 'common':
    config.url = common + config.url;
    break;
  case 'elevator':
    config.url = elevator + config.url;
    break;
  case 'workorder':
    config.url = workorder + config.url;
    break;
  case 'device':
    config.url = device + config.url;
    break;
  case 'authcontract':
    config.url = authcontract + config.url;
    break;
  case 'contract':
    config.url = contract + config.url;
    break;
  case 'events':
    config.url = events + config.url;
    break;
  default:
    break;
  }
  return config;
})

//添加响应拦截器,响应拦截器会在then/catch处理之前执行
fly.interceptors.response.use(
  response => {
    Tips.loaded();
    //返回错误代码处理前端页面提示
    if (response.data.code != 0) {
      errorCodeHandler(response.data.code, response.data.message);
      return Promise.reject(response.data)
    }
    return response.data;
  },
  error => {
    if (error.response) {
      Tips.error('出错啦,请稍后再试!')
    }
    return Promise.reject(error); // 返回接口返回的错误信息
  });


export default fly;

使用

import fly from '@/utils/http'
import qs from 'qs'

const config = {
  proxy: 'base' // 接口种类
};

export async function requestLogin(params) {
  return await fly.post('/login', qs.stringify(params), config)
}
<!--wpy文件调用-->

requestLogin({
    username: 'username',
    password: 'password'
}).then(res => {
    console.log(res);
});

完整的fly拦截器我们就完成l,主要公司业务接口毕竟多,我的想法就是模块化每个api种类来维护,使用拦截器就很好的组装接口,节省大家开发时间和减少重复工作。

wepy-plugin-replace

为 plugins 添加 replace 对象,支持单个或者多个规则,多个规则可以以 Array 或者 Object 实现,filter 的对象为生成后文件的路径, 例如'dist/app.js',每个规则也同时支持多个替换条目,同样是以 Array 或者 Object 实现。

简介

多环境对于每个公司来说都是需要的,测试环境,正式环境等等。以前我们都是每次发布时候都手动修改api接口地址,可这样的重复无聊的工作我们就应该交给工具来实现。我们要有一个工程化的**去构建整个项目,减少小伙伴在开发中遇到的无趣工作和节约他们的时间。所以我寻找到了上面的wepy-plugin-replace插件。

配置

wepy.config.js中

plugins: {
    replace: {
      filter: /\.js$/,
      config: {
        find: /BASE_API/,
        replace: function (matchs, word) {
            return process.env.NODE_ENV === 'production' ? '"https://api.a.com/"' : '"https://api.a.com/dev/"'
        }
      }
    }
}

//if判断中也需要新增
if (prod) {
    module.exports.plugins = {
        replace: {
          filter: /\.js$/,
          config: {
            find: /BASE_API/,
            replace: function (matchs, word) {
                return process.env.NODE_ENV === 'production' ? '"https://api.a.com/"' : '"https://api.a.com/dev/"'
            }
          }
        }
    }
}

package.json中

"dev": "cross-env NODE_ENV=development env_config=dev wepy build --watch",
"build": "cross-env NODE_ENV=production env_config=test wepy build --no-cache",

使用

npm run dev或者npm run build。插件就会自动匹配BASE_API然后替换成当前env_config匹配的api接口地址。在http.js中就简单的提现了,同时你也可以使用到其他方式上,

总结

前端工程化是前端架构中重要的一环,主要就是为了解决如何进行高效的多人协作?如何保证项目的可维护性?如何提高项目的开发质量。所以这个文章同样适用于其他地方,希望文章能帮到大家。本人搭建的wepy整体项目已托管到github上,对你有帮助给star

Vue后台管理系统之 多端打包环境配置

前言

前后端分离后,api接口需要前端自己配置api接口地址域名。以前需要自己手动去修改域名,那么每次都做者重复又让人十分无趣的工作。那么我们需要使用工具帮我们完成这些重复又无趣的工作。

不同环境文件配置

vue项目搭建完成后,在config文件夹中可以看到以下两个文件

dev.env.js
prod.env.js

dev.env.js运行环境配置

添加如下配置

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  //开发环境变量
  ENV_CONFIG: '"dev"',
  //开发环境接口地址
  BASE_API: '"http://dev.2018api.com"'
}

prod.env.js运行环境配置

添加如下配置

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  //打包环境变量
  ENV_CONFIG: '"prod"',
  //打包发环境接口地址
  BASE_API: '"http://prod.2018api.com"',
}

新增 test.env.js文件

添加如下配置

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  //测试环境变量
  ENV_CONFIG: '"test"',
  //测试包发环境接口地址
  BASE_API: '"http://test.2018api.com"',
}

webpack打包配置添加

通过package.json配置的scripts命令代码设置process.env.env_config区分不同接口环境。

"build": "cross-env NODE_ENV=production env_config=prod node build/build.js",
"build:test": "cross-env NODE_ENV=production env_config=test node build/build.js",

根据不同命令行,设置env_config文件自动匹配对应文件达到自动化
webpack.prod.conf.js文件修改如下:

// old webpack配置
const env = require('../config/prod.env')

// new webpack配置
const env = require('../config/' + process.env.env_config + '.env')

总结

添加多个.env.js文件和package.json配置的scripts命令可达到多个接口环境避免工作重复,而达到自动化项目的目的

当前文章提供了解决多接口环境的问题,如果对你有帮助请给个Star

前端硬菜之MongoDB

为什么前端需要学习数据库

前后端分离出来,职责也越来越明确。后端负责数据,前端负责展示。可久了你会发现太过于依赖后端自己成长很慢,引用一句码友文章中的话“你不要老想着自己是一个前端,你不是一个前端。你可以写前端,但是你不要限制自己只作为一个前端,不要把自己的眼光局限在前端。”学习后端的知识对我们写小项目和公司应急demo很有帮助,同时站在不同角度来思考整个项目可以让你避免更多的问题出现,最有意思的就是可以装一手b为我自己升职加薪。

为什么选择学习MongoDB

mongodb是文档型数据库与关系型数据库有很大区别,但是它对JavaScript很友好,javascript最爱json,而mongodb也是json存储的,mongodb的操作语言也是javascript,这样来说一点违和感都没有。用mongodb也很方便,不需要事先设置数据结构,字段随时可以加,要是用来保存RESTFUL API请求与返回数据那就更方便了,不论数据有多复杂,直接扔进去就好了。这样说不是很负责,但是这是很直接最可以让人信服的答案。MongoDB与其他数据库的对比优缺点等这里就不赘述了,自行Google。

创建数据库

安利一个很不错的db直连工具mongoDB.app,本文不赘述安装等流程。

一个数据库(Database)中可以包含多个集合(colection),每个集合中,可以包含多个文档。相当于SQL数据库,一个数据库中包含多个表,每个表中包含多个记录。

命令

MongoDB 用 ==use + 数据库名称== 的方式来创建数据库。use 会创建一个新的数据库,如果该数据库存在,则返回这个数据库。

>use hldb
switched to db hldb
//进入到当前数据库

使用命令 ==db== 检查当前选定的数据库,同时也表示当前数据库

>db
hldb

使用命令 show dbs 来检查数据库列表。

>show dbs
admin   0.000GB
config  0.000GB
local   0.000GB

刚创建的数据库 hldb 并不在数据库的列表中, 要显示它,我们需要向 hldb 数据库插入一些数据。

db.users.insert({"name":"隔壁家的老黄","age":23,"job":"垃圾前端"})
  • db:表示的是当前数据表
  • users:表示当前数据库的的一个集合,可以理解为当前数据下的一个表
  • insert():将一个或多个文档插入集合中

删除数据库

MongoDB 中使用==dropDatabase()== 删除数据库它将删除选定的数据库。如果没有选定要删除的数据库,则它会将默认的 test 数据库删除。

>use hldb
switched to db hldb
>db.dropDatabase()
{ "dropped" : "hldb", "ok" : 1 }
>show dbs
admin   0.000GB
config  0.000GB
local   0.000GB

创建集合(类表)

MongoDB 中使用 ==createCollection(name, options)== 方法来创建集合。

  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项
> use hldb
switched to db hldb
> db.createCollection("HlColl")
{ "ok" : 1 }
>

查看已有集合,可以使用 show collections 命令:

> show collections
HlColl
users

删除集合(类表)

MongoDB 中使用 ==drop()== 方法来删除集合。

>use hldb
switched to db hldb
>show collections
HlColl
users
>db.HlColl.drop()
true

插入文档(类数据)

MongoDB 使用 ==insert()== 或 ==save()== 方法向集合中插入文档,语法如下:

db.集合.insert(document)

==insert()== 和 ==save()== 方法当主键"_id"不存在时,都是添加一个新的文档,但主健"_id"存在时,就有些不同了

insert:当主键"_id"在集合中存在时,不做任何处理。

>db.users.insert({"_id":1,"name":"隔壁家老黄2","age":23,"job":"还看得下去"})
WriteResult({ "nInserted" : 1 })

> db.users.insert({"_id":1,"name":"隔壁家老黄2","age":23,"job":"还看得下去,看是否能更新"})
WriteResult({
	"nInserted" : 0,
	"writeError" : {
		"code" : 11000,
		"errmsg" : "E11000 duplicate key error collection: hldb.users index: _id_ dup key: { : 1.0 }"
	}
})

save:当主键"_id"在集合中存在时,进行更新。

>db.users.save({"_id":2,"name":"隔壁家老黄3","age":23,"job":"save方法"})
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 2 })

> db.users.save({"_id":2,"name":"隔壁家老黄3","age":23,"job":"save方法,更新"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

find()方法查看集合下文档

>db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家的老黄", "age" : 23, "job" : "垃圾前端" }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 23, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 23, "job" : "save方法,更新" }

更新文档(类数据)

==update()== 方法用于更新已存在的文档

db.集合.update(
 <query>,
 <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query : update的查询条件,类似sql update查询内where后面的。
  • update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
  • upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  • multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
  • writeConcern :可选,抛出异常的级别。
  • db.collection.update({},{}) 向指定集合更新单个文档
  • db.collection.update({},{},{multi:true}) 向指定集合更新多个文档

在3.2版本开始,MongoDB提供以下更新集合文档的方法:

  • db.collection.updateOne() 向指定集合更新单个文档
  • db.collection.updateMany() 向指定集合更新多个文档

我们通过 update() 方法来更新名字(name)

>db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家的老黄", "age" : 23, "job" : "垃圾前端" }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 23, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 23, "job" : "save方法,更新" }

>db.users.update({"name":'隔壁家的老黄'},{$set:{"name":'隔壁家老黄1'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
// multi 默认为false 只会修改文档中的一条数据 true则修改全部
>db.users.update({"age":23},{$set:{"age":24}},{multi:true})
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 3 })

>db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 24, "job" : "垃圾前端" }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 24, "job" : "save方法,更新" }

数据更新操作符

$currentDate
  • 用法:{$currentDate:{field:value}}
    • value:可以为true,{ $type: "timestamp" },{ $type: "date" }
  • 作用:对一个字段的值设置为当前日期,可设置为'timestamp' 或'date'格式。默认类型是'date'格式。
  • 示例:将name为隔壁家老黄1的lastTime设置为当前时间
> db.users.update({name:"隔壁家老黄1"},{$currentDate:{'lastTime':true}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 24, "job" : "垃圾前端", "ailas" : [ "菜鸡前端" ], "lastTime" : ISODate("2018-07-30T08:12:44.745Z") }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 24, "job" : "save方法,更新" }

> db.users.update({name:"隔壁家老黄1"},{$currentDate:{'lastTime':{$type:'timestamp'}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 24, "job" : "垃圾前端", "ailas" : [ "菜鸡前端" ], "lastTime" : Timestamp(1532938407, 1) }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 24, "job" : "save方法,更新" }

> db.users.update({name:"隔壁家老黄1"},{$currentDate:{'lastTime':{$type:'date'}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 24, "job" : "垃圾前端", "ailas" : [ "菜鸡前端" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z") }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 24, "job" : "save方法,更新" }
$inc
  • 用法:{$inc:{field:value}}
  • 作用:对一个数字字段的某个field增加value
  • 示例:将name为隔壁家老黄1的age增加5
> db.users.update({name:"隔壁家老黄1"},{$inc:{age:5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 29, "job" : "垃圾前端" }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 24, "job" : "save方法,更新" }
$min
  • 用法:{$min:{field:value}}
  • 作用:如果value小于字段的当前值,则$min将字段的值更新为value值。
  • 示例:判断name为隔壁家老黄3的age值,当前值为24大于23,则设置为23
> db.users.update({name:'隔壁家老黄3'},{$min:{age:23}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 29, "job" : "垃圾前端", "ailas" : [ "菜鸡前端" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z") }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 23, "job" : "save方法,更新" }
$max
  • 用法:{$max:{field:value}}
  • 作用:如果valu大于字段的当前值,则$max将字段的值更新为value值。
  • 示例:判断name为隔壁家老黄1的age值,当前值为29小于30,则设置为30
> db.users.update({name:'隔壁家老黄1'},{$max:{age:30}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "菜鸡前端" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z") }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23 }
$mul
  • 用法:{$max:{field:NumberInt(Number)}},{$max:{field:NumberDecimal()}},{$max:{field:Number}}
  • 作用:将字段的值乘以数字值,如果文档中不存在该字段,则创建该字段并将该值设置为与乘数相同的数字类型的零
  • 示例:判断name为隔壁家老黄1的age值,当前值为29小于30,则设置为30
> db.users.update({name:'隔壁家老黄1'},{$mul:{count:2}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "count" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23 }

> db.users.update({name:'隔壁家老黄2'},{$set:{count:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.update({name:'隔壁家老黄2'},{$mul:{count:NumberInt(5)}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端",  "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "count" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "count" : 5 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23 }

> db.users.update({name:'隔壁家老黄3'},{$set:{count:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.update({name:'隔壁家老黄3'},{$mul:{count:NumberDecimal(2.1)}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "count" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "count" : 5 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "count" : NumberDecimal("2.1000000000000000000000000000") }
$rename
  • 用法:{$rename:{field:value}}
  • 作用:把文档中某个字段名称修改为value
  • 示例:把所有的count修改为counts
> db.users.update({},{$rename:{count:'counts'}},{multi:true})
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 3 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "counts" : NumberDecimal("2.1000000000000000000000000000") }
$set
  • 用法:{$set:{field:value}}
  • 作用:把文档中某个字段field的值设为value
  • 示例: 把隔壁家老黄2的年龄设为23岁
> db.users.update({name:"隔壁家老黄2"},{$set:{age:23}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 29, "job" : "垃圾前端" }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 23, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "age" : 24, "job" : "save方法,更新" }
$unset
  • 用法:{$unset:{field:1}}
  • 作用:删除某个字段field
  • 示例: 将隔壁家老黄3的年龄字段删除
> db.users.update({name:"隔壁家老黄3"},{$unset:{age:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 29, "job" : "垃圾前端" }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 23, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新" }
$push
  • 用法:{$push:{field:value}}
  • 作用:把value追加到field里。注:field只能是数组类型,如果field不存在,会自动插入一个数组类型
  • 示例:给隔壁家老黄1添加别名"菜鸡前端"
> db.users.update({name:"隔壁家老黄1"},{$push:{"ailas":"菜鸡前端"}}) 
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 29, "job" : "垃圾前端", "ailas" : [ "菜鸡前端" ] }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 23, "job" : "还看得下去" }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新" }

$addToSet
  • 用法:{$addToSet:{field:value}}
  • 作用:加一个值到数组内,而且只有当这个值在数组中不存在时才增加。
  • 示例:往隔壁家老黄1的别名字段里添加两个别名A1、A2
> db.users.update({name:'隔壁家老黄1'},{$addToSet:{ailas:['A1','A2']}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "菜鸡前端", [ "A1", "A2" ] ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "counts" : NumberDecimal("2.1000000000000000000000000000") }
$pop
  • 用法:删除数组内第一个值:{$pop:{field:-1}}、删除数组内最后一个值:{$pop:{field:1}}
  • 作用:用于删除数组内的一个值
  • 示例:删除隔壁家老黄1记录中alias字段中最后一个别名
> db.users.update({name:'隔壁家老黄1'},{$pop:{ailas:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "菜鸡前端", [ "A1", "A2" ] ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "counts" : NumberDecimal("2.1000000000000000000000000000") }
$pull
  • 用法:{$pull:{field:_value}}
  • 作用:从数组field内删除一个等于_value的值
  • 示例:删除隔壁家老黄1记录中的别名A1
> db.users.update({name:"隔壁家老黄1"},{$pull:{ailas:'A1'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "A2", "菜鸡前端" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "counts" : NumberDecimal("2.1000000000000000000000000000") }
$each
  • 用法:{$push/$addToSet:{"field":{$each:[value1,value2....]}}}
  • 作用:给数组field内循环添加[value1,value2....]
  • 示例:给隔壁家老黄1 使用$push添加别名A2 A3 A4,$addToSet添加别名A2 A3 A4 A5
> db.users.update({name:"隔壁家老黄1"},{$push:{"ailas":{$each:['A2','A3','A4']}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "A2", "菜鸡前端", "A2", "A3", "A4" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "counts" : NumberDecimal("2.1000000000000000000000000000") }
> 

> db.users.update({name:"隔壁家老黄1"},{$addToSet:{"ailas":{$each:['A2','A3','A4','A5']}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "A2", "菜鸡前端", "A2", "A3", "A4", "A5" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }
{ "_id" : 2, "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23, "counts" : NumberDecimal("2.1000000000000000000000000000") }

push方法不会排除重复数据,addToSet会排除重复数据

删除文档(类数据)

MongoDB ==remove()== 函数是用来移除集合中的数据。

db.collection.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query :(可选)删除的文档的条件。
  • justOne : (可选)如果设为 true 或 1,则只删除一个文档。
  • writeConcern :(可选)抛出异常的级别。
//新增一条隔壁家老黄3的数据
> db.users.insert({ "name" : "隔壁家老黄3", "job" : "save方法,更新", "age" : 23})
WriteResult({ "nInserted" : 1 })

> db.users.remove({name:'隔壁家老黄3'})
WriteResult({ "nRemoved" : 2 })
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "A2", "菜鸡前端", "A2", "A3", "A4", "A5" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }

justOne设置为1,则只删除一条数据

> db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)

删除所有数据

> db.COLLECTION_NAME.remove({})

查询文档(类数据)

MongoDB 查询文档使用find() 方法的基本格式为:

  • find() 方法会以非结构化的方式来显示所有文档。
  • pretty() 会格式化方式显示结果
> db.users.find()
{ "_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"), "name" : "隔壁家老黄1", "age" : 30, "job" : "垃圾前端", "ailas" : [ "A2", "菜鸡前端", "A2", "A3", "A4", "A5" ], "lastTime" : ISODate("2018-07-30T08:14:19.944Z"), "counts" : 0 }
{ "_id" : 1, "name" : "隔壁家老黄2", "age" : 24, "job" : "还看得下去", "counts" : 2 }

> db.users.find().pretty()
{
	"_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"),
	"name" : "隔壁家老黄1",
	"age" : 30,
	"job" : "垃圾前端",
	"ailas" : [
		"A2",
		"菜鸡前端",
		"A2",
		"A3",
		"A4",
		"A5"
	],
	"lastTime" : ISODate("2018-07-30T08:14:19.944Z"),
	"counts" : 0
}
{
	"_id" : 1,
	"name" : "隔壁家老黄2",
	"age" : 24,
	"job" : "还看得下去",
	"counts" : 2
}

条件查询

操作 格式 范例
等于 {:} db.users.find({"name":"隔壁家老黄1"}).pretty()
小于 {:{$lt:}} db.users.find({"age":{$lt:20}}).pretty()
小于或等于 {:{$lte:}} db.users.find({"age":{$lte:20}}).pretty()
大于 {:{$gt:}} db.users.find({"age":{$gt:20}}).pretty()
大于或等于 {:{$gte:}} db.users.find({"age":{$gte:20}}).pretty()
不等于 {:{$ne:}} db.users.find({"age":{$ne:20}}).pretty()

MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,实现AND查询

> db.集合.find({key1:value1, key2:value2}).pretty()

> db.users.find({age:30,name:'隔壁家老黄1'}).pretty()
{
	"_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"),
	"name" : "隔壁家老黄1",
	"age" : 30,
	"job" : "垃圾前端",
	"ailas" : [
		"A2",
		"菜鸡前端",
		"A2",
		"A3",
		"A4",
		"A5"
	],
	"lastTime" : ISODate("2018-07-30T08:14:19.944Z"),
	"counts" : 0
}

MongoDB OR 条件语句使用了关键字 $or,查询满足其中一个的数据

> db.col.find({$or: [key1: value1}, {key2:value2}]}).pretty()

> db.users.find({$or:[{age:30},{name:'隔壁家老黄2'}]}).pretty()
{
	"_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"),
	"name" : "隔壁家老黄1",
	"age" : 30,
	"job" : "垃圾前端",
	"ailas" : [
		"A2",
		"菜鸡前端",
		"A2",
		"A3",
		"A4",
		"A5"
	],
	"lastTime" : ISODate("2018-07-30T08:14:19.944Z"),
	"counts" : 0
}
{
	"_id" : 1,
	"name" : "隔壁家老黄2",
	"age" : 24,
	"job" : "还看得下去",
	"counts" : 2
}

AND 和 OR 联合使用

> db.集合.find({"key1": {$gt:value1}, $or: [{"key2": "value2"},{"key3": "value3"}]}).pretty()

> db.users.find({"age": {$gt:24}, $or: [{"name": "隔壁家老黄1"},{"name": "隔壁家老黄2"}]}).pretty()
{
	"_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"),
	"name" : "隔壁家老黄1",
	"age" : 30,
	"job" : "垃圾前端",
	"ailas" : [
		"A2",
		"菜鸡前端",
		"A2",
		"A3",
		"A4",
		"A5"
	],
	"lastTime" : ISODate("2018-07-30T08:14:19.944Z"),
	"counts" : 0
}

limit() 方法:读取指定数量的数据记录

> db.COLLECTION_NAME.find().limit(NUMBER)

skip() 方法:跳过指定数量的数据

> db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

sort() 方法:可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

> db.COLLECTION_NAME.find().sort({KEY:1})

> db.users.find().sort({age:1}).pretty()
{
	"_id" : 1,
	"name" : "隔壁家老黄2",
	"age" : 24,
	"job" : "还看得下去",
	"counts" : 2
}
{
	"_id" : ObjectId("5b598fcfc6dd20ad1e89e0c8"),
	"name" : "隔壁家老黄1",
	"age" : 30,
	"job" : "垃圾前端",
	"ailas" : [
		"A2",
		"菜鸡前端",
		"A2",
		"A3",
		"A4",
		"A5"
	],
	"lastTime" : ISODate("2018-07-30T08:14:19.944Z"),
	"counts" : 0
}

express+socket.io=通讯服务器搭建(终)

前言

express+socket.io=通讯服务器搭建(一)系列一中我们已经可以从数据库中获取数据了,现在我们加上socket.io 实现实时通讯的功能。我的需求是node做中间层,java端调用node接口在使用socket.io 发送给客户端。

安装配置

实时通讯一方推送消息一方接收消息,所以我们需要实现一个接口提供后台访问推送消息。我们大概了解了socket.io api后继续往下走。

$ npm install socket.io --save

index.js内容如下:

var express = require('express');
var router = express.Router();

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);

io.on('connection', function (socket) {
  console.log('用户连接成功!');

  socket.on("disconnect", function () {
    console.log("用户退出");
  });

  socket.on("message", function (obj) {
    console.log('这个websocket信息是:' + obj.msg);
    io.emit("message", obj);
  });
});
//开启端口监听socket
server.listen(3001);

/* GET home page. */
router.get('/', function (req, res, next) {
  res.render('index', { title: '隔壁家老黄', message: '欢迎来到隔壁家老黄的首个接口服务器' });
});


router.post('/setMsg', function (req, res, next) {
  // 调用接口推送消息
  io.emit('message', req.body)
  res.json(req.body)
})

module.exports = router;

建立连接

客户端接收消息。

// 引入socket.io 
<script src="https://cdn.bootcss.com/socket.io/2.1.1/socket.io.dev.js"></script>

//建立连接
socket = io.connect('ws://localhost:3001');

//接收服务端推送的信息
socket.on("message", function (obj) {
   console.log(obj)
});

消息推送

axios.post('http://172.16.2.20:3000/index/setMsg', {
     msg: 'java端向node端推送消息',
    })
    .then(function (response) {
        console.log(response);
    }).catch(res => {
        console.log(ReadableStreamReader)
})

简单的websocket连接服务就完成了,java端推送消息给node端,node在进行处理。

总结

实时通讯代码其实都差不多,最重要的是一个思路,如果是简单聊天室那么消息推送和接收都是在客户端完成。源码地址github

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.