原生语法使用

添加

  • 使用 insert() 或 save() 方法向集合中插入文档

    1
    db.表名.insert(document);
    1
    2
    db.hello.insert({"title":"1","likes":1});
    db.hello.save({"title":"2","likes":2});
    1
    2
    3
    // 指定id更新对应id的数据
    db.hello.save({"title":"6","likes":6});
    db.hello.find({"title":"6"});

    image-20210301170747281

    1
    db.hello.save({'_id':ObjectId("603caed56a230000ef001b3e"),"title":"6","likes":12})

    image-20210301170846969

  • 如果使用save(),不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据;

修改

  • 使用insert()或save()方法来更新集合中的文档。

    1
    2
    3
    4
    5
    6
    7
    8
    db.col.update(    
    <query>, // update的查询条件
    <update>, // sql update查询内set后面的
    {
    upsert: <boolean>, // 可选,默认false,如果为true,当update数据不存在时则插入新数据
    multi: <boolean> // 可选,默认false,只更新条件找到的第一条记录,如果未true,更新所有
    }
    )
1
2
3
4
5
6
7
8
9
10
db.hello.update({
"title": '6'
}, {
$set: {
"likes": 18
}
}, {
upsert: false,
multi: false
});

image-20210301171234675

1
2
3
4
5
6
7
8
9
10
11
// 9不存在 执行后插入一条新纪录
db.hello.update({
"title": '9'
}, {
$set: {
"likes": 9
}
}, {
upsert: true,
multi: false
});

image-20210301171523905

1
2
3
4
5
6
7
8
9
10
11
// 7存在多条 likes会批量更新成14
db.hello.update({
"title": '7'
}, {
$set: {
"likes": 14
}
}, {
upsert: false,
multi: true
});

image-20210301171655808

删除

1
2
3
4
5
6
db.collection.remove(     
<query>,
{
justOne: <boolean> // 默认false删除所有,true只删除一条
}
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 只删除一条 
db.hello.remove(
{
"title": "8"
},
{
justOne: true
}
);
// 删除所有
db.hello.remove(
{
"title": "8"
},
{
justOne: false
}
);

image-20210301172018087image-20210301172046889

查询

1
2
3
4
db.collection.find(
<query>, // 可选,使用查询操作符指定查询条件
<projection> // 可选,使用投影操作符指定返回的键
)
1
2
// projection:key:1/0,当key为1时显示该字段否则隐藏
db.hello.find({"title":"3"},{"title":1,"likes":1})

比较

操作 格式 eg. sql
等于 {<key>:<value>} db.hello.find({"title":"MongoDB"}).pretty() where title = 'MongoDB'
小于 {<key>:{$lt:<value>}} db.hello.find({"likes":{$lt:50}}).pretty() where likes < 50
小于或等于 {<key>:{$lte:<value>}} db.hello.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.hello.find({"likes":{$gt:50}}).pretty() where likes > 50
大于或等于 {<key>:{$gte:<value>}} db.hello.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.hello.find({"likes":{$ne:50}}).pretty() where likes != 50

AND和Or

1
2
// And操作
db.col.find({key1:value1, key2:value2}); // 方法可以传入多个键(key),每个键(key)以逗号隔开
1
2
3
4
5
6
7
8
// Or操作
db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
)
1
2
3
4
5
6
db.hello.find({
$or: [
{ "likes": { $gt: 10 } },
{ "likes": { $lte: 2 } }
]
})

image-20210301174907166

Limit和Skip的分页

1
2
3
db.col.find().limit(NUMBER).skip(NUMBER); // skip的默认方法为0

db.hello.find().limit(2).skip(1);

image-20210301175039354

Sort排序

1
2
db.col.find().sort({KEY:1}) // Key为需要排序的字段,1正序,-1倒叙
db.hello.find().sort({"likes":1});

image-20210301175145112

  • 当limit,skip,sort同时使用,执行顺序是sort>skip>limit

索引操作

1
2
3
4
db.collection.createIndex(
<keys>, // 创建索引的字段,{key1,key2}复合索引
<options> // 1正序,-1倒序
)
参数 解释
background true/false 默认false,当为true后台执行
unique true/false 默认false,当为true指定唯一索引
name string 索引自定义名字,未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。

