• MongoDB中的嵌套List操作


    前言

    MongoDB区别Mysql的地方,就是MongoDB支持文档嵌套,比如最近业务中就有一个在音频转写结果中进行对话场景,一个音频中对应多轮对话,这些音频数据和对话信息就存储在MongoDB中文档中。集合结构大致如下

    1. {
    2. "_id":23424234234324234,
    3. "audioId": 2689944,
    4. "contextId": "cht000d24ab@dx187d1168a449a4b540",
    5. "dialogues": [{
    6. "ask": "今天是礼拜天?",
    7. "answer": "是的",
    8. "createTime": 1697356990966
    9. }, {
    10. "ask": "你也要加油哈",
    11. "answer": "奥利给!",
    12. "createTime": 1697378011483
    13. }, {
    14. "ask": "下周见",
    15. "answer": "拜拜!",
    16. "createTime": 1697378072063
    17. }]
    18. }

    下面简单介绍几个业务中用到的简单操作。

    查询嵌套List的长度大小

    1. public Integer getDialoguesSize(Long audioId) {
    2. Integer datasSize = 0;
    3. List group = Arrays.asList(
    4. new Document("$match",
    5. new Document("audioId",
    6. new Document("$eq", audioId)
    7. )
    8. ), new Document("$match",
    9. new Document("dialogues",
    10. new Document("$exists", true)
    11. )
    12. ), new Document("$project",
    13. new Document("datasSize",
    14. new Document("$size", "$dialogues"))
    15. )
    16. );
    17. AggregateIterable aggregate = generalCollection.aggregate(group);
    18. Document document = aggregate.first();
    19. if (document != null) {
    20. datasSize = (Integer) document.get("datasSize");
    21. }
    22. return datasSize;
    23. }

    根据嵌套List中属性查询

    下面的代码主要查询指定audioId中的dialogues集合中小于createTime,并且根据limit分页查询,这里用到了MongoDB中的Aggregates和unwind来进行聚合查询,具体使用细节,可以参见MongoDB官方文档

    1. public AIDialoguesResultDTO queryAiResult(Long audioId, Long createTime, Integer limit) {
    2. AIDialoguesResultDTO aiDialoguesResultDTO = new AIDialoguesResultDTO();
    3. List pipeline = Arrays.asList(
    4. Aggregates.match(Filters.eq("audioId", audioId)),
    5. Aggregates.unwind("$dialogues"),
    6. Aggregates.match(Filters.lt("dialogues.createTime", createTime)),
    7. Aggregates.sort(Sorts.descending("dialogues.createTime")),
    8. Aggregates.limit(limit)
    9. );
    10. AggregateIterable aggregate = generalCollection.aggregate(pipeline);
    11. List aiDialoguesResultList = new ArrayList<>();
    12. String contextId = Constant.EMPTY_STR;
    13. for (Document document : aggregate) {
    14. AIDialoguesResult aiDialoguesResult = new AIDialoguesResult();
    15. List key = Collections.singletonList("dialogues");
    16. aiDialoguesResult.setAnswer(document.getEmbedded(key, Document.class).getString("answer"));
    17. aiDialoguesResult.setAsk(document.getEmbedded(key, Document.class).getString("ask"));
    18. aiDialoguesResult.setCreateTime(document.getEmbedded(key, Document.class).getLong("createTime"));
    19. aiDialoguesResultList.add(aiDialoguesResult);
    20. contextId = document.getString("contextId");
    21. }
    22. if (!CollectionUtils.isEmpty(aiDialoguesResultList)) {
    23. aiDialoguesResultList = aiDialoguesResultList.stream().sorted(Comparator.comparingLong(AIDialoguesResult::getCreateTime)).collect(Collectors.toList());
    24. }
    25. aiDialoguesResultDTO.setCount(aiDialoguesResultList.size());
    26. aiDialoguesResultDTO.setContextId(contextId);
    27. aiDialoguesResultDTO.setResult(aiDialoguesResultList);
    28. return aiDialoguesResultDTO;
    29. }

    当然,我们还有一种比较简单的写法

    1. public AIDialoguesResultDTO queryAiResultBackupVersion(Long audioId, Long createTime, Integer limit) {
    2. Bson query = and(eq("audioId", audioId));
    3. AITextResult aiTextResult = mongoDao.findSingle(query, AITextResult.class);
    4. AIDialoguesResultDTO aiDialoguesResultDTO = new AIDialoguesResultDTO();
    5. if (Objects.isNull(aiTextResult)) {
    6. aiDialoguesResultDTO.setResult(Collections.emptyList());
    7. aiDialoguesResultDTO.setCount(0);
    8. aiDialoguesResultDTO.setContextId("");
    9. }
    10. List aiDialoguesResultList = aiTextResult.getDialogues();
    11. if (CollectionUtils.isEmpty(aiDialoguesResultList)) {
    12. return aiDialoguesResultDTO;
    13. }
    14. Long finalCreateTime = createTime;
    15. List afterFilterAiDialoguesResultList =
    16. aiDialoguesResultList.stream().filter(t -> t.getCreateTime()
    17. < finalCreateTime).sorted(Comparator.comparingLong(AIDialoguesResult::getCreateTime).reversed())
    18. .limit(limit).collect(Collectors.toList());
    19. if (CollectionUtils.isEmpty(afterFilterAiDialoguesResultList)) {
    20. aiDialoguesResultDTO.setCount(0);
    21. } else {
    22. aiDialoguesResultDTO.setCount(afterFilterAiDialoguesResultList.size());
    23. }
    24. afterFilterAiDialoguesResultList = afterFilterAiDialoguesResultList.
    25. stream().sorted(Comparator.comparingLong(AIDialoguesResult::getCreateTime)).collect(Collectors.toList());
    26. aiDialoguesResultDTO.setResult(afterFilterAiDialoguesResultList);
    27. aiDialoguesResultDTO.setContextId(aiTextResult.getContextId());
    28. return aiDialoguesResultDTO;
    29. }

    上面这种写法比较直接,就是直接audioId进行匹配查询, 然后将当前文档中的dialogues全部加载到内存中,然后在内存中进行排序,分页返回,显然如果dialogues集合长度很大,对内存占用会比较高。

    嵌套List的增量追加

    对于dialogues数组,如果我们要向dialogues追加元素,我们可以把audioId对应的dialogues全部取出来,然后在List后面追加一个元素,大致代码如下

    1. public void saveAiResult(SaveAIResultDTO saveAIResultDTO) {
    2. Long audioId = saveAIResultDTO.getAudioId();
    3. Bson filter = Filters.eq("audioId", audioId);
    4. AITextResult aiTextResult = mongoDao.findSingle(filter, AITextResult.class);
    5. if (Objects.isNull(aiTextResult)) {
    6. aiTextResult = AITextResult.buildAiTextResult(saveAIResultDTO);
    7. mongoDao.saveOrUpdate(aiTextResult);
    8. return;
    9. }
    10. List aiDialoguesResults = aiTextResult.getDialogues();
    11. AIDialoguesResult aiDialoguesResult = new AIDialoguesResult();
    12. aiDialoguesResult.setCreateTime(new Date().getTime());
    13. aiDialoguesResult.setAsk(saveAIResultDTO.getAsk());
    14. aiDialoguesResult.setAnswer(saveAIResultDTO.getAnswer());
    15. aiDialoguesResults.add(aiDialoguesResult);
    16. aiTextResult.setDialogues(aiDialoguesResults);
    17. mongoDao.saveOrUpdate(aiTextResult);
    18. }

    上面这种写法本身没有什么问题,但是如果dialogues集合大小比较大,每次追加都将dialogues全部取出来进行追加操作,可能比较占用内存,我们可以利用MongoDB中的push操作,直接追加

    1. public void saveAiResultIncremental(SaveAIResultDTO saveAIResultDTO) {
    2. Long audioId = saveAIResultDTO.getAudioId();
    3. Document query = new Document("audioId", audioId);
    4. Bson projection = Projections.fields(Projections.include("contextId"), Projections.excludeId());
    5. FindIterable result = generalCollection.find(query).projection(projection);
    6. AITextResult aiTextResult;
    7. if (!result.iterator().hasNext()) {
    8. aiTextResult = AITextResult.buildAiTextResult(saveAIResultDTO);
    9. mongoDao.saveOrUpdate(aiTextResult);
    10. return;
    11. }
    12. AIDialoguesResult aiDialoguesResult = new AIDialoguesResult();
    13. aiDialoguesResult.setCreateTime(new Date().getTime());
    14. aiDialoguesResult.setAsk(saveAIResultDTO.getAsk());
    15. aiDialoguesResult.setAnswer(saveAIResultDTO.getAnswer());
    16. Bson update = push("dialogues", aiDialoguesResult);
    17. Bson filter = Filters.eq("audioId", audioId);
    18. generalCollection.updateOne(filter, update);
    19. }

    总结

    既然选择了MongoDB,就不能继续沿用Mysql的查询风格,要学会利用MongoDB的特性,否则往往达不到预期效果。

  • 相关阅读:
    2.4GHz、DA14530-00000FX2射频收发器/LSM6DSOTR 6 轴运动传感器/SKY66423-11射频前端 860MHz 至 930MHz
    GMSM,CSM总结
    如何从 PDF文档中删除页面
    SpringBoot死信队列、延迟队列
    谈谈高并发系统的一些解决方案
    Spring 事务(Transaction)的简介说明
    java ftputils 模拟测试方法 有效
    在nvidia-docker容器中测试TensorFlow-Slim 训练图像分类模型
    es6 Set和Map方法
    面试面经|Java面试mybatis面试题
  • 原文地址:https://blog.csdn.net/qq_28165595/article/details/133848595