梁心鸿 發表於 2019-8-12 13:04:00

上手mongodb

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>上手MongoDB</li><li>体系结构</li><li>支持的数据类型</li><li>聚合类比</li><li>使用命令行进行数据的读写</li><li>Java操作MongoDB<ul><li>查询全部</li><li>根据限制条件查询</li><li>添加一个文档</li><li>更新</li><li>替换文档</li><li>删除文档</li></ul></li><li>SpringDataMongoDB<ul><li>编写Repository层,进行简单的CRUD</li><li>MongoTemplate的使用:<ul><li>更新</li></ul></li></ul></li></ul></div><p></p>
<h2 id="上手mongodb">上手MongoDB</h2>
<p>MongoDB 是一个跨平台的,面向文档的数据库,<strong>如果你了解spring-data-jpa的使用, 那么恭喜你,你已经可以使用mongodb做开发了</strong></p>
<p>使用这种类型的数据库还是挺方便的,最小的存储单位是一个文档,但是文档有什么字段,有多少字段它都不关心,而mysql这样的典型的关系型数据库,开发之前得把表设计的明明白白的,说不定还得预留几个字段以备不时之需,因为后续再改就麻烦了</p>
<p>。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。</p>
<h2 id="体系结构">体系结构</h2>
<table>
<thead>
<tr>
<th>MongoDB</th>
<th>Mysql</th>
</tr>
</thead>
<tbody>
<tr>
<td>database</td>
<td>database</td>
</tr>
<tr>
<td>collection</td>
<td>数据表</td>
</tr>
<tr>
<td>document</td>
<td>表中的一行记录</td>
</tr>
<tr>
<td>index</td>
<td>index</td>
</tr>
<tr>
<td>$lookup</td>
<td>table join</td>
</tr>
<tr>
<td>_id</td>
<td>primary key</td>
</tr>
<tr>
<td>aggregation pipeline</td>
<td>group by</td>
</tr>
</tbody>
</table>
<p><strong>一 个MongoDB实例支持多个database并存,同时一个database中可以包含多个collection,所以大家都说它是介于关系数据库和非关系数据库之间,因为它的组成结构真的特别像关系型数据库</strong></p>
<h2 id="支持的数据类型">支持的数据类型</h2>
<table>
<thead>
<tr>
<th>数据类型名</th>
<th>BSON</th>
</tr>
</thead>
<tbody>
<tr>
<td>null</td>
<td></td>
</tr>
<tr>
<td>布尔值:</td>
<td></td>
</tr>
<tr>
<td>int</td>
<td></td>
</tr>
<tr>
<td>Long</td>
<td></td>
</tr>
<tr>
<td>字符串</td>
<td></td>
</tr>
<tr>
<td>日期</td>
<td></td>
</tr>
<tr>
<td>正则</td>
<td></td>
</tr>
<tr>
<td>布尔值:</td>
<td></td>
</tr>
<tr>
<td>数组</td>
<td></td>
</tr>
<tr>
<td>文档的嵌套:</td>
<td>{"XXX":{"YYY":3}}</td>
</tr>
<tr>
<td>对象id</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="聚合类比">聚合类比</h2>
<table>
<thead>
<tr>
<th>MySQL</th>
<th>MongoDB</th>
</tr>
</thead>
<tbody>
<tr>
<td>where</td>
<td>$match</td>
</tr>
<tr>
<td>group by</td>
<td>$group</td>
</tr>
<tr>
<td>having</td>
<td>$match</td>
</tr>
<tr>
<td>select</td>
<td>$project</td>
</tr>
<tr>
<td>order by</td>
<td>$sort</td>
</tr>
<tr>
<td>limit</td>
<td>$limit</td>
</tr>
<tr>
<td>sum</td>
<td>$sum</td>
</tr>
<tr>
<td>count</td>
<td>$sum</td>
</tr>
</tbody>
</table>
<p>注意点:</p>
<ul>
<li><strong>当我们再在命令行窗口通过命令行进行数据的读写时,如果我们这样写 {"XXX",2}, 它会默认把2处理成浮点类型,而不是整形</strong></li>
<li>每一个文档唯一身份id是   _id , 前面有个下划线</li>
</ul>
<h2 id="使用命令行进行数据的读写">使用命令行进行数据的读写</h2>
<p>点击文档官网</p>
<ul>
<li>开启本地的mongo服务器(用来保存数据)<strong>打开之后,就别关了</strong></li>
</ul>
<p>默认使用的端口是 27017</p>
<pre><code>mongod -dbpath=指定文件夹当作数据存储路径 --port 端口号
</code></pre>
<ul>
<li>连接数据库(客户端,进行CRUD)</li>
</ul>
<pre><code>// 在本机直接用下面的命令,模拟连接本机的mongodb
mongo
// 连接远程的mongo
mongo 192.168.88.88
</code></pre>
<ul>
<li>退出</li>
</ul>
<pre><code>exit
</code></pre>
<hr>
<ul>
<li>选择数据库</li>
</ul>
<p>默认选中的数据库是test库</p>
<pre><code>// 如果数据名不存在,默认直接把它创建出来,并且直接选中它使用
user 数据库名
</code></pre>
<ul>
<li>查看当前数据库</li>
</ul>
<pre><code>db
</code></pre>
<ul>
<li>保存一条数据</li>
</ul>
<pre><code>入参位置的bson, 注意key的部分是没有双引号的
db.集合名.insert({content:"哈哈,保存了数据"})
</code></pre>
<ul>
<li>查询全部数据, 默认按照_id从小到大排序</li>
</ul>
<pre><code>db.集合名.find()
</code></pre>
<ul>
<li>返回满足条件的全部数据</li>
</ul>
<pre><code>入参部分的查询条件 bson同样是json格式
db.集合名.find({id:"123"})
</code></pre>
<ul>
<li>返回满足条件的第一条数据</li>
</ul>
<pre><code>入参部分的查询条件 bson同样是json格式
db.集合名.findOne({id:"123"})
</code></pre>
<ul>
<li>根据限制的数量, 返回满足条件的数据</li>
</ul>
<pre><code>入参部分的查询条件 bson同样是json格式
db.集合名.find({id:"123"}).limit(1)
</code></pre>
<ul>
<li>更新文档</li>
</ul>
<pre><code>db.集合名.update(条件,修改后的数据)

