hadoop HDFS(二)

hdfs的shell命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
hadoop fs # 帮助命令
hadoop fs -df -h /
hadoop fs -du -s -h hdfs://cluster1:9000/*
hadoop fs -rm -f -R hdfs://cluster1:9000/*
# 上传文件,保存在/root/hadoop-2.8.1/tmp/dfs
hadoop fs -put install_hadoop.sh hdfs://cluster1:9000/
# 下载 只是权限变了
hadoop fs -get hdfs://cluster1:9000/install_hadoop.sh ./
# 创建目录
hadoop fs -mkdir -p /wordcount/input # 同下
hadoop fs -mkdir -p hdfs://cluster1:9000/wordcount/input
# 浏览器查看文件
http://192.168.1.222:50070
# 通过上面的浏览器--Utilities--Browse the file system就可以看到上传的文件
# 查看目录
hadoop fs -ls /wordcount/output
# 查看文件内容
hadoop fs -cat /wordcount/output/part-r-00000
HDFS实现机制

/root/hadoop-2.8.1/tmp/dfs/data/current/BP-XX/current/finalized:保存datanode主机的 block块的地方

java客户端调用HDFS API

安装eclipse,配置所需要的jar包和配置文件
  1. /root/hadoop-2.8.1/share/hadoop/common/下jar包,以及common jar中的依赖包common/bin 的所有jar
  2. /root/hadoop-2.8.1/share/hadoop/hdfs/下jar包,以及hdfs jar中的依赖包hdfs/bin 的所有jar,以及yarn,mapreduce目录下的
  3. 将/root/hadoop-2.8.1/etc下的 core-site.xml和hdfs-site.xml文件放在Project的src目录下

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    package cn.itcast.hadoop.hdfs;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import org.apache.commons.compress.utils.IOUtils;
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.FSDataInputStream;
    import org.apache.hadoop.fs.FSDataOutputStream;
    import org.apache.hadoop.fs.FileStatus;
    import org.apache.hadoop.fs.FileSystem;
    import org.apache.hadoop.fs.LocatedFileStatus;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.fs.RemoteIterator;
    import org.junit.Before;
    import org.junit.Test;
    public class HdfsUtil {
    FileSystem fs = null;
    @Before
    public void init() throws IOException, InterruptedException, URISyntaxException {
    Configuration conf = new Configuration();
    conf.set("fs.defaultFS", "hdfs://cluster1:9000/");
    // 设置权限
    fs = FileSystem.get(new URI("hdfs://cluster1:9000/"),conf,
    "root");
    //如果在集群中只需要指定dfs.nameservices的值即可:hdfs://ns1/
    //fs需要哪些成员才能读写hdfs文件
    // fs-->RPCProxy-->NameNode.open(src)
    }
    @Test
    public void download() throws IOException {
    // input stream
    Path src =new Path("hdfs://cluster1:9000/install_hadoop.sh");
    FSDataInputStream in =fs.open(src);
    //ouput local
    FileOutputStream os=
    new FileOutputStream("/root/Downloads/install2.sh");
    IOUtils.copy(in, os);
    }
    @Test
    public void download2() throws IOException {
    fs.copyFromLocalFile(
    new Path("hdfs://cluster1:9000/upload2.txt"),
    new Path("/root/Downloads/install2.sh")
    );
    }
    @Test
    public void upload() throws IOException {
    // to upload a file to hdfs
    Path dst =new Path("hdfs://cluster1:9000/upload.txt");
    FSDataOutputStream os =fs.create(dst);
    FileInputStream in=
    new FileInputStream("/root/Downloads/install2.sh");
    IOUtils.copy(in, os);
    }
    @Test
    public void upload2() throws IOException {
    fs.copyFromLocalFile(
    new Path("/root/Downloads/install2.sh"),
    new Path("hdfs://cluster1:9000/a/b/upload2.txt"));
    }
    @Test
    public void mkdir() throws IllegalArgumentException, IOException {
    fs.mkdirs(new Path("/a/b"));
    }
    @Test
    public void rm() throws IllegalArgumentException, IOException {
    fs.delete(new Path("/a"),true);
    }
    @Test
    public void listFiles() throws FileNotFoundException, IllegalArgumentException, IOException {
    RemoteIterator<LocatedFileStatus> files = fs.listFiles(new Path("/"), true);
    while(files.hasNext()) {
    LocatedFileStatus file = files.next();
    //LocatedFileStatus 是通过RPC机制 得到的 fs也是类似
    Path filepath = file.getPath();
    String fileName = filepath.getName();
    System.out.println(fileName);
    }
    System.out.println("--------------");
    FileStatus[] listStatus = fs.listStatus(new Path("/"));
    for(FileStatus status: listStatus) {
    String name =status.getPath().getName();
    System.out.println(name);
    }
    }
    }

权限问题

如果不是在虚拟机上测试的话,会有权限的问题,需要在eclipse的Run Configuration的Arguments中的VM arguments中增加如下信息
-DHADOOP_USER_NAME=root

Share Comments

hadoop简介与安装(一)

介绍hadoop

在海量数据的场景下,为了解决分布式部署中的公共问题一些框架就出现了hadoop,zookeeper
hadoop:由很多的技术框架组成的一个生态系统.它是一个上层的应用软件(java编写的)
不关是用来做海量数据储存的,因为在解决海量数据的处理中,解决了一些分布式很共同的问题,把解决这些问题的方法抽离出来,形成各种各样的框架.将这些框架单独拿出来也可以在项目中使用.

