zoukankan      html  css  js  c++  java
  • 使用场景举例

    本部分内容包括:
      1、学习原生api的必要性;
      2、原生api的书写语法总结;
      3、应用场景举例;
    ----------------------------------------------------------------------------------------------------------------------------------------------------------
    1、学习原生api的必要性
      我们对mongodb针对mongoTemplate与原生api进行了简单的使用,通过对比,可以很直观的发现mongoTemplate使用更加简单,可以在不用了解mongodb的查询api的情况下直接按照sql的查询逻辑进行书写,但一切真的那么完美么,显然不是。原生api对查询更加的灵活,更重要的是它与通过命令行以及客户端工具进行查询时,对查询条件的书写是一致的,这在开发及运维的过程中是很重要的。不熟悉mongodb的查询语法就无法展开运维工作,也很难在开发时对数据库的数据进行随时查询。
    2、原生api的语法总结
      首先,我们看一下mongodb查询条件的几个简单写法,毕竟复杂的查询也是简单的条件拼接出来的嘛
      A、and 查询
      查询 age <20 and country='china'的用户
      写法:{"age":{$lt:20},"country":{$eq:'china'}}

      当然,这是用工具查的,如果用命令行,应该写作db.users.find({"age":{$lt:20},"country":{$eq:'china'}}).pretty(),最后的pretty是用来格式化查询结果的。
      基本语法就是{"条件字段1":{运算符:目标值},"条件字段2":{运算符:目标值}},可以连续多个条件;运算符有$eq, $gt, $lt, $lte, $gte, $ne等;
      B、or 查询
      查询 salary > 9999 or length < 1.5
      写法:{$or:[{"salary":{$gt:9999}}, {"length":{$lt:1.5}}]}

      字段的条件书写还是{"条件字段":{运算符:目标值}},区别只是将所有or连接的条件组装成了一个数组,且在数组的外边有一个运算符$or;
      C、and 跟 or联合使用
      查询 country=china and (alary > 9999 or length < 1.5)
      写法:{"country":{$eq:'china'}, $or:[{"salary":{$gt:9999}}, {"length":{$lt:1.5}}]}

      注意括号的配对,所有查询条件最外层有花括号{},具体的查询条件没有花括号,是直接键值对,键为字段名,值为一个对象,一般是判断条件跟目标值;
      D、like 查询
      查询 username like '张%'
      写法:{"username":{$regex:'张.*'}}

      注意,这里不是百分号%,而是.*,一个小数点跟一个*号;,匹配是通过正则运算;具体的正则规则比较多,不做赘述;
      E、order 排序
      查询 order by age asc, salary desc
      写法:sort({"age":1,'salary':-1})

      注意,这里1位升序,-1为降序排列
      F、分页,查询m到n条
      查询 limit m,(n-m) --mysql语法
      写法:db.xxx.find().skip(m).limit(n-m)

      G、group 分组
      查询 select max(salary),avg(length) from users where age>18 gourp by country; 查询每个国家的最大薪资跟平均身高;
      写法:
    db.users.aggregate([
    	{
    		$match:{
    			"age":{"$gt":18}
    		}
    	},
    	{	
    		$group:{
    			"_id":{"country":"$country"},
    			"avg_salary":{"$max":"$salary"},
    			"avg_length":{"$avg":"$length"}
    			}
    	}
    ])
    

      

      其中,$match为where的过滤条件,排序字段为$group的"_id"字段,如果有多个排序字段,直接在对象中添加即可;
      以上即为常见的sql查询以及对应的mongodb的查询写法,虽然语法跟传统sql不一样,但基本逻辑并无大的差别,感性认识上,mongodb的语法结构大概就是 字段名:{比较条件:比较值},熟悉了mongodb的基本语法,我们再看几个典型的使用场景;
    3、应用场景举例
      实际使用中,查询一般是多个条件的组合,查询结果可能也要作相应处理,这里就会用到聚合操作。
      聚合可以理解为就是一个管道,管道里的每一步的输出都作为下一步的输入数据:
      输入文档 ----> 管道操作1 ----> 管道操作2 ----> 管道操作3 ----> 输出文档
      常用的管道操作:
        $project: 投影,指定输出文档 中的字段
        $match: 过来条件
        $limit: 限制返回的文档数
        $skip: 跳过指定数量的文档
        $unwind: 将文档中的某一个数组类型的字段拆分为多条,每条包含数组中的一个值
        $group:将集合中的文档分组,用于统计结果
        $sort:将输入文档排序后输出
      应用举例:
      1、假设微博用户信息存放在mongodb中,要求用户界面默认显示3条评论,点击显示更多,可以查看接下来的4条记录;数据格式如下:
      数据:

    var cang = {"username" : "cang laoshi",
    "country" : "japan",
    "address" : {"aCode" : "411000","add" : "东郡"},
    "favorites" : {
    "movies" : ["蜘蛛侠","钢铁侠","蝙蝠侠"],
    "cites" : ["青岛","东莞","上海"]
    },
    "salary":NumberDecimal("9999.88"),
    "comments":[
    {
    "author": "jack1",
    "content": "jack的评论1",
    "commentTime": ISODate("2017-12-11T04:26:18.234Z")
    },
    {
    "author": "jack2",
    "content": "jack的评论2",
    "commentTime": ISODate("2017-12-12T04:26:18.234Z")
    },
    {
    "author": "jack3",
    "content": "jack的评论3",
    "commentTime": ISODate("2017-12-13T04:26:18.234Z")
    },
    {
    "author": "jack4",
    "content": "jack的评论4",
    "commentTime": ISODate("2017-12-14T04:26:18.234Z")
    },
    {
    "author": "jack5",
    "content": "jack的评论5",
    "commentTime": ISODate("2017-12-15T04:26:18.234Z")
    },
    {
    "author": "jack6",
    "content": "jack的评论6",
    "commentTime": ISODate("2017-12-16T04:26:18.234Z")
    },
    {
    "author": "jack7",
    "content": "jack的评论7",
    "commentTime": ISODate("2017-12-17T04:26:18.234Z")
    }
    ]};
    db.users.insert(cang);
      应该这么写:
      db.users.find({"username":"cang laoshi"},{"comments":{"$slice":[3,4]},"$elemMatch":""})
      解释一下:find()中两个参数,第一个为查询条件,必须有,第二个参数为投影设置,可有可无;投影的意思是从查询结果中只找我们需要的进行显示,而把敏感信息隐藏;一般写法为{字段名:1,字段名2:0} 1的意思为显示,0为隐藏,需要注意的是写的时候,如果多个字段,要么都是1要么都是0,混搭是不行的,mongodb约束如此,没得解释;$slice用于返回内容片段而不是全部,后边[3,4]的意思为从第3个元素(从0开始计数)连续取4个;$elemMatch一般用于内嵌文档匹配,此处用作投影使用;

      需要注意的是,此处如果不使用$elemMatch,而仍需要只有comments投影的效果,在comments跟$slice后添加comments:1是无效的,因为后面的会把前边的配置覆盖掉,最终效果是查询并显示了所有的comments;避免方式之一是将评论放在comments.list中,也就是将文档多添加了一层list,可以在设置$slice的时候用comments.list,但这样会导致文档结构多了一层无用list,这么设计并不好。
      2、数据结构仍然跟上结构类似,但增加几条数据,然后查询 tony1或者jack1 评论过的用户
      应该这么写:db.users.find({"comments.author":{"$in":["tony1","jack1"]}})

      当然,这里也可以用or来实现,写的会略微复杂一些;同理,另一个问题,tony1跟jack1都评论过的用户应该怎么写呢?
      答案:db.users.find({"comments.author":{"$all":["tony1","jack1"]}})
      3、查找jack3的评论为"板凳板凳"的用户名
      db.users.find({"comments":{"$elemMatch":{"author":"jack3","content":"板凳板凳"}}})
      4、将1 的场景详细化:
      查看评论,打开页面默认显示3条;点击查看更多,新加载3条;按照评论时间降序排列;
      解决办法之一:
        1、查找的时候进行排序,先排序后查找,相对来说是比较麻烦的,但可以在新增的时候就对评论进行排序,那么查询的时候就无序关心排序问题了;
        2、默认3条跟查看更多类似于分页,在示例1中有;
        3、如果有多种排序需求,该如何处理;
      按照上面思路,新增评论:

    db.users.updateOne({{"username":"name111"},{
    "$push":{
    "comments":{
    "$each":[{
    "author":"james",
    "content":"我的评论aaa",
    ”commentTime“:ISODate("2019-01-09T04:23:23.233Z")
    },
    {
    "author":"james2",
    "content":"我的评论aaa2",
    ”commentTime“:ISODate("2019-02-09T04:23:23.233Z")
    }],
    "$sort":{"commentTime":-1}
    }
    }
    }});
      updateOne的两个参数分别为查询条件跟更新的数据,$push为新增之意,comments为更新的字段,$each表示所有数据都遵循此操作,$sort为插入时的排序字段,-1表示倒序;
      分页查询,不再赘述,只提供一种写法:db.users.find({"username":"cang laoshi"},{"comments":{"$slice":[0,3]},"id":1});会只显示id跟comments,且comments只有3条;
      对于排序,我们的解决思路是插入的时候就进行排序,但这里有个问题,如果我们有多种排序需求,比如按照评论的点赞数进行排序,或者按照作者的知名度(知名度的计算过程不考虑)进行排序,该怎么处理?答案是使用聚合,例如:

    db.users.aggregate([
    {"$match":{"username":"cang laoshi"}},
    {"$unwind":"$comments"},  //将文档中的某一个数组类型字段拆分为多条,没条包含数组中的一个值
    {"$sort":{"comments.author.xxx":1}},     //排序
    {"$project":{"comments":1}},  //投影
    {"$skip":0},
    {"$limit":3}
    ]) 
      unwind这个不太好理解,参考文章https://www.cnblogs.com/wangxiaoheng/articles/9699625.html这个有个例子,说的比较清楚了;
      不过多举例了,通过以上我们发现,mongodb的查询api还是比较琐碎的,想要用好的话,还是需要一定练习的。
      回想一下mongodb的查询api,假设有这么个需求,该怎么查:有个订单表,要求统计2016年5月6号之前,每个用户每个月消费了多少钱,并按照用户名排序
      答案:
    db.orders.aggregate([
    {"$match":{"orderTime":{"$lt":new Date("2016-05-06T00:00:00.000Z")}}},
    {"$group":{"_id":{"userName":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}},
    {"$sort":{"total":1}}
    ])
    ----------------------------
      我们注意到,上边的查询示例并没有join操作,是mongodb不支持关联查询么?
      这个,倒不是说mongodb不支持关联查询,如果要做的话也是可以的,这就是DBRef相关内容,但mongodb的设计理念是尽量使用单表内嵌,而不是表关联;比如,论坛系统,如果用关系库来设计,可能要有用户表,论贴表,评论表等,但用mongodb的话,可能一个用户表就够了,发表的帖子,自己的评论等都作为字段内容内嵌到用户信息里边即可,这也是mongodb设计的方便之处;但mongodb约束单个文档内容不能超过16M,当超过16M的时候,需要选择DBRef,将大数据放到另外一张表中。

     

  • 相关阅读:
    个人收藏的flex特效网址【经典中的极品】
    JavaWEB开发国际化
    Java实现寻找和为定值的多个数
    Java实现寻找和为定值的多个数
    Java实现寻找和为定值的多个数
    Java实现寻找和为定值的多个数
    Java实现二进制幂
    Java实现二进制幂
    Java实现二进制幂
    Java实现二进制幂
  • 原文地址:https://www.cnblogs.com/nevermorewang/p/11574466.html
Copyright © 2011-2022 走看看