Java应用

  • 添加数据源依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
  • 添加配置

    1
    2
    3
    4
    5
    spring:
    data:
    mongodb:
    uri: mongodb://xxxxxx
    database: test
  • 更改实体bean 添加@Document("集合名")注解,方便后续使用

  • 引入MongoTemplate

简单查询

  • 构建查询条件

    1
    Query query = new Query();
  • 查询的两个常用with()方法

    • 分页

      image-20210301140409572

      1
      2
      3
      4
      // 1. 只分页
      query.with(PageRequest.of(pageNumber, pageSize));
      // 2. 分页+排序
      query.with(PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "排序字段")));
    • 排序

      image-20210301140426071

      1
      2
      // Sort.Direction 区分正序还是倒序
      query.with(Sort.by(Sort.Direction.DESC, "排序字段"));
  • 添加查询条件

    1
    2
    3
    4
    // 1. 初始化
    Query query = new Query(Criteria.where(""));
    // 2. add
    query.addCriteria(Criteria.where(""));
    Java Sql
    Criteria.where(“id”).is(id); id = id
    Pattern.compile(“^.“ + text + “.$”, Pattern.CASE_INSENSITIVE);
    Criteria.where(“id”).regex(pattern);
    id like ‘%text%’
    Criteria.and(“createAt”).gte(startTime).lte(endTime); createAt >= startTime and createAt <= endTime

| Criteria.and(“status”).nin(“SUCCESS”, “FAILURE”);
Criteria.and(“status”).in(“SUCCESS”); | status not in (“SUCCESS”, “FAILURE”)
status in (“SUCCESS”) |
| Criteria.orOperator(Criteria.where(“orderId”).is(1), Criteria.where(“externalOrderId”).is(2)); | and (orderId = 1 or externalOrderId = 2) |

  • 执行查询

    1
    mongoTemplate.find(query, xxx.class);

聚合查询

  • 分页

    1
    2
    3
    4
    5
    List<AggregationOperation> operations = new ArrayList<>(2);
    operations.add(Aggregation.skip((Math.max(pageNo, 1) - 1) * pageSize));
    operations.add(Aggregation.limit(pageSize));
    // 执行查询获取结果
    mongoTemplate.aggregate(Aggregation.newAggregation(inClazz, operations), outClazz).getMappedResults();
  • 为聚合查询添加查询条件

    1
    2
    3
    4
    5
    // 通过Aggregation.match添加查询条件,因为是作为operations,所以可以传入多个 执行的时候
    List<AggregationOperation> operations = new ArrayList<>();
    operations.add(Aggregation.match(andCriteria));
    ...
    // 执行后的结果如下图

    image-20210301160850024

  • 映射字段,当操作聚合数据的时候,如果涉及到分组求和等操作,这个操作必须进行字段映射,类似于sql的select a,b,c

    1
    Aggregation.project("字段名");
  • project的函数表达式

    1
    2
    3
    4
    // 这个的操作结果相当于 ifnull() 函数
    project.and("rechargeValue").applyCondition(ConditionalOperators.IfNull.ifNull("rechargeValue").thenValueOf("product.rechargeValue")));
    // 相当于 (serPrice - costPrice) as subUserCostPrice
    project.and("userPrice").minus("costPrice").as("subUserCostPrice");
  • Group分组

    1
    2
    3
    4
    5
    6
    7
    // 根据字段分组
    GroupOperation group = Aggregation.group(数组);
    // 求和 a as sumCount
    group.count().as("sumCount");
    // 判断求和 case when then else end
    group.sum(ConditionalOperators.when(Criteria.where("aa").is(RechargeOrderStatusEnum.FAILURE))
    .then(1).otherwise(0)).as("bb");

更新

1
2
3
4
5
6
7
8
9
// 只更新匹配的第一条数据
UpdateResult updateResult = mongoTemplate.updateFirst(查询条件, 更新数据, 集合);
// 更新所有匹配的数据
UpdateResult updateResult = mongoTemplate.updateMulti(查询条件, 更新数据, 集合);
// 更新
Update update = new Update();
// 放入更新的值 相当于 update xxx set a=xxx,b=xxx ....
// values是一个map集合
values.forEach(update::set);