hadoop三个主要框架:

海量数据的存储(HDFS):分布式集群的文件系统,区别本机的文件系统

海量数据的分析(运算模型)(MapReduce):分析运算的模型,自己写运算逻辑(程序),写出来的就是MapReduce程序,通过YARM分配到节点之后运行MapReduce程序

Map程序:在不同节点并发运行
Reduce程序:全局处理,只在一同节点上运行,通过网络取得Map程序的结果.在分组统计时,Reduce也可以有多个

MapReduce:擅长海量离线日志分析,可由hive工具编写
storm:实时的流计算
spark:实时的迭代运算

资源管理调度(YARN):集群

安装hadoop:

集群的安装是很繁琐的事,Cloudera这个公司开发的安装系统(脚本)之后,只需要在一个节点,在浏览器中打开Cloudera,然后选择你所需要的服务,点确定之后,所以的程序包自动下载并安装好,同时还提供对集群的管理监控

但个人还是使用apache官方的hadoop

环境为: centos7 + hadoop.2.8.1 + virtualbox5 + jdk8

前提:

  1. virtualbox5网络配置,联网成功
  2. vim /etc/sysconfig/network-scripts/ifcfg-enp0s3 设置IP为192.168.1.222,并重启network
  3. 在/root目录下执行如下脚本,并且请在此目录提前下载好hadoop-2.8.1.tar.gz
1
curl https://file.femnyy.com/file/install_hadoop.sh | sudo sh
注意:之后的所有操作也是root用户操作

访问:http://192.168.1.222:50070

巨坑:所有的服务都成功开启,但就是在主机访问不了,关闭了防火墙也不行,弄了半天的virtualbox的网络设置(自认为是没有错的)也不行,没想到居然是配置文件的问题
配置文件的巨坑

centos 7启动图形界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 方法一
yum group list
yum group install "GNOME Desktop"
startx
# 当重新启动时执行,就可以进入系统了
# You might have to hit 1, then 2 to agree to the license, then C to continue.
1--2--c--yes
# yum group install "GNOME Desktop" "Graphical Administration Tools"
# 开机时自动就是设置图形界面
# ln -sf /lib/systemd/system/runlevel5.target /etc/systemd/system/default.target
# reboot
# 方法二
init 5
Share Comments

网络基础与协议

网络就是一种辅助双方或者多方能够连接在一起的工具
网络的目的就是为了联通多方然后进行通信用的,即把数据从一方传递给另外一方

而所谓的网络编程就是,让在不同的电脑上的软件能够进行数据传递,即进程之间的通信(网络socket)

不同计算机之间都约定遵守的网络通信协议叫做TCP/IP协议族
因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议
常用的电脑之间的数据传输协议 四层模型以及七层模型(OSI)

网络基础术语

ip(Internet Protocol)地址:⽤来在⽹络中标记⼀台电脑的⼀串数字(ipv4:32位/ipv6:64位)
ip地址分类
注意:如下ip网段属于私⽹IP,不在公⽹中使⽤的,它们的范围是:

1
2
3
4
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
127.0.0.1~127.255.255.255 # ⽤于回路测

⼦⽹掩码(netmask):确定了一个IP地址的32位二进制数字中哪些是网络号,哪些是主机号

端口(Port):区分不同的服务
因为IP地址与网络服务(HTTP(万维网服务),FTP(文件传输),SMTP(电子邮件)等)的关系是一对多的关系.
实际上是通过”IP地址+端口号”来区分不同的服务的
端口区别不同电脑的进程,进程号是区别同一台电脑中的进程

arp协议:通过ip得到mac地址

mac地址(物理地址):产商序列(前3个字节)和网卡序列(后3个字节)组成电脑的唯一标识

交换机(还有路由功能)与集线器(hub:向所有的电脑进行广播):同一网段的直接通信
交换机的作⽤:

  1. 转发过滤:当⼀个数据帧的⽬的地址在MAC地址表中有映射时,它被转发到连接⽬的节点的端⼝⽽不是所有端⼝(如该数据帧为⼴播帧则转发 ⾄所有端⼝)
  2. 学习功能:以太⽹交换机了解每⼀端⼝相连设备的MAC地址,并将地址 同相应的端⼝映射起来存放在交换机缓存中的MAC地址表中,但到了一定时间会自动清空
    如果PC不知⽬标IP所对应的的MAC,那么可以看出,pc会先发送arp⼴ 播,得到对⽅的MAC,然后在进⾏数据的传送
    当switch第⼀次收到arp⼴播数据,会把arp⼴播数据包转发给所有端⼝ (除来源端⼝);如果以后还有pc询问此IP的MAC,那么只是向⽬标的 端⼝进⾏转发数据

rarp协议:根据mac地址找IP

ping 192.168.1.1–>走ICMP协议

网关(Gateway):发送的ip不在同一个网段内,那么会把这个数据发送给默认网关
因为跨网之间不能直接通信这是TCP/UDP协议规定的

路由(就是一个网关设备):连接不同网段间的通信,至少两个网卡(同时网卡设置时,必须是不同网段)

RIP(路由协议):多个路由器之间的通信

TTL:指一个数据包在网络上经过路由器数量的最大值(也就是说一个数据包经过了多少个路由器,一般最大值是128)

MSL:一个数据包在网络中存储的最长时间(保证数据包的存活时间,一般是1-2分钟)

NAT(网络地址转换器):私有IP不能直接上网,必须通过路由器的转换成公有IP(也是国内的路由器所具有的功能)

