MongoDB入门与实战-第七章-MongoDB内嵌文档

参考

MongoDB更新修改内嵌文档操作

概述

MongoDB是文档型的数据库系统,doc是MongoDB的数据单位,每个doc相当于关系型数据库的数据行(row),doc和row的区别在于field的原子性:row中的column是不和分割的原子对象,而doc中的field可以是原子对象,也可以是内嵌doc(embedded doc),数组等数据类型。内嵌doc中所有field的Key不允许重复。

{
    
    
	name:"t1",
	age:21,
	contact: {
    
    
        phone:123,
        email:"[email protected]"
    }
}

简单查询

在引用内嵌doc中的field时,使用 dot notation,格式是:embedded_doc.field:value,如果内嵌doc的field也是内嵌文档,依次类推,embedded_doc1.embedded_doc2.field:value。

示例,查询contact 字段中phone是123的所有doc。

db.foo.find({
    
    "contact.phone":123})

限制返回的doc的field

示例,使用find的第二个参数Projection doc,只返回两个field:name 字段和内嵌doc的email字段。通过dot notation,将内嵌doc中的某些字段返回,格式是:embedded_doc.field:1,表示返回该字段,embedded_doc.field:0,表示不返回该字段。

db.foo.find({
    
    "contact.phone":123},{
    
    _id:0,"contact.email":1,name:1})

修改内嵌doc

upsert 选项非常有用,如果当前的doc中不存在内嵌文档,通过 s e t m o d i f i e r 来增加;如果当前的 d o c 中存在内嵌文档,通过 set modifier 来增加;如果当前的doc中存在内嵌文档,通过 setmodifier来增加;如果当前的doc中存在内嵌文档,通过set modifier来修改内嵌文档的值。

1,修改doc,增加内嵌doc

示例,增加address字段,这是内嵌doc

db.foo.updateMany(
{
    
    name:"t1"},
{
    
    $set:{
    
    address:{
    
    province:"henan",city:"xinyang"}}},
{
    
    upsert:true}
)

2,修改内嵌doc中的字段

示例,修改内嵌doc中province 和 city 字段的内容,全部修改为"shanghai"

db.foo.updateMany(
{
    
    name:"t1"},
{
    
    $set:{
    
    address:{
    
    province:"shanghai",city:"shanghai"}}},
{
    
    upsert:true}
)

上面方法会将整个内嵌文档替换,如果只是修改指定字段则使用如下

db.foo.update(
  {
    
    name: 't1'}, 
  {
    
    '$set': {
    
    'address.province': "广东"}}
);

3,如果是数组

# 在爱好上追加,如果多次执行会重复
db.col.update({
    
    name:"龙猫不热"},{
    
    $push:{
    
    "hobby.movies":"平凡的世界"}})
# 多次执行不会重复添加
db.col.update({
    
    name:"龙猫不热"},{
    
    $addToSet:{
    
    "hobby.movies":"平凡的世界"}})

删除doc中的内嵌doc

KaTeX parse error: Expected '}', got 'EOF' at end of input: …除doc中的字段,使用格式:{ unset:{field1:“”, field2:“”}},将删除的字段放在$unset文档中。

1,如果要删除内嵌doc中的field或数组中的元素,可以使用dot notation。

示例,删除address内嵌doc中的province 字段

db.foo.updateMany(
{
    
    name:"t1"},
{
    
    $unset:{
    
    "address.province":""}},
{
    
    upsert:true}
)

2,如果不使用dot notation,那么删除的将是整个内嵌doc

示例,在$unset modifier中,使用address 内嵌doc 格式,那么将删除address field。

db.foo.updateMany(
{
    
    name:"t1"},
{
    
    $unset:{
    
    address:{
    
    province:"shanghai"}}},
{
    
    upsert:true}
)

3,数组

$pull: 数组中符合条件的值将被删除
> db.col.update({
    
    name:"龙猫不热"},{
    
    $pull:{
    
    "hobby.movies":"平凡的世界"}})))))

存储

插入嵌入文档,我们采用map和JSONObject两种内嵌文档实现

文档实体定义

@Data
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DeviceV2DTO extends BaseDTO {
    
    

    /**
     * 设备编码
     * 该属性对应mongodb的字段的名字,如果一直,则无需要注解
     */
    @NotEmpty(message = "设备编码不能为空")
    @Schema(description = "设备编码")
    private String deviceCode;
    /**
     * 设备类型:1:扬尘监测,2:塔吊,3:升降机,4:AI分析
     */
    @NotNull(message = "设备类型不能为空")
    @Schema(description = "设备类型")
    private Integer deviceType;
    /**
     * 经度 WGS84
     */
    @Schema(description = "经度 WGS84")
    private Double lng;
    /**
     * 纬度 WGS84
     */
    @Schema(description = "纬度 WGS84")
    private Double lat;


    private DeviceAttrDTO installation;
    /* ********************扩展信息********************** */


    private JSONObject extra;
    private Map<String,Object> extraParams;
}