// 下面的这条修改语句存在问题,只会保留name字段
db.集合.update({_id:"1"},{name:"新名字"})

// 保留其他字段的写法 db.集合名.update({条件},{$set:{新的值} })
db.集合.update({_id:"1"},{$set:{name:"新名字"}})

</code></pre>
<ul>
<li>删除文档</li>
</ul>
<pre><code>db.集合.remove(条件)

// 删除全部
db.集合.remove({})
</code></pre>
<ul>
<li>统计数量</li>
</ul>
<pre><code>db.spit.count()

db.spit.count({条件})
</code></pre>
<ul>
<li>使用正则实现.模糊查询<br>
/将被模糊查询的字符串/</li>
</ul>
<pre><code>db.集合名.find({content:/将被模糊查询的字符串/})

//匹配以XXX开头的文档
db.集合名.find({content:/^XXX/})
</code></pre>
<ul>
<li>大于,小于,不等于</li>
</ul>
<pre><code>db.集合名.find({"参考的字段":{$gt:value}}) // 查找参考的字段大于value值的文档
$lt 小于
$lte 小于等于   
$gte 大于等于
$ne 不等于
</code></pre>
<ul>
<li>包含和不包含</li>
</ul>
<pre><code>db.集合名.find({_id:{$in:["1","2","3"]}})
db.集合名.find({_id:{$nin:["1","2","3"]}})
</code></pre>
<ul>
<li>条件连接</li>
</ul>
<pre><code>$and:[{},{},{}]
// 查询id 大于1000,小于2000的文档
db.集合名.find({$and:[{_id:{$gte:1000}},{_id:{$lte:2000}}]})

$or:[{},{},{}]
</code></pre>
<ul>
<li>实现列值的增长</li>
</ul>
<pre><code>db.集合名.update({_id:"12"},{$inc:{number:NumberInt(1)}})
</code></pre>
<ul>
<li>按照某个字段排序</li>
</ul>
<pre><code>db.集合名.find({}).sort(字段:1) // 1是升序

db.集合名.find({}).sort(字段:-1) // -1是降序

// 先按照字段1升序, 如果字段1的值相同,按照字段2降序排序
db.集合名.find({}).sort(字段:1| 字段2:-1)


</code></pre>
<h2 id="java操作mongodb">Java操作MongoDB</h2>
<p>添加坐标</p>
<pre><code class="language-xml"> &lt;dependency&gt;
      &lt;groupId&gt;org.mongodb&lt;/groupId&gt;
      &lt;artifactId&gt;mongodb-driver&lt;/artifactId&gt;
      &lt;version&gt;3.6.3&lt;/version&gt;
&lt;/dependency&gt;

</code></pre>
<p>配置文件</p>
<pre><code class="language-yml">server:
port: 9002
data:
    mongodb:
      host: 192.168.88.130
      database: 数据库名字
</code></pre>
<p>使用mongo官方提供的java的驱动包,相当于jdbc</p>
<pre><code class="language-java">// 连接mongo服务器
MongoClient client = new MongoClient("192.168.88.130");
// 得到想操作的数据库
MongoDatabase db = client.getDatabase("数据库名");
// 得到想要操作的集合
MongoCollection&lt;Document&gt; collection = db.getCollection("集合名");