TCP协议

tcp(传输控制协议)在通信开始之前,⼀定要先建⽴相关的链接,才能发送数据,注重数据传输稳定(一定能接收到)
tcp协议与Socket通信过程

可以通过packet tracer 来模拟tcp协议的三次握手与四次挥手过程
tcp协议的三次握手与四次挥手

tcp的十种状态和2MSL问题

即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,
必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个ACK包对方没收到,
那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包

当是服务先close时,为了避免2MSL的等待状态出现,所导致的端口被占用的问题

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

TCP通信过程(网络是双向的,所以在手机配置路由协议的时候,返回的路由协议也要配置好)

传递数据包中目标IP始终不变,而MAC在两两设备之间都在变

UDP广播

udp(用户数据包协议)通信模型中,在通信开始之前,不需要建⽴相关的链接,只需要发送数据即可,注重速度流畅
单播(点对点)
多播(一对多)
广播(一对所有,只向交换机发一份数据,然后交换机进行广播发送)

tcp协议与udp协议通信的区别

tcp在三次握手过程中的数据格式有syn,syn+ack和ack

TCP长/短连接

  1. ⻓连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间.对于频繁请求资源的客户来说,较适用⻓连接
  2. 短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段,如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽

处理应用层的HTTP协议(无状态,无连接)

应用层的协议:根据应用场景不一样,从而对数据进行不同格式的封装(将应用层的数据能拿到传输层中去send()或rece()

服务器与客户端共同遵守才使得Server-client之间看得懂而进行交流),比如ftp(传输文件),ssh(远程登陆),http(网页),smtp(邮件)

所以应用层要解决的问题是:传递什么数据(封装数据)

浏览器访问网页的过程

tcp三次握手之后,使用http协议传输数据报文给下面三层(理解为一个整体快递公司,传输数据方式(快递公司)–>IP(地理位置坐标)–>具体的传输工具)

socket是操作系统提供的tcp/udp传输协议的工具

1
2
3
4
5
6
7
8
9
物理层:电器连接
数据链路层:交换机,STP,帧中继
网络层:路由器,IP 协议
传输层:TCP、UDP 协议(socket)
# http包通过socket方式打包成TCP包
会话层:建立通信连接,网络拨号
表示层:每次连接只处理一个请求
# https(http+ssl):通过对应用层的加密
应用层:HTTP、FTP,SSH,SMTP

HTML超文本文件,HTTP:超文本传输协议

http协议的数据包,传输的是html格式文件,而不是字符串格式

http客户端(浏览器,app,爬虫)与服务器如何收发数据:

浏览器和服务器都会自动生成socket, 响应头/请求头Content-Length:128响应体/请求体

浏览器引擎:就是解析HTML格式(也可以说是对字符串的解析)成网页的算法,同时也有JS的解析器
JS解析器已经脱离了前端发展成Node.js这各后台的服务器了

服务器的开发实际上就是在服务端的rece()和send()之间数据处理开发

http协议是无状态的,一次请求之后就会关闭 http1.0短连接,即使是长连接也是无状态

默认情况下所在HTTP1.1中所有连接都被保持,除非在请求头或响应头中指明要关闭:Connection: Close

Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接

URI:全球资源标识

通过位置来标识:URL:全球资源定位符(Location:表示位于服务器的那个位置)?k=value&(包含在请求头的Query String)
通过名字来标识资源:URN(N:Name)

Share Comments

VirtualBox下虚拟机的网络配置

基本思路

  1. 从主机可以通过静态IP访问到每一个虚拟机,从虚拟机中可以访问主机(主机也有一个固定的静态IP)(Host-only Adapter 模式)
    注意:这里设置是主机的IPV4,不是虚拟机的
  2. 虚拟机之间组成一个由静态IP构成的网络,而且虚拟机之间可以互相访问 (Internal Network 模式)
    注意,虚拟机之间的内网名字可以随便起,这里叫做 intnet.如果在配置在第二个虚拟机的时候,也要记得选择 intnet 这个内网名字,这样虚拟机之间才可以互相通信
  3. 从主机,从虚拟机都可以访问internet (NET 模式)

具体操作

在Virtualbox中设置全局变量的 Host-only Networks

1
打开Virtualbox--任务栏--File--Preferences--Network--Host-only Networks

Virtualbox的host-only设置

配置虚拟机的网卡

1
选择一个虚拟机--Settings--Network-->设置三个网卡

Virtualbox三个网卡的设置

保存,启动虚拟机,安装系统
进入虚拟机内部配置网卡

修改静态IP,与主机同一个网段,实现主机与虚拟机的通信

1
2
3
4
5
6
vi /etc/sysconfig/network-scripts/ifcfg-enp0s3
BOOTPROTO=static
ONBOOT=yes
IPADDR=192.168.1.220
PREFIX=24
BROADCAST=192.168.1.255

修改静态IP,与虚拟机同一个网段,实现虚拟机之间的通信
注意:虚拟机内网我们选择 192.168.0. 这个网段

1
2
3
4
5
vi /etc/sysconfig/network-scripts/ifcfg-enp0s8
BOOTPROTO=static
ONBOOT=yes
IPADDR=192.168.0.2
PREFIX=24

实现连接internet

1
2
vi /etc/sysconfig/network-scripts/ifcfg-enp0s9
ONBOOT=yes

重新启动网络

1
service network restart

或者使用如下脚本来配置这三个网卡的信息,如果有错误请重新启动虚拟机就可以了