插入

 @Test
    @SneakyThrows
    public void insertDevice(){
    
    
        DeviceV2DTO device = new DeviceV2DTO();
        device.setDeviceCode("0001");
        device.setLng(108.12);
        device.setLat(22.33);
        device.setActivated(1);
        device.setActiveTime(DateUtil.date());
        device.setHeartBeatInterval(60);

		// 内嵌文档-安装信息
        DeviceAttrDTO installation = new DeviceAttrDTO();
        installation.setInstallationUnit("业");
        installation.setInstallationUnitCode("001");
        installation.setInstallationTime("2022-01-21");
        device.setInstallation(installation);

		// 内嵌文档-扩展属性
        JSONObject extra = new JSONObject();
        extra.put("sim1","13224521245");
        extra.put("num1",123);
        device.setExtra(extra);
		
		// 内嵌文档-扩展属性2
        Map<String,Object> extraParams = new TreeMap<>();
        extraParams.put("name1","test");
        extraParams.put("value1",12.3);
        device.setExtraParams(extraParams);

        BulkOperations operations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "device");
        List<DeviceV2DTO> rows = new ArrayList<>();
        rows.add(device);
        operations.insert(rows);
        BulkWriteResult result = operations.execute();
        result.getInserts().forEach(i->{
    
    
            String id = i.getId().asObjectId().getValue().toString();
            System.out.println("id:"+id);
        });
    }

存入效果

在这里插入图片描述

查询

通过map和JSONObject接受内嵌文档

		Query query = new Query(Criteria.where("id").is("63d6299f74acce5e8fb93457"));
        DeviceV2DTO data =  mongoTemplate.findOne(query, DeviceV2DTO.class,"iot_device");
        System.out.println(JsonUtil.toJson(data));
        assert data != null;
        for(Map.Entry<String,Object> entry : data.getExtra().entrySet()){
    
    
            System.out.println(entry.getKey() + "=" +entry.getValue());
        }
        if(data.getExtraParams() != null){
    
    
            for(Map.Entry<String,Object> entry : data.getExtraParams().entrySet()){
    
    
                System.out.println(entry.getKey() + "=" +entry.getValue());
            }
        }

通过实体接受内嵌文档

实体:

@Data
@EqualsAndHashCode(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DeviceV3DTO extends BaseDTO {
    
    

    /**
     * 设备编码
     * 该属性对应mongodb的字段的名字,如果一直,则无需要注解
     */
    @NotEmpty(message = "设备编码不能为空")
    @Schema(description = "设备编码")
    private String deviceCode;
    /**
     * 经度 WGS84
     */
    @Schema(description = "经度 WGS84")
    private Double lng;
    /**
     * 纬度 WGS84
     */
    @Schema(description = "纬度 WGS84")
    private Double lat;


    private DeviceAttrDTO installation;
    /* ********************扩展信息********************** */


    private DeviceExtraDTO extra;
    private DeviceExtraParamsDTO extraParams;
DeviceV3DTO dataV3 =  mongoTemplate.findOne(query, DeviceV3DTO.class,"iot_device");
 System.out.println(JsonUtil.toJson(dataV3));

注意:如果将来需要序列化到redis,则请不要用map定义内嵌文档,会在redis中出现@Type java.util.Map,导致后期不好序列化

navicat查询

在查询时不要使用json字符串进行查询,在这种具有嵌套文档的集合中,可以使用点符号进行操作

db.testcollection.find({
    
    "extra.maxHeight":2,"extra.banana":3})

更新

内嵌文档更新时需要指定内嵌字段,否则会整个文档替换,比如上面例子中,如果要更新扩展属性,则应该是extra.maxHeight=xxx

public DeviceDTO modifyDevice(DeviceDTO data) {
    
    
        DeviceDTO entity =  get(data.getId());
        if (entity != null) {
    
    
            DeviceDoc newEntity = new DeviceDoc();
            BeanUtil.copyProperties(data, newEntity);
            // 反射获取更新字段
            Update update = getUpdate(newEntity);
            // 更新字段
            BulkOperations operations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, DeviceDoc.class);
            operations.updateOne(Query.query(Criteria.where("id").is(data.getId())), update);
            operations.execute();
        }
        return null;
    }

public Update getUpdate(DeviceDoc entity){
    
    
        // 复制属性
        entity.doBeforeUpdate();
        StringBuilder updateSql = new StringBuilder();

        Update update = new Update();
        Class<? extends DeviceDoc> entityClass = entity.getClass();
        Field[] fields = getAllFields(entityClass);
        for (Field field : fields) {
    
    
            // 判断是否忽略字段
            UpdateIgnoreField property = field.getAnnotation(UpdateIgnoreField.class);
            String fieldName = field.getName();
            field.setAccessible(true);
            Object value = field.get(entity);
            if(property == null && value!= null && !fieldName.equals(IotConstant.FIELD_ID)){
    
    
            	// 判断是内嵌文档扩展属性
                if(field.getType().equals(JSONObject.class) ){
    
    
                    JSONObject jsonObject = (JSONObject) value;
                    for(Map.Entry<String,Object> item: jsonObject.entrySet()){
    
    
                        String key = fieldName+SymbolConstants.DOT+item.getKey();
                        Object v = item.getValue();
                        update.set(key, v);
                        updateSql.append(SymbolConstants.C_COMMA).append(key).append("=").append(v);
                    }

                }else{
    
    
                    update.set(fieldName, value);
                    updateSql.append(SymbolConstants.C_COMMA).append(fieldName).append("=").append(value);
                }
            }
        }
        log.info("修改数据id: {}, value:{}",entity.getId(), updateSql);
        return update;
    }

猜你喜欢

转载自blog.csdn.net/Blueeyedboy521/article/details/128817441