// todo CRUD with collection

// 释放资源
client.close();

</code></pre>
<h3 id="查询全部">查询全部</h3>
<pre><code class="language-java">FindIterable&lt;Document&gt; documents = collection.find();

for (Document document : documents) {
      System.out.println(document);
    }
</code></pre>
<h3 id="根据限制条件查询">根据限制条件查询</h3>
<ul>
<li>查询出content字段为"我很开心"全部文档</li>
</ul>
<pre><code class="language-java">// 条件查询限制为userid==123
BasicDBObject bson = new BasicDBObject("content", "我很开心");

FindIterable&lt;Document&gt; documents = collection.find(bson);
</code></pre>
<ul>
<li>封装条件,查询访问量大于1000的,可以还可以更换成gt gte等等</li>
</ul>
<pre><code class="language-java">//    find(visit:{$gt:1000})
BasicDBObject bson = new BasicDBObject("visits", new BasicDBObject("$gt",1000));

FindIterable&lt;Document&gt; documents = collection.find(bson);
</code></pre>
<p><strong>我发现如果是使用_id当作限制条件,此时如果_id的类型是原生的objectid类型的话,是查询不处任何结果的,但是如果换成我们自定义的_id, 就可以当成限制条件匹配出相应的文档</strong></p>
<h3 id="添加一个文档">添加一个文档</h3>
<p>_id不能重复</p>
<pre><code class="language-java">HashMap&lt;String, Object&gt; map = new HashMap&lt;&gt;();
map.put("_id","10");
map.put("content","啊");
map.put("article","嘻嘻嘻嘻嘻");
Document document = new Document(map);
collection.insertOne(document);
</code></pre>
<p>添加嵌套文档</p>
<pre><code class="language-java">Document document = new Document();

// 插入普通字段
document.put("_id","123");
document.put("key1","value1");
document.put("key2","value2");
document.put("key3","value3");

Document doc = new Document();
doc.put("key4","value4");
// 嵌套文档
document.put("docDetail",doc);

collection.insertOne(document)
</code></pre>
<h3 id="更新">更新</h3>
<ul>
<li>更新数值类型的数,在原来的基础上增加</li>
</ul>
<pre><code class="language-java">// 使用$inc ,在将入参2位置对应的文档的指定字段增加指定的数
BasicDBObject newDoc = newBasicDBObject().append("$inc",new BasicDBObject().append("thumbup",1));

// 第一个参数是限制条件找到要被更新的bson, 第二个参数是将被改变的值
collection.updateOne(newBasicDBObject().append("content","我是新的content"),newDoc);

</code></pre>
<ul>
<li>更新普通字符串类型</li>
</ul>
<pre><code class="language-java">// 使用$set ,在将入参2位置对应的文档的指定字段更新为指定的值
BasicDBObject newDoc = newBasicDBObject().append("$set",new BasicDBObject().append("nickname","朱昌武"));

// 第一个参数是限制条件找到要被更新的bson, 第二个参数是将被改变的值
collection.updateOne(newBasicDBObject().append("_id","1"),newDoc);
</code></pre>
<h3 id="替换文档">替换文档</h3>
<p><strong>同样,如果入参1位置的过滤条件,使用的_id是原生ObjectId类型的话,匹配不出任何结果</strong></p>
<pre><code class="language-java">BasicDBObject bson = new BasicDBObject("_id", "5d4fd7cb92cb2acaf109a46e");

//   Document filter = new Document();
//   filter.append("_id", "5d4fd7cb92cb2acaf109a46e");

Document replacement = new Document();
replacement.append("key1","我替换了一第一个文档");

collection.replaceOne(bson,replacement);
</code></pre>
<h3 id="删除文档">删除文档</h3>
<pre><code class="language-java">// 删除一个
Document filter = new Document();
filter.append("hello", "world");

collection.deleteOne(filter);

// 删除多个
Document document = new Document();

// 第一个参数是限制条件找到要被更新的bson, 第二个参数是将被改变的值
collection.deleteMany(document);
</code></pre>
<h2 id="springdatamongodb">SpringDataMongoDB</h2>
<h3 id="编写repository层进行简单的crud">编写Repository层,进行简单的CRUD</h3>
<p>spring-data-mongodb的用法和jpa是一样的,Spring很强大,一统天下</p>
<ul>
<li>
<p>引入启动器</p>
</li>
<li>
<p>配置文件,和上面的一样</p>
</li>
<li>
<p>编写DAO层</p>
</li>
</ul>
<pre><code class="language-java">public interface SpitRepository   extends MongoRepository&lt;Spit,String&gt; {}
</code></pre>
<p>它内置了简单的增删改查的方法,可以分页,也可以使用Spring对jpql的高级封装比如findByXXX等</p>
<h3 id="mongotemplate的使用">MongoTemplate的使用:</h3>
<h4 id="更新-1">更新</h4>
<p><strong>如果被更新的文档没有该条记录,就会创建出这个field,然后赋值</strong></p>
<pre><code class="language-java">Query query = new Query();
Criteria criteria = new Criteria();
query.addCriteria(Criteria.where("_id").is("5d50cee71e5a931d2cd72e43"));