1
curl https://file.femnyy.com/file/centos7_net.sh |sudo sh

注意:此脚本的host-only为192.168.1.221网段,internal为192.168.0.221网段

关机保存上面的配置好的虚拟机
clone虚拟机,然后进入clone后的虚拟机,修改如下配置及可

1
2
3
4
5
6
7
# 设置一个没有使用的IP
vi /etc/sysconfig/network-scripts/ifcfg-enp0s3
IPADDR=192.168.1.220
# 设置一个新的内网IP
vi /etc/sysconfig/network-scripts/ifcfg-enp0s8
IPADDR=192.168.0.2
# 因为internet的网络设置是DHCP,所以不用配置
Share Comments

内存信息的生成与交换

由C编译的程序占用内存情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//main.cpp
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\\0在常量区,p3在栈上.
static int c =0;//全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);//分配得来得10和20字节的区域就在堆区.
strcpy(p1, "123456"); //123456\\0放在常量区,
// 编译器可能会将它与p3所指向的"123456"优化成一个地方.
char *s = "hello, world";
printf("%s\n",&s[7]);//world
}

如图

不能进行交换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
/*
*void swap(int a, int b);
*/
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
int main(void)
{
/* main函数会分配一个内存空间 */
int x = 1;
int y = 2;
/* main函数中会为x,y分配两个紧邻的内存空间
* */
printf("x is %i\n", x);
printf("y is %i\n", y);
printf("Swapping...\n");
printf("Swapped.\n");
swap(x, y);
/* swap函数也会分配独立的内存空间,并且是在main内存空间之上
*/
printf("x is %i\n", x);
printf("y is %i\n", y);
}

成功交换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
void swap(int *a, int *b);
int main(void)
{
int x = 1;
int y = 2;
printf("x is %i\n", x);
printf("y is %i\n", y);
printf("Swapping...\n");
swap(&x, &y);
/* &表示内存地址,提供的是内存地址值
* 这个传递的不是x,y本身,而是x,y的int型地址
* 使一个函数能够使用另一个函数的内存块 */
printf("Swapped!\n");
printf("x is %i\n", x);
printf("y is %i\n", y);
}
/* */
/* */
void swap(int *a, int *b)
/* 当有函数调用swap函数时,函数原型使用*号之后,
* a不再是一个整形数,而是一个int型指针,指的是内存地址本身的值*/
{
/* 但是在函数中,*意思是定位到这个内存地址指向的值
* swap函数起了作用 来修改本来不属于它的内存,
* 不在它的作用域的变量 */
int tmp = *a;
*a = *b;
*b = tmp;
/*但 = 左边的 表示内存地址的值 */
}
/*当这个函数调用完成之后swap内存空间将会被释放 */

如图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(void)
{
printf("s: ");
/* 指针是操作内存地址的方法
stack栈 heap堆:为函数分配的内存地址(指针)
*/
string *s = get_string();
string *t = s;/*t为指针,s为具体的值*/
int *x;
int *y;
x = malloc(sizeof(int));/*内存分配:
表示向系统申请存在空间的字节数
已经有预定指向内存地址了*/
/* x = 52;会重新分配新内存,而上面的内存也就浪费掉了*/
*x = 52;
/* *y = 12; 这个没有事先分配的内存地址*/
y = x;
*y = 12;
}
Share Comments

归并排序

数据结构和算法动态可视化工具:VisuAlgo

递归:
函数的自我调用(重复做同一件事,也可以用迭代语句),适用于如果你可以写一个函数,并让它调用自己,只是每次减少参数值,这时就可以用递归思想
但不断的递归可能会造成内存耗尽,所以合法的检查(基本条件)是正确解决整个问题的关键所在.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def sigma(int):
if int <= 0:return 0
sum = 0
for i in range(int+1):
sum+=i
print(sum)
sigma(100)
# 递归
def sigma1(int):
if int <=0:return 0 # 终止条件
else:
return int+sigma1(int-1)
print(sigma1(100))

归并排序:
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为二路归并

归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]

归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def merge(left, right):
i, j = 0, 0
result = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
print(result)
return result
def merge_sort(lists):
if len(lists) <= 1:
return lists
num = int((len(lists) / 2)+0.5)
# 二分查找 logN 拆分成单个元素
left = merge_sort(lists[:num])
right = merge_sort(lists[num:])
print(left,right,sep='下面')
return merge(left, right) # 合并之后就是有序的了
print(merge_sort([1,6,4,2,9,7,5,8,3]))
# 归并排序 (合并过程的次数也是N)N*logN
# T(n) = 0,if n < 2 #合法的检查 基本条件
# T(n) = T(n/2) +T(n/2)+n ,if n >1
# 算法的核心:拆分 合并
# T(4) = 2 * T(2) + 4
# T(2) = 2 * T(1) + 2
# T(1) = 0
# 8个元素排序需要8×log8 = 24

递归和合并过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1
merge( merge_sort([1,6,4,2,7]),merge_sort([7,5,8,3])
)
# 2
merge( merge( merge_sort([1,6,4]),merge_sort([2,7])),
merge( merge_sort([7,5]),merge_sort([8,3]))
)
# 3
merge( merge(
merge( merge_sort([1,6]),merge_sort(4)),
merge( merge_sort([2]),merge_sort([7]))
),
merge(
merge( merge_sort([7]),merge_sort([5])),
merge( merge_sort(8),merge_sort(3))
)
)
# 4
merge( merge(
merge(
merge( merge_sort([1]),merge_sort([6])),
merge_sort(4)
),
merge( merge_sort([2]), merge_sort([7]))
),
merge(
merge( merge_sort([7]), merge_sort([5])),
merge( merge_sort(8), merge_sort(3))
)
)

