mongodb常见疑问

本文主要是分享一些关于使用mongodb方面的问题,后续会慢慢补充。如还不了解mongodb,请参阅《入门手册》。

目录

1. 如何快速熟悉各种命令?

2. 如何处理文档的引用关系?

3. 不支持事务,是不是就不可接受?

4. ObjectId是个什么东东?

5. 有Int的自动增长类型吗?

6.库名文档名命名规范


1. 如何快速熟悉各种命令?

使用runCommand执行,可参考7.5

2. 如何处理文档的引用关系?

MongoDB 中的文档各种关系,官方推荐的思想是整存整取(内嵌文档),但如果内嵌文档在不断增加,数据量不断变大,会影响读写性能。所有引用式关系也是必要的,可通过引用文档的 id 字段来建立关系。

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address_ids": [
      ObjectId("52ffc4a5d85242602e000000"),
      ObjectId("52ffc4a5d85242602e000001")
   ]
}
var result = db.users.findOne({"name":"Tom Benzamin"},{"address_ids":1})
var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})

3. 不支持事务,是不是就不可接受?

mongodb不支持事务,但是mongodb提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操作。所谓原子操作就是要么这个文档保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的情况。

4. ObjectId是个什么东东?

ObjectId 是一个12字节 BSON 类型数据,有以下格式:

  • 前4个字节表示时间戳
  • 接下来的3个字节是机器标识码
  • 紧接的两个字节由进程id组成(PID)
  • 最后三个字节是随机数。

MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。

5. 有Int的自动增长类型吗?

MongoDB 没有像 SQL 一样有自动增长的功能,但在某些情况下,我们可能需要实现 ObjectId 自动增长功能。

//切换数据库
use mydb
//创建文档idGenarator
db.idGenarator.insert({_id:"transProductId",sequence_value:0})
db.idGenarator.find()    //查询document
//创建函数
db.system.js.insert({
	_id: "getNextSequenceValue",
	value: function getNextSequenceValue(sequenceName) {
		var sequenceDocument = db.idGenarator.findAndModify({
				query: {
					_id: sequenceName
				},
				update: {
					$inc: {
						sequence_value: 1
					}
				},
				new: true
			});
		return sequenceDocument.sequence_value;
	}
})
db.system.js.find().pretty()    //查看函数创建信息
getNextSequenceValue('transProductId');    //执行或使用db.eval
//插入时_id自增
db.products.insert({"_id":getNextSequenceValue("productid"),"product_name":"Samsung S3"})

方式1

按这样的思路去实现,如何使用呢?直接执行函数,参阅官方“Script Operations

ScriptOperations operations = mongoTemplate.scriptOps();
Object result = operations.call("getNextSequenceValue", "transProductId");
System.out.println("result:" + result);

在我本地是可以运行的,但是在我们测试环境【db.version】->3.2.12,会报这样的一个错:

not authorized on omsopa to execute command { $eval: "getNextSequenceValue('transProductId')", args: [] }'

最后反复和dba确认,用户权限也是没有问题的。

方式2

直接使用mongoTemplate.findAndModify

@Component
public class IdGenaratorVisitor {
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 获取自增Id
     *
     * @param groupName
     * @return
     */
    public Long getNextId(String groupName) {
        Query query = new Query(Criteria.where(IdGenaratorDomain.ID_COL_NAME).is(IdGroup.TransProduct.getName()));
        Update update = new Update();
        update.inc(IdGenaratorDomain.SEQ_COL_NAME, 1);
        FindAndModifyOptions options = new FindAndModifyOptions();
        options.upsert(true);
        options.returnNew(true);
        IdGenaratorDomain seq = mongoTemplate.findAndModify(query, update, options, IdGenaratorDomain.class);
        return seq.getSequenceValue();
    }


    @Document(collection = "idGenarator")
    public class IdGenaratorDomain {
        private static final String ID_COL_NAME = "_id";
        private static final String SEQ_COL_NAME = "sequence_value";
        @org.springframework.data.mongodb.core.mapping.Field(SEQ_COL_NAME)
        private Long sequenceValue;

        public Long getSequenceValue() {
            return sequenceValue;
        }

        public void setSequenceValue(Long sequenceValue) {
            this.sequenceValue = sequenceValue;
        }
    }
}

在jpa中如何实现呢?

Spring Data MongoDB是有生命周期的,通过继承AbstractMongoEventListener类来实现监听,它内部有onBeforeConvert,onBeforeSave,onAfterSave,onAfterLoad,onAfterConvert,onAfterDelete,onBeforeDelete。

/**
 * 如果需要生成自增id可以标注
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface IdAutoInc {
    IdGroup value();
}
/**
 * id枚举值:每个枚举值都会从1开始生成
 */
public enum IdGroup {
    TransProduct;

    public String getName() {
        switch (this.ordinal()) {
            case 0:
                return "transProductId";
            default:
                return "";
        }
    }
}
/**
 * id生成document类型约束,需继承才会执行生成
 */
public interface IdIncDomain {
}
/**
 * 处理Mongo document新增id自增问题
 */