Update update = new Update();
update.set("key1","新的值");

// 只会更新满足条件的第一条
入参位置1query作用: 根据条件找出对应的doc
入参位置2update作用:进行更新的操作
mongoTemplate.updateFirst(query,update,"集合");
</code></pre>
<p>使用下面的函数也能达到相同更新的效果</p>
<pre><code class="language-java">mongoTemplate.upsert(query,update,"集合");
</code></pre>
<p>批量更新:</p>
<p>批量更新满足条件的doc,只要前面的条件满足, 就会被更新, 不存在的字段会添加上去, 存在的被更新掉</p>
<pre><code class="language-java">Query query = new Query();
query.addCriteria(Criteria.where("nickname").is("恩恩"));

Update update = new Update();
update.set("share","2");// 同样, key8不存在,就新添加进去

// 只会更新满足条件的第一条
mongoTemplate.updateMulti(query,update,"spit");
</code></pre>
<p>仅仅更新满足条件的第一个</p>
<pre><code class="language-java">Query query = new Query();
query.addCriteria(Criteria.where("nickname").is("恩恩"));

Update update = new Update();
update.set("share","2");// 同样, key8不存在,就新添加进去

// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");
</code></pre>
<p>给指定的字段增加指定的数量, <strong>注意点是, 如果尝试对字符串增加指定的数,不报错,但是也不会改变原来的值</strong></p>
<pre><code class="language-java">Query query = new Query();
query.addCriteria(Criteria.where("_id").is("1"));

Update update = new Update();
update.inc("thumbup",3); // 给指定的字段增加指定的数量

// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");

</code></pre>
<p>重命名文档的某个key</p>
<pre><code class="language-java">Query query = new Query();
Criteria criteria = new Criteria();
query.addCriteria(Criteria.where("_id").is("1"));

Update update = new Update();
update.rename("_class","新的class"); // 给指定的字段增加指定的数量

// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");
</code></pre>
<p>移除key</p>
<pre><code class="language-java">update.unset("新的class");
</code></pre>
<p>在现有的文档基础上,嵌入文档</p>
<pre><code class="language-java">Query query = new Query();
Criteria criteria = new Criteria();
query.addCriteria(Criteria.where("_id").is("1"));

Update update = new Update();

Spit spit = new Spit();
spit.set_id("666");
spit.setContent("我的被嵌套进来的文档");

update.addToSet("childSpit",spit);
// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");
</code></pre>
<p>修改被嵌入的文档</p>
<pre><code class="language-java">Query query = new Query();
Criteria criteria = new Criteria();
// 通过添加条件定位出 指定的嵌套文档
query.addCriteria(Criteria.where("_id").is("1").and("childSpit._id").is("666"));

Update update = new Update();

// todo 通过这样条件字段名.$.field
update.set("childSpit.$.content","嵌套文档的conntent被改变了");

// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");
</code></pre>
<p>删除嵌套文档</p>
<pre><code class="language-java">Query query = new Query();
Criteria criteria = new Criteria();
// 通过添加条件定位出 指定的嵌套文档
query.addCriteria(Criteria.where("_id").is("1").and("childSpit._id").is("666"));

Update update = new Update();

update.unset("childSpit.$");

// 只会更新满足条件的第一条
mongoTemplate.updateFirst(query,update,"spit");
</code></pre>
<p>使用:MongoTemplate 提高效率的场景</p>
<p>在点赞的业务中,给某个帖子点赞的逻辑如下:</p>
<ul>
<li>前端提交帖子的id, 后端根据id查询一次数据库,把帖子查出来</li>
<li>给帖子的赞数+1</li>
<li>把帖子更新进数据库</li>
</ul>
<blockquote>
<p>前前后后使用了两次查询,下面使用MongoTemplate,可以实现和数据库一次交互,完成更新</p>
</blockquote>
<pre><code class="language-java">Query query = new Query();
query.addCriteria(Criteria.where("_id").is("前端提交的id"));
Update update = new Update();
update.inc("赞的字段",1); // 自增1
mongoTemplate.updateFirst(query,update,"集合名字");
</code></pre>
<hr>
<p>参考博客:illustriousness<br>
参考博客: tbyang</p><br><br>
来源:https://www.cnblogs.com/ZhuChangwu/p/11339109.html
頁: [1]
查看完整版本: 上手mongodb