工具类

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import lombok.Getter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import vip.xxx.common.exceptions.ErrorCodeException;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
* Mongo常用操作
* @author ibean
* @date 2021/01/29
*/
@Component
@Getter
public class MongoDbUtils {
@Resource
private MongoTemplate mongoTemplate;

/**
* 保存对象List到指定集合中
* <p>
* 也可以在实体类上使用@Document(collection=“集合名称”)指定集合名称,未指定则默认实体类的类名为集合名称
*
* @param entities 保存数据
*/
public <T> void saveAll(List<T> entities, Class<T> collection) {
if (entities.isEmpty()) {
return;
}
BulkOperations ops = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, collection);
ops.insert(entities).execute();
}


/**
* 根据id倒序查询最后一条记录
*
* @param entity 数据实体
* @param direction 倒序/正序 Direction.DESC/ASC
*/
public <T> T findOneSortById(Class<T> entity, Sort.Direction direction) {
Query query = new Query().with(Sort.by(direction, "id"));
return mongoTemplate.findOne(query, entity);
}

/**
* 根据指定key value到指定集合中查询匹配得数量
*
* @param clazz 集合名称
* @param key key
* @param value value
* @return 结果
*/
public <T> long count(Class<T> clazz, String key, String value) {
Query query = Query.query(Criteria.where(key).is(value));
return mongoTemplate.count(query, clazz);
}

/**
* 指定集合 根据条件查询出符合的第一条数据
*
* @param entity 数据对象
* @param map 条件map Map<条件key,条件value> map
* @param sorts 排序规则
* @return 结果
*/
public <T> T findSortFirst(Class<T> entity, Map<String, Object> map, List<Sort> sorts) {
if (map.containsKey(null)) {
throw new ErrorCodeException("查询条件Key不能为Null");
}
Criteria criteria = getCriteria(map);
Query query = Query.query(criteria);
if (Objects.nonNull(sorts) && !sorts.isEmpty()) {
sorts.forEach(query::with);
}
return mongoTemplate.findOne(query, entity);
}

/**
* 指定集合 修改数据,且修改所找到的所有数据
*
* @param criteria 查询条件
* @param values 修改内容
* @param clazz 集合
* @param updateFirst true修改第一条数据 false修改所有匹配得数据
*/
public <T> UpdateResult updateMulti(Criteria criteria, Map<String, Object> values, Class<T> clazz, boolean updateFirst) {
Query query = Query.query(criteria);
Update update = new Update();
values.forEach(update::set);
UpdateResult updateResult;
if (updateFirst) {
updateResult = mongoTemplate.updateFirst(query, update, clazz);
} else {
updateResult = mongoTemplate.updateMulti(query, update, clazz);
}
return updateResult;
}

/**
* 对数据做聚合处理
*
* @param inClazz 数据实体类
* @param outClazz 结果集类型
* @return 结果
*/
public <T, R> List<R> aggregation(Class<T> inClazz, Class<R> outClazz, List<AggregationOperation> operations) {
Aggregation aggregation = Aggregation.newAggregation(operations);
return Optional.of(mongoTemplate.aggregate(aggregation, inClazz, outClazz).getMappedResults()).orElse(new ArrayList<>());
}

/**
* 分页查询
*
* @param entity 数据实体类
* @param criteria 查询条件
* @param sorts 排序规则
* @param pageNo 当前页 入参从1开始
* @param pageSize 当前页数据条数
* @return 分页数据
*/
public <T> MongoPage<T> page(Class<T> entity, List<Criteria> criteria, List<Sort> sorts, int pageNo, int pageSize) {
Query query = new Query();
criteria.forEach(query::addCriteria);
long count = mongoTemplate.count(query, entity);
int pages = (int) Math.ceil((double) count / (double) pageSize);
pageNo = Math.max(pageNo, 1) - 1;
query.with(PageRequest.of(pageNo, pageSize));
// 放入排序
Optional.ofNullable(sorts).ifPresent(item -> sorts.forEach(query::with));
// 查询数据
List<T> list = mongoTemplate.find(query, entity);
MongoPage<T> page = new MongoPage<>();
page.setPageNumber(pageNo);
page.setPageSize(pageSize);
page.setTotal(count);
page.setPages(pages);
page.setList(list);
return page;
}

