NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。
NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
我们每次插入一条数据系统都会自动帮我们插入一个_id键,这个键的值不可以重复,它可以是任何类型的,我们也可以手动的插入,默认情况下它的数据类型是ObjectId,由于MongoDB在设计之初就是用作分布式数据库,所以使用ObjectId可以避免不同数据库中_id的重复(如果使用自增的方式在分布式系统中就会出现重复的_id的值),这个特点有点类似于Git中的版本号和Svn中版本号的区别。
ObjectId使用12字节的存储空间,每个字节可以存储两个十六进制数字,所以一共可以存储24个十六进制数字组成的字符串,在这24个字符串中,前8位表示时间戳,接下来6位是一个机器码,接下来4位表示进程id,最后6位表示计数器。
第一个参数是查询的条件,第二个参数是修改器。第一个false表示如果不存在update记录,是否将我们要更新的文档作为一个新文档插入,true表示插入,false表示不插入,默认为false,第二个true表示是否更新全部查到的文档,false表示只更新第一条记录,true表示更新所有查到的文档。
$set: 设置将某key设置为某值
db.sang_collec.update({x:3},{$set:{x:1}})// 将所有的x = 3的改成1$unset: 删除当前Key
db.sang_collec.update({x:1},{$unset:{x:1}})// 将所有的x = 1的x(field)删除$inc: 对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作
db.sang_collec.update({},{$inc:{y:1}})// 所有文档中的y + 1,但是只会更新第一条。 db.sang_collec.update({},{$inc:{y:1}},false,true)// 设置属性以后。所有文档y + 1$push : 数组修改器
db.sang_collec.update({x:4},{$push:{name:"xxxx"}})// 添加一个 db.sang_collec.update({x:4},{$push:{name:{$each:["111","222","333"]}}})// 添加多个 db.sang_collec.update({x:4},{$push:{name:{$each:["qqq","www","eee"],$slice:-4}}})// 添加多个,保留最新的4个 注意$slice的值为负数 db.sang_collec.update({x:4},{$push:{name:{$each:[{score:1},{score:3},{score:5},{score:1},{score:2},{score:6}],$slice:4,$sort:{score:-1}}}})// 根据score排序(-1表示降序,1表示升序)保存前4个。sort不能只和each$addToSet: 数组判断插入
db.sang_collec.update({x:4},{$addToSet:{names:"yyyy"}})// addToSet如果names:"yyyy"存在则不插入。不存在插入$pop: 数组中删除数据根据首尾
db.sang_collec.update({x:4},{$pop:{names:1}})//pop 删除数据中数据。1表示从数组的末尾删除一条数据,-1表示从数组的开头删除一条数据。$pull: 数组删除中的指定数据
db.sang_collec.update({x:4},{$pull:{names:"xxxx"}})// pull指定删除数组中的数据查询 成绩在[90,100]之间的学生 :
db.sang_collect.find({score:{$lte:100,$gte:90}}) $in 有点类似于SQL中的in关键字 如我想查询x为1或者2的所有文档 : db.sang_collect.find({x:{$in:[1,2]}}) $nin 恰好相反,表示查询某一个字段不在某一个范围内的文档, 如查询**x不为1或者2(不为1且不为2)**的所有文档 : db.sang_collect.find({x:{$nin:[1,2]}})$or 有点类似于SQL中的or关键字,表示多个查询条件之间是或的关系 :
db.sang_collect.find({$or:[{x:1},{y:99}]})$type 可以用来根据数据类型查找数据,比如我想要查找x类型为数字的文档 :
db.sang_collect.find({x:{$type:1}}) 类型对应数字别名说明Double11doubleString2stringObject3objectArray4arrayBinary data5binDataUndefined6undefined弃用ObjectId7objectIdBoolean8boolDate9dateNull10nullRegular Expression11regexDBPointer12dbPointerJavaScript13javascriptSymbol14symbolJavaScript(with scope)15javascriptWithScope32-bit integer16intTimestamp17timestamp64-bit integer18longMin key-1minKeyMax key127maxKey$not 用来执行取反操作,比如我想要查询所有x的类型不为数字的文档 :
db.sang_collect.find({x:{$not:{$type:1}}})$and 类似于SQL中的and,比如我想查询y大于98并且小于100的数据 :
db.sang_collect.find({$and:[{y:{$gt:98}},{y:{$lt:100}}]})null
null的查询稍微有点不同,假如我想查询z为null的数据,如下:
db.sang_collect.find({z:null})这样不仅会查出z为null的文档,也会查出所有没有z字段的文档,如果只想查询z为null的字段,那就再多加一个条件,判断一下z这个字段存在不,如下:
db.sang_collect.find({z:{$in:[null],$exists:true}})1.name表示索引的名称 2.dropDups表示创建唯一性索引时如果出现重复,则将重复的删除,只保留第一个 3.background是否在后台创建索引,在后台创建索引不影响数据库当前的操作,默认为false 4.unique是否创建唯一索引,默认false 5.sparse对文档中不存在的字段是否不起用索引,默认false 6.v表示索引的版本号,默认为2 7.weights表示索引的权重
$match: 获取集合中所有指定字段的指定值的文档
db.sang_collect.aggregate({$match:{x:1}}) ----------------------------------------- { "_id" : ObjectId("5db006610c442201f697ab65"), "x" : 1, "y" : 12 } { "_id" : ObjectId("5db006610c442201f697ab66"), "x" : 1, "y" : 12 } { "_id" : ObjectId("5db022d30c442201f697ab6d"), "x" : 1, "y" : 11 } { "_id" : ObjectId("5db022d30c442201f697ab6e"), "x" : 1, "y" : 11 } { "_id" : ObjectId("5db022d30c442201f697ab6f"), "x" : 1, "y" : 11 }$project: 可以用来提取想要的字段
db.sang_collec.aggregate({$project:{x:1,_id:0}}) ------------------------------------------------ { "x" : 5 } { "x" : 123132 } { "x" : 123132 } { "x" : 123132 } { "x" : 123132 } { "x" : 1 } { "x" : 1 } { "x" : 1 } { "x" : 4 } { "x" : 5 }数字表达式:
{ "_id" : ObjectId("59f841f5b998d8acc7d08863"), "orderAddressL" : "ShenZhen", // 商品价格 "prodMoney" : 45.0, // 运费 "freight" : 13.0, // 折扣金额 "discounts" : 3.0, "orderDate" : ISODate("2017-10-31T09:27:17.342Z"), "prods" : [ "可乐", "奶茶" ] } 订单的总费用为商品费用加上运费,查询如下: db.sang_collect.aggregate({$project:{totalMoney:{$add:["$prodMoney","$freight"]}}}) 实际付款的费用是总费用减去折扣,如下: db.sang_collect.aggregate({$project:{totalPay:{$subtract:[{$add:["$prodMoney","$freight"]},"$discounts"]}}}) 比如计算prodMoney和freight和discounts的乘积: db.sang_collect.aggregate({$project:{test1:{$multiply:["$prodMoney","$freight","$discounts"]}}}) 再比如求freight的商,如下: db.sang_collect.aggregate({$project:{test1:{$divide:["$prodMoney","$freight"]}}}) 再比如用prodMoney取模,如下: db.sang_collect.aggregate({$project:{test1:{$mod:["$prodMoney","$freight"]}}})日期表达式:
db.sang_collect.aggregate({$project:{"年份":{$year:"$orderDate"},"月份":{$month:"$orderDate"},"一年中第几周":{$week:"$orderDate"},"日期":{$dayOfMonth:"$orderDate"},"星期":{$dayOfWeek:"$orderDate"},"一年中第几天":{$dayOfYear:"$orderDate"},"时":{$hour:"$orderDate"},"分":{$minute:"$orderDate"},"秒":{$second:"$orderDate"},"毫秒":{$millisecond:"$orderDate"},"自定义格式化时间":{$dateToString:{format:"%Y年%m月%d %H:%M:%S",date:"$orderDate"}}}}) ------------------------------------------------------------------------------------------------ { "_id" : ObjectId("59f841f5b998d8acc7d08861"), "年份" : 2017, "月份" : 10, "一年中第几周" : 44, "日期" : 31, "星期" : 3, "一年中第几天" : 304, "时" : 9, "分" : 27, "秒" : 17, "毫秒" : 342, "自定义格式化时间" : "2017年10月31 09:27:17" }字符串表达式:
$substr 截取0-2(前两个字符)
db.sang_collect.aggregate({$project:{addr:{$substr:["$orderAddressL",0,2]}}})$concat 拼接字符串
db.sang_collect.aggregate({$project:{addr:{$concat:["$orderAddressL",{$dateToString:{format:"--%Y年%m月%d",date:"$orderDate"}}]}}})$toLower 转小写
db.sang_collect.aggregate({$project:{addr:{$toLower:"$orderAddressL"}}})$toUpper 转大写
db.sang_collect.aggregate({$project:{addr:{$toUpper:"$orderAddressL"}}})$group分组操作:
基本操作:
//根据地址分类。统计每个地址的数量 db.sang_collect.aggregate({$group:{_id:"$orderAddressL",count:{$sum:1}}}) //根据地址分类。统计每个地址的freight的平均数 db.sang_collect.aggregate({$group:{_id:"$orderAddressL",avgFreight:{$avg:"$freight"}}})极值操作
//根据地址分类。展示最大的freight的值 db.sang_collect.aggregate({$group:{_id:"$orderAddressL",maxFreight:{$max:"$freight"}}}) // 根据地址分类。展示该城市第一个运费单: db.sang_collect.aggregate({$group:{_id:"$orderAddressL",firstFreight:{$first:"$freight"}}})mapReduce: MapReduce可以用来实现更复杂的聚合命令,使用MapReduce主要实现两个函数:map函数和reduce函数,map函数用来生成键值对序列,map函数的结果作为reduce函数的参数,reduce函数中再做进一步的统计,比如我的数据集如下:
{"_id" : ObjectId("59fa71d71fd59c3b2cd908d7"),"name" : "鲁迅","book" : "呐喊","price" : 38.0,"publisher" : "人民文学出版社"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908d8"),"name" : "曹雪芹","book" : "红楼梦","price" : 22.0,"publisher" : "人民文学出版社"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908d9"),"name" : "钱钟书","book" : "宋诗选注","price" : 99.0,"publisher" : "人民文学出版社"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908da"),"name" : "钱钟书","book" : "谈艺录","price" : 66.0,"publisher" : "三联书店"} {"_id" : ObjectId("59fa71d71fd59c3b2cd908db"),"name" : "鲁迅","book" : "彷徨","price" : 55.0,"publisher" : "花城出版社"}假如我想查询每位作者所出的书的总价,操作如下:
var map=function(){emit(this.name,this.price)} var reduce=function(key,value){return Array.sum(value)} var options={out:"totalPrice"} db.sang_books.mapReduce(map,reduce,options); db.totalPrice.find() ---------------------------------------------------------- { "_id" : "曹雪芹", "value" : 22.0 } { "_id" : "钱钟书", "value" : 165.0 } { "_id" : "鲁迅", "value" : 93.0 }emit函数主要用来实现分组,接收两个参数,第一个参数表示分组的字段,第二个参数表示要统计的数据,reduce来做具体的数据处理操作,接收两个参数,对应emit方法的两个参数,这里使用了Array中的sum函数对price字段进行自加处理,options中定义了将结果输出的集合,届时我们将在这个集合中去查询数据
将每位作者的书列出来,如下:
var map=function(){emit(this.name,this.book)} var reduce=function(key,value){return value.join(',')} var options={out:"books"} db.sang_books.mapReduce(map,reduce,options); db.books.find() ----------------------------------------------------------- { "_id" : "曹雪芹", "value" : "红楼梦" } { "_id" : "钱钟书", "value" : "宋诗选注,谈艺录" } { "_id" : "鲁迅", "value" : "呐喊,彷徨" }pom.xml:
<!--mongodb--> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>3.11.1</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>2.1.6.RELEASE</version> </dependency>MongoDBConfig.java:
@Configuration @EnableMongoRepositories("chendongdong.spring.test.bean.Test9.dao") public class MongoDBConfig extends AbstractMongoConfiguration{ @Override protected String getDatabaseName() { return "mymongo"; } @Override public MongoClient mongoClient() { return new MongoClient("119.23.xxx.130",27017); } }Userdao.java:
public interface UserDao extends MongoRepository<User, String> { }测试代码:
@Autowired private UserDao userDao; @RequestMapping("/Print6") @ResponseBody public Object Print6(){ Iterable<User> all = userDao.findAll(); return "ok"; }我们来看看为什么没有提供任何方法就可以使用其中的很多方法,那么这些方法究竟是怎么来的呢?
我们只写了接口,但是没有写实现类,这个实现类就是Spring在运行的时候,注入的代理对象。
Spring怎么知道生成的那个dao的实现类?因为在配置文件中指定了dao接口所在的包
@EnableMongoRepositories("chendongdong.spring.test.bean.Test9.dao")通过JdkDynamicAopProxy对象的invoke生成了代理对象SimpleJpaRepository,因为JdkDynamicAopProxy实现了InvocationHandler接口,所以这个类有invoke方法
在JdkDynamicAopProxy的invoke方法中有个target对象,这个对象就是真正干活的对象( SimpleJpaRepository )
SimpleJpaRepository实现了我们dao接口继承的那两个接口,所以这个类中肯定有接口的所有方法
SimpleMongoRepository源码:
public class SimpleMongoRepository<T, ID> implements MongoRepository<T, ID> { private final MongoOperations mongoOperations; private final MongoEntityInformation<T, ID> entityInformation; public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) { Assert.notNull(metadata, "MongoEntityInformation must not be null!"); Assert.notNull(mongoOperations, "MongoOperations must not be null!"); this.entityInformation = metadata; this.mongoOperations = mongoOperations; } @Override public <S extends T> S save(S entity) { Assert.notNull(entity, "Entity must not be null!"); if (entityInformation.isNew(entity)) { return mongoOperations.insert(entity, entityInformation.getCollectionName()); } return mongoOperations.save(entity, entityInformation.getCollectionName()); } ... ... 剩下的不copy了 }