如图:
归并排序

Share Comments

冒泡和简单选择排序算法

冒泡排序(Bubble Sort又称为泡沫排序或气泡排序):
是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字由来是因为越小的元素会经由交换慢慢”浮”到数列的顶端,而最大数在第一次的外循环就固定了
实例分析

以列表[5, 1, 4, 2, 8] 为例说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 第一次外循环
( 5 1 4 2 8 ) → ( 1 5 4 2 8 ), 5 > 1 交换位置
( 1 5 4 2 8 ) → ( 1 4 5 2 8 ), 5 > 4 交换位置
( 1 4 5 2 8 ) → ( 1 4 2 5 8 ), 5 > 2 交换位置
( 1 4 2 5 8 ) → ( 1 4 2 5 8 ), 5 < 8 位置不变
# 第二次外循环(除开最后一个元素8,对剩余的序列)
( 1 4 2 5 8 ) → ( 1 4 2 5 8 ), 1 < 4 位置不变
( 1 4 2 5 8 ) → ( 1 2 4 5 8 ), 4 > 2 交换位置
( 1 2 4 5 8 ) → ( 1 2 4 5 8 ), 4 < 5 位置不变
# 第三次外循环(除开已经排序好的最后两个元素,
# 可以注意到上面的数组其实已经排序完成,但是程序本身并不知道
# 所以还要进行后续的循环,直到剩余的序列为 1)
( 1 2 4 5 8 ) → ( 1 2 4 5 8 )
( 1 2 4 5 8 ) → ( 1 2 4 5 8 )
# 第四次外循环(最后一次)
( 1 2 4 5 8 ) → ( 1 2 4 5 8 )

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def bubble_sort(lists):
# 冒泡排序
count = len(lists)
n = 0
for i in range(0, count):
for j in range(i + 1, count):
if lists[i] > lists[j]:
lists[i], lists[j] = lists[j], lists[i]
n+=1
print(n)
return lists
print(bubble_sort([5,1,4,2,8]))
print(bubble_sort([8,5,4,2,1]))
# 输出如下
# 4
# [1, 2, 4, 5, 8]
# 10
# [1, 2, 4, 5, 8]

简单选择排序算法:
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止.

以列表[5, 1, 4, 2, 8] 为例说明
第一趟: 1,5,4,3,8
第二趟: 1,3,4,5,8
第三趟: 1,3,4,5,8
第四趟: 1,3,4,5,8
第五趟: 1,3,4,5,8

代码实现:

1
2
3
4
5
6
7
8
9
10
`def select_sort(lists):
count = len(lists)
for i in range(0, count):
min = i
for j in range(i + 1, count):
if lists[min] > lists[j]:
min = j
lists[min], lists[i] = lists[i], lists[min]
return lists
print(select_sort([5, 1, 4, 2, 8]))

Share Comments

shell_sed_awk

Sed全名为Stream EDitor,即流式编辑器,行编辑器

sed常用用法

1
2
sed [options] 'script(模式命令)' input_file...
sed [options] -f script_file(sed脚本) input_file...

options

-n 静默模式,不打印模式空间中的内容

-e 执行模式命令,适用于多脚本处理

-i 直接操作原文件

1
2
3
4
5
6
7
sed -n -e '1p' -e '37,40p' /etc/passwd > passwd.txt && cat passwd.txt
# 输出如下
root:x:0:0:root:/root:/bin/bash
rtkit:x:118:126:RealtimeKit,,,:/proc:/bin/false
saned:x:119:127::/var/lib/saned:/bin/false
usbmux:x:120:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false
femn:x:1000:1000:femn,,,:/home/femn:/bin/bash

模式命令 s:替换 p:打印 d:删除 a:增加 g:表示一行上的替换所有的匹配

1
2
3
4
5
6
sed -e '1,37d' /etc/passwd
# 输出如下
saned:x:119:127::/var/lib/saned:/bin/false
usbmux:x:120:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false
femn:x:1000:1000:femn,,,:/home/femn:/bin/bash
mysql:x:121:130:MySQL Server,,,:/nonexistent:/bin/false

a \:在模式匹配到的行后面添加新内容
i 前面

1
2
3
4
5
6
7
8
9
10
sed -e '/root/i \this is head' \
-e '/femn/a \this is tail\n' passwd.txt
# 输出如下
this is head
root:x:0:0:root:/root:/bin/bash
rtkit:x:118:126:RealtimeKit,,,:/proc:/bin/false
saned:x:119:127::/var/lib/saned:/bin/false
usbmux:x:120:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false
femn:x:1000:1000:femn,,,:/home/femn:/bin/bash
this is tail

匹配内容的行’/{content}/‘ 可以使用正则匹配

1
2
3
4
sed -n -e '/\<root\>/s/bin/nologin/p' -e 's/femn/hehe/p' passwd.txt
# 输出如下
root:x:0:0:root:/root:/nologin/bash
hehe:x:1000:1000:femn,,,:/home/femn:/bin/bash

-f 指定文件

1
2
3
4
vim ss
/\<root\>/s/bin/nologin/p
s/femn/hehe/p
sed -n -f ss passwd.txt

sed脚本

1
2
3
4
5
6
vim ss.sh
#!/bin/sed -f
/\<root\>/s/bin/nologin/p
s/femn/hehe/p
chmod a+u ss.sh
./ss.sh passwd.txt

删除注释和空行 显示删除之后的行数

1
2
sed -e '/^#/d' -e '/^$/d' passwd.txt | wc -l
wc -l !$ # 上个文件多少行

个人的一个实用脚本,将中文符号换成英文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
for i in `ls /home/femn/blog/source/_posts/*.md`
do
sed -i -e 's/:/:/g' \
-e 's/;/;/g' \
-e 's/,/,/g' \
-e 's/././g' \
-e 's/(/(/g' \
-e 's/)/)/g' \
-e 's/"/"/g' \
-e 's/"/"/g' \
-e "s/'/'/g" \
-e "s/'/'/g" \
"$i"
done

awk擅于处理列

awk的一些内建变量

FS 输入字段分隔符 默认是空格或Tab

$0 当前记录(这个变量中存放着整个行 的内容)

$1~$n 当前记录的第n个字段,字段间由FS分隔

OFS 输出字段分隔符, 默认也是空格

NR 已经读出的记录数,就是行号

FNR 文件自己的行号

1
2
3
4
5
6
7
8
user_redis=`cat /etc/passwd|grep redis|awk -F : '{print $1}'`
awk 'BEGIN{FS=":"} {print $1,$3,$6}' < passwd.txt
# 如下输出
root 0 /root
rtkit 118 /proc
saned 119 /var/lib/saned
usbmux 120 /var/lib/usbmux
femn 1000 /home/femn

匹配内容

1
2
3
4
5
6
awk -F: '$1=="root" || $1 ~ /femn|mysql/ {print NR, FNR, $1,$3,$6}' OFS="\t" passwd.txt
# 如下输出
1 1 root 0 /root
5 5 femn 1000 /home/femn
# -F: 指定分隔符为':'去分隔文件 默认为空格或Tab
# ~ 表示匹配模式开始./ /中是模式.这就是一个正则表达式的匹配.!~ 表示取反

把指定的列输出到文件

1
2
3
4
5
6
7
8
9
10
11
awk 'NR!=1{if($1 ~ /root|femn/) print > "1.txt"; \
else if($1 ~ /mysql/) print > "2.txt"; \
else print > "3.txt" }' passwd.txt
vim 1.txt
emn:x:1000:1000:femn,,,:/home/femn:/bin/bash
# NR!=1表示不处理表头,所以没有root的那一行
vim 2.txt # 是个空的
vim 3.txt
rtkit:x:118:126:RealtimeKit,,,:/proc:/bin/false
saned:x:119:127::/var/lib/saned:/bin/false
usbmux:x:120:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false

注意其中的if-else-if语句,可见awk其实是个脚本解释器

统计

1
2
3
4
5
6
7
8
9
10
ll *.txt | awk '{print $5,$9}'
# 输出如下
46 1.txt
0 2.txt
151 3.txt
229 passwd.txt
ll *.txt |awk '{sum+=$5} END {print sum}'
# 输出如下
426

统计每个用户的进程的占了多少内存,sum的RSS那一列

1
ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'

按连接数查看客户端IP

1
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr

awk脚本

1
2
3
4
5
6
7
8
9
awk -F: 'BEGIN {i=1} { if ($7=="/bin/bash") i+=1 } END {print "number "i}' < /etc/passwd
# 输出如下
number 3
vim aa
BEGIN {i=1}
{ if ($7=="/bin/bash") i+=1 }
END {print "number "i}
cat /etc/passwd | awk -F: -f aa

Share Comments

DRF的JWT认证

几种常用的认证机制

HTTP Basic Auth

HTTP Basic Auth 在HTTP中,基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式,通常用户名和明码会通过HTTP头传递。

在发送之前是以用户名追加一个冒号然后串接上口令,并将得出的结果字符串再用Base64算法编码。例如,提供的用户名是Aladdin、口令是open sesame,则拼接后的结果就是Aladdin:open sesame,然后再将其用Base64编码,得到QWxhZGRpbjpvcGVuIHNlc2FtZQ==。最终将Base64编码的字符串发送出去,由接收者解码得到一个由冒号分隔的用户名和口令的字符串

优点 基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。

缺点 由于用户名和密码都是Base64编码的,而Base64编码是可逆的,所以用户名和密码可以认为是明文。
所以只有在客户端和服务器主机之间的连接是安全可信的前提下才可以使用。

OAuth

为第三方的认证所设计,但是更难配置。至少在服务器端更难,

名词定义:
Third-party application: 第三方应用程序,又称”客户端”(client)
HTTP service:HTTP服务提供商
Resource Owner:资源所有者,通常称”用户”(user)。
User Agent:用户代理,比如浏览器。
Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。
                它与认证服务器,可以是同一台服务器,也可以是不同的服务器
OAuth流程图,有点像是再次TCP握手的意思

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源

优点 快速开发 实施代码量小 维护工作减少 如果设计的API要被不同的App使用
    并且每个App使用的方式也不一样,使用OAuth2是个不错的选择。

缺点: OAuth2是一个安全框架,描述了在各种不同场景下,多个应用之间的授权问题。
    有海量的资料需要学习,要完全理解需要花费大量时间。 
    OAuth2不是一个严格的标准协议,因此在实施过程中更容易出错

Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象,主要是为了解决HTTP这一无状态协议下服务器如何识别用户的问题;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效

TOKEN

客户端通过一些可靠信息和服务器交换取token,这个token作为客服端再次请求的权限钥匙,当然token也是存在有效时间控制的。 Token通常比密码更加长而且复杂。那么一旦获得了token,在每次调用API的时候都要附加上它。这仍然比直接发送账户和密码更加安全,哪怕是 HTTPS。 把token想象成一个安全的护照。你在一个安全的前台验证你的身份(通过你的用户名和密码),如果你成功验证了自己,你就可以取得这个。当你走进大楼的时候(试图从调用API获取资源),你会被要求验证你的护照,而不是在前台重新验证

优点:
支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,
        前提是传输的用户认证信息通过HTTP头传输.

无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,
        因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.

更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等)
        而你的服务端只要提供API即可.

去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,
        只要在你的API被调用的时候,你可以进行Token生成调用即可.

更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,
        Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。

CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。

性能: 一次网络往返时间(通过数据库查询session信息)
        总比做一次HMACSHA256计算 的Token验证和解析要费时得多.

不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.

基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 
        这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)

JWT(JSON Web Token)

JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案.给客户端的是公钥,然后用公钥把数据加密发送给服务端,服务端在根据来源的信息,使用对应的私钥来解析数据。这样就能保证数据的安全性

编码之后的JWT看起来是这样的一串字符

1
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo4LCJ1c2VybmFtZSI6ImZlbW4iLCJleHAiOjE1NDc5NjgyOTAsImVtYWlsIjpudWxsfQ.3TKg7HEHWipAjq7nSY2jMbnIdEvWoFGOWjzjUdP__XI

由 . 分为三段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hearder: 可以对称解密
{
"alg": "HS256",# 声明类型,这里是jwt
"typ": "JWT" # 声明加密的算法 通常直接使用 HMAC SHA256
}
Payload:载荷(存放有效信息的地方) 可以对称解密
{
"nickname": "goodspeed",
"username": "goodspeed",
}
Signature:签名
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
SECREATE_KEY
)
client 使用 JWT 与server 交互过程

server配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
pip install djangorestframework-jwt
# app/settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
# app/urls.py
from rest_framework_jwt.views import obtain_jwt_token
#...
urlpatterns = [
'',
# ...
url(r'^api-token-auth/', obtain_jwt_token),
# 注意不是下面这个TOKEN啊
# url(r'^api-token-auth/', views.obtain_auth_token),
# 获取TokenAuthentication的 TOKEN API 需要INSTALL_APP:'rest_framework.authtoken'
]
#查看JWT
curl -X POST -d "username=femn&password=asdf1234" http:localhost:8000/api-token-auth/
# 或者直接访问 http:localhost:8000/api-token-auth/ 输入对应的用户名和密码
# post请求验证
curl -H "Authorization: JWT token" http://127.0.0.1:8000/api
# 代码实现:手动创建令牌
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'token':token})

前端手动获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!--使用post方法获取token并存入html的localStorage中-->
<script type="text/javascript">
function post_test() {
$.post("http://10.127.48.204:8000/api-token-auth/",{
'username':'earthchen',
'password':'xxxxxxxx'
},
function(result){
if(result){
localStorage.token=result.token; 存入数据
}
})
}
</script>
<!--在请求数据时需要在头部添加token-->
<script type="text/javascript">
function test(){
$.ajax({
headers:{
'Authorization':'JWT '+localStorage.token //注意:jwt后面有个空格
},
type:"get",
url:"http://10.127.48.204:8000/snippets/1/",
success:function(result){
document.write(result.style);
}
})
}
<script>

或者使用PyJWT实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import jwt
import time
def create_token(request):
grant_type = request.json.get('grant_type')
username = request.json['username']
password = request.json['password']
if grant_type == 'password':
account = verify_password(username, password)
elif grant_type == 'wxapp':
account = verify_wxapp(username, password)
if not account:
return {}
payload = {
"iss": "gusibi.com",
"iat": int(time.time()),
"exp": int(time.time()) + 86400 * 7,
"aud": "www.xx.com",
"sub": account['_id'],
"username": account['username'],
"scopes": ['open']
}
token = jwt.encode(payload, 'secret', algorithm='HS256')
return True, {'access_token': token, 'account_id': account['_id']}
def verify_bearer_token(token):
# 如果在生成token的时候使用了aud参数,那么校验的时候也需要添加此参数
payload = jwt.decode(token, 'secret', audience='www.xx.com', algorithms=['HS256'])
if payload:
return True, token
return False, token

还需要注意的是:DRF用户认证(Authentication)和用户授权(Authorization)[权限控制Permissions]是两个不同的概念,认证解决的是“有没有”的问题,而授权解决的是“能不能”的问题

Share Comments

pymongo基本操作

知道了大概的mongodb命令后,再下载Mongodb的python驱动,sudo pip3 install pymongo ,然后在python文件中导入相应的库,进行如下的配置:

1
2
3
4
5
6
7
from pymongo import MongoClient
_client = MongoClient('127.0.0.1', 27017) #主机IP,Port
#先创建一个名为Mongodb的数据库,再连接到此数据库
_db = _client['Mongodb']
_db.authenticate('username', 'password')#如果是设置了密码登陆时
test = _db['test'] # 表名
femn = _db['femn']

返回结果

insert , insert_one , insert_many

1
2
3
4
5
insert({'name':1}) # <class 'bson.objectid.ObjectId'>
insert([{'name':1},{'name':2}]) # [ObjectId('xx'),ObjectId('xx')]
insert_one # r.inserted_id <class 'bson.objectid.ObjectId'>
insert_many # <class 'pymongo.results.InsertManyResult'>
print(r.inserted_ids) # list [ObjectId('xx'), ObjectId('xx')]

update

1
{'ok': 1, 'updatedExisting': True, 'nModified': 1, 'n': 1}

upsert=True

1
{'ok':1,'nModified':0,'upserted':ObjectId(''),'n':1,'updatedExisting':True}

update_one

1
2
3
r = db.test.update_one({'_id':'xx'},{'$set':{'name':'femn'}})
# <pymongo.results.UpdateResult object at 0x7f9cb2ebaaf8>
r.raw_result ={'nModified': 0, 'n': 1, 'updatedExisting': True, 'ok': 1}

delete_many delete_one

1
2
3
4
result = db.test.delete_many({'x': 1}) #pymongo.results.DeleteResult
result.deleted_count # 2
result.raw_result # {'n': 2, 'ok': 1}
#n表示匹配成功删除的个数 和delete_one是一样的

find find_one

1
2
3
find() find({}) # return 一个pymongo.cursor.CursorType
find_one({'_id':'xx'}) # return dict 如果没有则返回None
find_one_and_update() # return dict

find find_one 命令

1
2
3
4
5
6
femn.find(filter={'field':'xx'},
projection={'mobile':1,'name':0},limit=10,skip=0,
sort=[('update_time':pymongo.DESCENDING)])
femn.find({'name':xx},{'name':1}).limit().skip().
sort=([('update_time':pymongo.DESCENDING)])

filter中的多个查询操作符

$all $in $nin 此字段可包含(或排除)多个值的文档

1
2
3
4
5
({filed:{'$all':[]}})
# 这个字段是数组并满足这个数组的值和指定的相同的文档
({filed:{'$in','$nin' :['x']}})
# 而这个只表示'x'在字段不管是数组类型的值,还是字符串类型的值,只要有这个'x'的文档就行

$regex 正规匹配字段的值

1
2
({filed:{'$regex': '%s' % student_name}}) 就是 ' *%s* '
({filed:{'$not':{'$regex':'femn'}}})

$exists 判断文档是否有此字段

1
2
({filed: {'$exists': False}})
#得到所有不存在flag字段的文档 可取True

$or 满足一个字段的值就行

1
2
({'gender': '女', '$or': [{'expand.expand_time': '2015-01-01'},
{'coin': {'$lt': 2}}]})

$slice

1
2
({'expand.expand_time':{'$slice':[5,3]}})
# 不是分片,去掉前5个后,再取3个 ,-5时去掉后5个取,取最后3个

混合包含和排除不能同时用

1
({'album_id':{'$in':[i]}},projection={'play_count':1,'cover_image':1})

混合包含和排除不能同时用,既in:[],cover_image:1,comment:0不能同时使用,但可以 in:[],cover_image:1,comment:1

其它的查询操作符 $eachMatch,$gt:2, $lte, $gte, $lt, ‘$slice’ $ne不等于

find_one_and_update命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
find_one_and_update(q,u,p,upsert=False,
return_document=ReturnDocument.AFTER)
test.update({'name':'femn'},
{
# update中的多个更新操作符
'$set':{'name':'xx'},
# 改变某个字段的值
'$inc':{'age':-1},
# 增加或减少 一个字段内容是数值的值
'$addToSet':{'name_list':{'$each':['','']}},
# 往一个数组的字段,添加不一样的内容,可避免反复添加
# $each 一次性增加多个值
# $push 往一个数组的字段中,可重复的添加一样的内容
'$pull':{'list':{'key':'value'}},
# 删除数组中 所有key为value的数组都会删除掉 将所有匹配到的数据都删除
'$pullAll':{'l':[1,2,3]}
# 一次性删除多个数组
})
test.update({'_id':id},{'$set':
{'recording':{'openid':openid,'book_id':book_id}}})
# recording 是一个Object类型
test.find_one({'_id':bookcase_id,recording.book_id':book_id})

改变数值中的值 通过位置或者操作符$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 通过$
test.update({"relationships.fname":"xiong",'_id':id},
{$set:{"relationships.$.age":22}})
# 通过位置用来定位查询文档已匹配的元素,并进行更新
test.update({'_id':id},
{'$inc':{'book_class.'+str(i)+'.class_count':count}})
# 指定一个数组中的一个字段改变
test.update({'_id':id},
{'activity_history':{'click_total':1,per:1,'login':1}}})
# 此方法不行 换成如下方法
test.update({'_id':id},
{'$inc':{'activity_history.0.click_total':1,
'activity_history.0.2017-02':1,
'activity_history.0.login':1}},)

注意pull一个个的数组是个整体

1
2
3
4
5
6
7
'reservation_list':[{'time':"",'device_id':'',
'order_book':[{1},{2}]},{...},{...}]
# 错误的设计 不可能单独的删除order_book中的1,2还保留着...
# 它会删除整个的那个数组reservation_list
'reservation_list':[{'device_id':'','book_id':'',
'book_plu':'','shelf_number':101}]
# 所以要将order_book中保存的信息,放出来

数组字段的内容album_id = ['1','xx'],不是JSON类型

1
2
3
4
test.update({'_id':id},{'$pull':{'album_id':'xx'}})
# album_id必须是数组
{'$pull': {'album_id': {'$in': ['xx']}}
#也可以使用其它的查询操作符 $gt

删除以字典形式保存的数组

1
2
{'$pull': {'praise': {'user_id': user_id,'_id':'xx'}}
{'$pull': {'save_receiver_address': {'flag': del_flag}}})

Share Comments