/**
* 聚合分页查询
*
* @param operations 查询条件
* @param sorts 排序规则
* @param pageNo 当前页 入参从1开始 不传不分页
* @param pageSize 当前页数据条数 不传不分页
* @return 分页数据
*/
public <T, R> MongoPage<R> aggregationPage(List<AggregationOperation> operations, List<Sort> sorts, Long pageNo, Integer pageSize, Class<T> inClazz, Class<R> outClazz) {
// 查询总数量
List<AggregationOperation> countList = new ArrayList<>(operations);
countList.add(Aggregation.count().as("count"));
// 查询满足条件的总数量
TypedAggregation<T> aggregation = Aggregation.newAggregation(inClazz, countList);
// 总数量
R countRaw = mongoTemplate.aggregate(aggregation, outClazz).getUniqueMappedResult();
Long count = 0L;
if (Objects.nonNull(countRaw)) {
try {
// count public字段 获取聚合后总数据量
count = (Long) countRaw.getClass().getDeclaredField("count").get(countRaw);
} catch (Exception e) {
count = 0L;
}
}
MongoPage<R> page = new MongoPage<>();
// 查询分页后的具体数据
List<R> content = Collections.emptyList();
if (count > 0) {
sorts.forEach(item -> operations.add(Aggregation.sort(item)));
if (Objects.nonNull(pageNo) && Objects.nonNull(pageSize)) {
int pages = (int) Math.ceil((double) count / (double) pageSize);
page.setPageNumber(pageNo.intValue());
page.setPageSize(pageSize);
page.setPages(pages);

// 分页数据
operations.add(Aggregation.skip((Math.max(pageNo, 1) - 1) * pageSize));
operations.add(Aggregation.limit(pageSize));
}
aggregation = Aggregation.newAggregation(inClazz, operations);
// 查询数据
content = mongoTemplate.aggregate(aggregation, outClazz).getMappedResults();
}
page.setTotal(count);
page.setList(content);
return page;
}

/**
* 拼接查询条件
*
* @param map 查询参数
* @return 结果
*/
private Criteria getCriteria(Map<String, Object> map) {
Criteria criteria = new Criteria();
if (map == null) {
return criteria;
}
int i = 0;
for (String key : map.keySet()) {
if (i == 0) {
criteria = Criteria.where(key).is(map.get(key));
i++;
} else {
criteria.and(key).is(map.get(key));
}
}
return criteria;
}

/**
* 返回左右模糊的表达式
*
* @param text 字符串
* @return 结果
*/
public Pattern allLike(String text) {
return Pattern.compile("^.*" + text + ".*$", Pattern.CASE_INSENSITIVE);
}

/**
* 获取类的所有字段名
*
* @param clazz 类
* @param ignoredField 忽略的字段
* @param <T> 类
* @return 字段集合
*/
public <T> String[] getClassProject(Class<T> clazz, List<String> ignoredField) {
return Stream.of(Lists.newArrayList(clazz.getDeclaredFields()), Lists.newArrayList(clazz.getSuperclass().getDeclaredFields()))
.flatMap(Collection::stream).filter(item -> !ignoredField.contains(item.getName())).filter(Objects::nonNull).map(Field::getName)
.distinct().toArray(String[]::new);
}
}
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
import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.util.List;

/**
* mongo分页
*
* @author ibean
* @date 2021/01/29
*/
@Data
@ToString
public class MongoPage<T> implements Serializable {
private static final long serialVersionUID = 4580954926581934608L;
/**
* 当前页
*/
private int pageNumber;
/**
* 每页的数量
*/
private int pageSize;
/**
* 当前页的数量
*/
private Integer size;
/**
* 总记录数
*/
private Long total;
/**
* 总页数
*/
private Integer pages;
/**
* 结果集
*/
private List<T> list;
}