优势说明
mongoDB作为文档型数据库,比关系型数据库更加擅长存放复杂类型的数据。关系型数据库在定义表结构时三范式要求每一个单元格数据表示的信息是不可以再分割的。而mongoDB则是运行集合下的属性可以是一个数据项一个对象或者是一个数组。
使用场景
用关系型数据库来记录(文章,评论,点赞信息,文章附件)信息时我们需要建立四张表来维持它们之间的关系。分别是文章表,评论表,点赞表,附件表,其中评论表、点赞表和附件表都持有文章表主键。而使用mongoDB时我们只需要一个文章集合就可以,这个集合包括,文章内容属性,文章发表时间属性,文件状态属性,评论数组属性,点赞数组属性,文章附件数组属性。
这样的系统通常的操作是先查询文章列表,再根据用户选择查询某一篇文章的详细信息,详细信息中包括文章内容,附件,点赞信息和评论信息。这样的需要使用关系型数据库也可以很好地胜任,只是在查询详细信息时使用关联从四个表中获取数据。但是我们如果把需求再提升一下,我们需要在文章列表中就展示文章内容、评论、附件和点赞信息,这样的需要也是存在的,比如微信的朋友圈就是这样。这是使用关系型数据库就有些不那么方便了,需要先从文章表中查询出文章列表,在根据每一个文章的主键到其他表中获取信息。这时如果列表要求显示10条记录,就需要执行11次的sql语句。而在这样的需求下mongoDB任然是只需要执行一次查询就可以获取到全部数据。
需要克服的问题
大部分的同行对关系型数据使用的熟练度大大地高于mongoDB的熟练度,所有在使用mongoDB开发以上需要的系统时,需要克服以下几个问题。
新增数据
使用脚本操作
[javascript] view plain copy
db.message.insert({
"_id" : 5.0,
"content" : "心情超级很好,这是美好的一天。。。",
"createTime" : ISODate("2018-05-24T12:45:43.302+08:00"),
"userName" : "caifu",
"attachments" : [
{
"url" : "http://test123/123.jpg",
"name" : "123.jpg"
},
{
"url" : "http://test123/1234.jpg",
"name" : "1234.jpg"
}
]
})
使用JAVA操作
[java] view plain copy
import org.springframework.data.mongodb.core.MongoTemplate;
https://my.oschina.net/u/3881961/blog/1827121
https://my.oschina.net/u/3881961/blog/1827118
https://my.oschina.net/u/3881961/blog/1827110
https://my.oschina.net/u/3881961/blog/1827107
[java] view plain copy
mongoTemplate.insert(message,"message");
向文章中添加评论
使用脚本操作
[javascript] view plain copy
db.message.update(
{ _id: 5 },
{ $addToSet: { comments: {
"_id":"123245",
"user" : "cai",
"createTime" : ISODate("2018-05-29T10:32:46.209+08:00"),
"content":"一起出去玩呀",
"status":0
} } }
)
[javascript] view plain copy
使用JAVA操作
[java] view plain copy
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
[java] view plain copy
Query query = Query.query(Criteria.where("_id").is(messageId));
Update update = new Update();
update.addToSet("replies", reply);
mongoTemplate.upsert(query, update, "message");
删除评论
使用JAVA操作,这个操作会在数组中用null替换掉原来的数据,建议使用逻辑删除
[java] view plain copy
Query query = Query.query(Criteria.where("_id").is(messageId)
<span style="white-space:pre;"> </span>.and("replies._id").is(replyId));
Update update = new Update();
update.unset("replies.$");
mongoTemplate.updateFirst(query, update, "message");
修改评论
使用JAVA操作,这个操作可以作为逻辑删除
[java] view plain copy
Query updateQuery = Query.query(Criteria.where("_id").is(messageId).and("comments._id").is(commentId));
Update update = new Update();
update.set("comments.$.status", 1);
cloudChairmanMongo.upsert(updateQuery, update, CLOUDCHAIRMANMESSAGE);
查询部分评论和总的赞数,排序等等等。。。
[javascript] view plain copy
db.message.aggregate([
{$match:{messageType:2,status:0}},
{ $sort: { createDate : -1 } },
{ $skip: 0 },
{ $limit: 30},
{"$project":{
content:1,
messageType:1,
createDate:1,
likes:1,
createUserId:1,
createUserName:1,
nowDate:1,
picUrls:1,
status:1,
likess:"$likes.createUserId",
comments:{$filter:{input:"$comments",as:"comment",cond:{$eq:["$$comment.status",0]}}}
}
},
{$addFields: {
likeCount: { $size: "$likes" },
myLike:{$in: ["d98f735b-6a78-4fb8-a824-1e40138b58d6",'$likess']},
likes:{ $slice: ['$likes', -30, 30]}
}
},
{
"$project":{
likess:0
}
}
])
使用JAVA操作
[java] view plain copy
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
[java] view plain copy
Criteria matchCriteria = new Criteria();
matchCriteria = matchCriteria.where("status").is(0);
DBObject condition = new BasicDBObject();
condition.put("$eq",new Object[]{"$$comment.status",0});
Aggregation agg = newAggregation(
match(matchCriteria),
sort(new Sort(new Sort.Order(Sort.Direction.DESC, "createDate"))),
skip(skip),
limit(limit),
project("createUserHeadurl","content","messageType","createDate","likes","createUserId","createUserName","nowDate","picUrls","status")
.andExpression("likes.createUserId").as("likess")
.andExpression("filter(comments,[0],[1])","comment",condition).as("comments"),
project("createUserHeadurl","content","messageType","createDate","createUserId","createUserName","nowDate","picUrls","status","likess")
.andExpression("size(likes)").as("likeCount")
.andExpression("in([0],likess)",currentUserId).as("userLike")
.andExpression("slice(likes,-30,30)").as("likes")
.andExpression("slice(comments,-30,30)").as("comments")
);
AggregationResults<MessageDto> results = cloudChairmanMongo.aggregate(agg,"message",MessageDto.class);
List<MessageDto> list = results.getMappedResults();
查询最近30条点赞和评论信息
脚本
[javascript] view plain copy
db.message.aggregate([
{$addFields:{allComments:{$concatArrays:['$comments','$likes']}}},
{$unwind:{path:"$allComments"}},
{$project:{allComments:1,
"content":1,
"picUrls":1,
"status":1
}},
{$sort:{"allComments.createDate":-1}},
{$match:{"allComments.status":0,"status":0}},
{$limit:30}
])
java代码
[html] view plain copy
Criteria matchCriteria = new Criteria();
matchCriteria = matchCriteria.where("status").is(0).and("allComments.status").is(0);
Aggregation agg = newAggregation(
project("content","createDate","createUserHeadurl","createUserId","createUserName","messageType","status","picUrls")
.andExpression("concatArrays(comments,likes)").as("allComments"),
unwind("allComments"),
match(matchCriteria),
sort(new Sort(new Sort.Order(Sort.Direction.DESC, "allComments.createDate"))),
skip(skip),
limit(limit)
);
AggregationResults<MessageWithCommentDto> results = cloudChairmanMongo.aggregate(agg,"message",MessageWithCommentDto.class);
List<MessageWithCommentDto> list = results.getMappedResults();
获取评论和点赞个数
java代码
[java] view plain copy
Criteria matchCriteria = new Criteria();
matchCriteria = matchCriteria.where("status").is(0).and("allComments.status").is(0);
Aggregation agg = newAggregation(
project("status")
.andExpression("concatArrays(comments,likes)").as("allComments"),
unwind("allComments"),
match(criteria),
count().as("count")
);
AggregationResults<DBObject> results = cloudChairmanMongo.aggregate(agg,"message",DBObject.class);
List<DBObject> list = results.getMappedResults();
if(list!=null && list.size()>0){
Integer obj = (Integer) list.get(0).get("count");
return obj;
}
以上是使用还可以使用spring-data-mongoDB操作mongoDB,还可以使用原生java操作mongoDB
原生java操作mongoDB
[java] view plain copy
import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
[java] view plain copy
List<DBObject> pipelines = new ArrayList<>();
DBObject match = new BasicDBObject();
DBObject sort = new BasicDBObject();
DBObject skip = new BasicDBObject();
DBObject limit = new BasicDBObject();
DBObject project = new BasicDBObject();
DBObject addFields = new BasicDBObject();
DBObject reProject = new BasicDBObject();
DBObject matchCondition = new BasicDBObject();
matchCondition.put("status",0);
matchCondition.put("messageType",2);
match.put("$match", matchCondition);
DBObject sortCondition = new BasicDBObject();
sortCondition.put("createDate",-1);
sort.put("$sort",sortCondition);
skip.put("$skip",80);
limit.put("$limit",10);
DBObject projectCondition = new BasicDBObject();
projectCondition.put("comments",1);
projectCondition.put("content",1);
projectCondition.put("messageType",1);
projectCondition.put("createDate",1);
projectCondition.put("likes",1);
projectCondition.put("createUserId",1);
projectCondition.put("createUserName",1);
projectCondition.put("nowDate",1);
projectCondition.put("picUrls",1);
projectCondition.put("status",1);
projectCondition.put("likess","$likes.createUserId");
project.put("$project",projectCondition);
DBObject addFieldsCondition = new BasicDBObject();
DBObject likeCountSize = new BasicDBObject();
likeCountSize.put("$size","$likes");
DBObject myLikeIn = new BasicDBObject();
myLikeIn.put("$in",new Object[]{
"d98f735b-6a78-4fb8-a824-1e40138b58d6","$likess"
});
DBObject likesSlice = new BasicDBObject();
likesSlice.put("$slice",new Object[]{
"$likes", -30, 30
});
addFieldsCondition.put("likeCount",likeCountSize);
addFieldsCondition.put("myLike",myLikeIn);
addFieldsCondition.put("likes",likesSlice);
addFields.put("$addFields",addFieldsCondition);
DBObject reProjectCondition = new BasicDBObject();
reProjectCondition.put("likess",0);
reProject.put("$project",reProjectCondition);
pipelines.add(match);
pipelines.add(sort);
pipelines.add(skip);
pipelines.add(limit);
pipelines.add(project);
pipelines.add(addFields);
pipelines.add(reProject);
AggregationOutput output = cloudChairmanMongo.getCollection("message").aggregate(pipelines);
String list = null;
for (Iterator<DBObject> it = output.results().iterator(); it.hasNext(); ) {
BasicDBObject dbo = (BasicDBObject) it.next();
log.info(dbo.toString());
}
投影查询
有时只要部分部分数据,如果其他属性的数据来很多,投影查询可以减少数据流量和实例化工作
[java] view plain copy
import com.alibaba.fastjson.JSON;
[html] view plain copy
DBObject projection = new BasicDBObject();