@Component
public class MongoFieldFillOnSave extends AbstractMongoEventListener<Object> {
    private static Map<String, IdField> idCachePool = Maps.newConcurrentMap();
    @Autowired
    private IdGenaratorVisitor idGenaratorVisitor;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) {
        Object source = event.getSource();
        if (source != null) {
            Class<?> sourceClz = source.getClass();
            IdField idFld = idCachePool.get(sourceClz.getName());
            if (idFld == null) {
                //类型规约
                if (!ClassUtils.getAllInterfacesForClassAsSet(Student.class).contains(IdIncDomain.class)){
                    return;
                }
                ReflectionUtils.doWithFields(source.getClass(), new ReflectionUtils.FieldCallback() {
                    @Override
                    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                        ReflectionUtils.makeAccessible(field);
                        IdAutoInc idAnno = field.getAnnotation(IdAutoInc.class);
                        if (idAnno != null) {
                            IdField idField = new IdField(field, idAnno.value());
                            idFieldFill(source, idField);
                            idCachePool.putIfAbsent(sourceClz.getName(), idField);
                        }
                    }
                });
            } else {
                idFieldFill(source, idFld);
            }
        }
    }
    private void idFieldFill(Object source, IdField idFld) {
        try {
            Long id = idGenaratorVisitor.getNextId(idFld.getIdGroup().getName());
            ReflectionUtils.makeAccessible(idFld.getField());
            idFld.getField().set(source, id);
        } catch (IllegalAccessException e) {
            throw new BusinessRuntimeException("mongo ID AutoGenarator error:" + e.getMessage());
        }
    }

    /**
     * field缓存实例
     */
    public class IdField {
        private Field field;
        private IdGroup idGroup;
        public IdField(Field field, IdGroup idGroup) {
            this.field = field;
            this.idGroup = idGroup;
        }
        public Field getField() {
            return field;
        }
        public void setField(Field field) {
            this.field = field;
        }
        public IdGroup getIdGroup() {
            return idGroup;
        }
        public void setIdGroup(IdGroup idGroup) {
            this.idGroup = idGroup;
        }
    }
}
/**
 *
 * test
 */
@Document(collection = "myStudent")
public class Student implements Serializable,IdIncDomain {
    @IdAutoInc(IdGroup.TransProduct)
    @Id
    private long id;
    private String name;

    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public interface  StudentRepository extends MongoRepository<Student, Long>{
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class Mongo_test {   
    @Autowired
    private StudentRepository studentRepository;
    @Test
    public void go_insert() {
        for (int i = 0; i < 2; i++) {
            Student student = new Student();
            student.setName("alex" + i);
            Student result = studentRepository.save(student);
            System.out.println("result:" + result.getId());
        }
    }
}

6.库名文档名命名规范

数据库命名

  • 不能为空字符串(" ")
  • 不能$开头
  • 不得含有' '(空格)、.、$、/、\和\0 (空字符)
  • 数据库名区分大小写(建议数据库名全部使用小写)
  • 数据库名最长为64个字节
  • 不要与系统保留的数据库名相同,这写数据库包括:admin,local,config等

集合命名

  • 集合名不能为空字符串(" ")
  • 不能包含\0或空字符,这个字符表示键的结尾
  • 集合名不能包含$字符(注:可包含 . 点号),除非你要访问这种系统创建的集合
  • 集合名不能以"system."开头,此前缀是系统本身保留的

命名空间

  • 命名空间的长度不可超过100个字节
  • 组织子集合的方式就是使用“.”,分隔不同命名空间的子集合

在MongoDB中,为了使组织结构更清晰更方便管理,使用子集合来组织数据非常高效,值得推荐。比如一个具有博客功能可能包含两个集合,分别是blog.posts和blog.authors,但blog集合(这个集合甚至不需要存在)跟它的两个子集合没有任何关系。

文件键命名

  • _id 是系统保留关键字, 是默认主键, 在集合中必需唯一, 且不可更改.
  • 下划线"_"开头的键是保留的(不是严格要求的)
  • 键不能包含\0或空字符,这个字符表示键的结尾
  • .和$尽量不要用,只有在特定环境下才能使用
  • 分大小写且不能重复,如:{Aaa:1, aaa:2}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于 MongoDB 常见面试题,我可以为您提供以下问题和答案: 1. MongoDB 是什么?它与传统的关系型数据库有什么不同? 答:MongoDB 是一个开源的 NoSQL 数据库,它使用文档存储数据,而不是传统的表格。与关系型数据库相比,MongoDB 更加灵活,可以处理非结构化数据,并且具有更好的可扩展性和性能。 2. MongoDB 中的文档是什么?它们有哪些特点? 答:MongoDB 中的文档是一组键值对,类似于 JSON 对象。它们可以包含不同类型的值,如字符串、数字、日期、数组等。文档可以嵌套,这意味着一个文档可以包含另一个文档作为其值。文档还具有动态模式,这意味着不同的文档可以具有不同的字段。 3. MongoDB 中的集合是什么?它们与传统数据库中的表格有什么不同? 答:MongoDB 中的集合是一组文档,类似于关系型数据库中的表格。但是,与表格不同的是,集合中的文档可以具有不同的结构和字段。这使得 MongoDB 更加灵活,并且可以处理非结构化数据。 4. MongoDB 中的索引是什么?它们有什么作用? 答:MongoDB 中的索引是一种数据结构,用于加速查询操作。它们可以提高查询性能,并且可以帮助 MongoDB 在大型数据集上进行快速搜索。MongoDB 支持多种类型的索引,包括单字段索引、复合索引、全文索引等。 5. MongoDB 中的聚合管道是什么?它们有什么作用? 答:MongoDB 中的聚合管道是一种数据处理工具,用于对文档进行聚合操作。它们可以将多个操作组合在一起,以便在单个查询中执行多个操作。聚合管道可以用于数据分析、数据清洗、数据转换等多种场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值