MongoDB聚合搜索Aggregation(时间,模糊匹配,最新值)SpringCloud JAVA

场景:一个设备的molde属性,该属性定义在物模型中,且使用属性通过上报的方式存在设备数据ThingData中,每次都会存一条并记录时间;通过点击传thingId,再通过id调用dubbo访问其他服务根据thingId获取到对应的productId,且productId关联物模型的id,以此拿到物模型定义中的property数组,其中每个property都有一个唯一标识,而属性上报也是根据唯一值来做判断对应的属性的;通过循环property列表拿到标识让后再ThingData文档中先筛选出符合的数据,然后还要根据场景来判断是否需要进行模糊匹配得到所有符合idenfier或value的存在thingData中的最新值;

!!!注意细节:mongo的映射关系,还有文档名必须正确还要注意层级关系!

需求1.多条件查询的难点:的双属性模糊匹配

listThingPropertiesData(String thingId, String keyWords, Integer pageSize, Integer pageNum) {
        ThingModelDTO thingModelData = getThingModelByThingId(thingId);

        // 遍历设备物模型的所有属性,每个属性读取最新值
        List<ThingData.PropertyData> propertiesData = new ArrayList<>();

        // 创建一个空的关键字条件
        Criteria keywordCriteria = new Criteria();

        // 如果有关键字,添加模糊匹配规则
        if (StringUtils.hasText(keyWords)) {
            keywordCriteria = keywordCriteria.orOperator(
                    Criteria.where("property.identifier").regex(keyWords, "i"), // 匹配标识符
                    Criteria.where("property.value").regex(keyWords, "i") // 匹配属性值
            );
        }

        for (ThingModelDTO.PropertyDTO propSpecDTO : propertiesSpec) {
            String propIdentifier = propSpecDTO.getIdentifier();
            // 第一步:构建基本查询条件,根据thingId和属性标识符查询
            Criteria baseCriteria = Criteria.where("thingId").is(thingId)
                    .and("property.identifier").is(propIdentifier);

            Aggregation aggregation = Aggregation.newAggregation(
                    Aggregation.match(baseCriteria),
                    // 如果有关键字,再执行模糊匹配
                    Aggregation.match(keywordCriteria),
                    Aggregation.sort(Sort.by(Sort.Order.desc("property.timestamp"))),
                    Aggregation.group("property.identifier")
                            .first("property.identifier").as("identifier")
                            .first("property.value").as("value")
                            .first("property.timestamp").as("timestamp")
            );

           try {
               AggregationResults<ThingData.PropertyData> aggregationResults = mongoTemplate.aggregate(
                       aggregation,"thingData", ThingData.PropertyData.class);
               List<ThingData.PropertyData> matchingData = aggregationResults.getMappedResults();
               propertiesData.addAll(matchingData);

               // 添加ThingModel中有但ThingData中没有的属性
               if (matchingData.isEmpty()) {
                   propertiesData.add(new ThingData.PropertyData("ThingModel:" + propSpecDTO.getIdentifier(), null, System.currentTimeMillis()));
               }
           }catch (Exception e) {
               log.debug("查询失败! 原因" + e);
               throw new ServiceException(ServiceException.ExceptionType.INTERNAL_FAILURE,
                       ExceptionInfoBuilder.build(ExceptionTemplate.INTERNAL_FAILURE_DATABASE,"聚合查询"));
           }
        }

        List<ThingDataDTO.PropertyDataDTO> propertyDataDTOs = ThingDataMapper.INSTANCE.toPropertyDTO(propertiesData);

        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, propertyDataDTOs.size());

        List<ThingDataDTO.PropertyDataDTO> paginatedList = propertyDataDTOs.stream()
                .skip(startIndex)
                .limit(endIndex - startIndex)
                .collect(Collectors.toList());

        return ResponseDTOBuilder.build(paginatedList, propertyDataDTOs.size(), pageSize, pageNum);
    }
需求2.:多条件查询难点:筛选只符合一个时间段内的最新数据;
listThingServicesData(String thingId, String status, Long startTime, Long endTime, Integer pageSize, Integer pageNum) throws ServiceException {

        // 遍历设备物模型的所有服务,每个服务读取最新值
        List<ThingData.ServiceData> servicesData = new ArrayList<>();
        for (ThingModelDTO.ServiceDTO serviceSpecDTO : servicesSpec) {
            String serviceIdentifier = serviceSpecDTO.getIdentifier();
            // 构建基本查询条件,根据thingId和服务标识符查询
            Criteria baseCriteria = Criteria.where("thingId").is(thingId)
                    .and("service.identifier").is(serviceIdentifier);

            // 创建一个空的关键字条件
            Criteria keywordCriteria = new Criteria();
            if (status != null && !status.isEmpty()) {
                keywordCriteria = keywordCriteria.and("service.status").is(status);
            }

            // 如果有时间范围,添加时间范围匹配规则
            if (startTime != null && endTime != null) {
                keywordCriteria = keywordCriteria.and("service.timestamp").gte(startTime).lte(endTime);
            } else if (startTime != null) {
                keywordCriteria = keywordCriteria.and("service.timestamp").gte(startTime);
            } else if (endTime != null) {
                keywordCriteria = keywordCriteria.and("service.timestamp").lte(endTime);
            }

            Aggregation aggregation = Aggregation.newAggregation(
                    Aggregation.match(baseCriteria),
                    Aggregation.match(keywordCriteria),
                    Aggregation.sort(Sort.by(Sort.Order.desc("service.timestamp"))),
                    Aggregation.group("service.identifier")
                            .first("service.identifier").as("identifier")
                            .first("service.status").as("status")
                            .first("service.serviceName").as("serviceName")
                            .first("service.callType").as("callType")
                            .first("service.inputParams").as("inputParams")
                            .first("service.outputParams").as("outputParams")
                            .first("service.timestamp").as("timestamp")
            );

            try{
                AggregationResults<ThingData.ServiceData> aggregationResults = mongoTemplate.aggregate(
                        aggregation, "thingData", ThingData.ServiceData.class);

                ThingData.ServiceData data = aggregationResults.getUniqueMappedResult();

                servicesData.add(Objects.requireNonNullElseGet(data, () -> new ThingData.ServiceData(serviceSpecDTO.getIdentifier(), serviceSpecDTO.getServiceName(),
                        null, serviceSpecDTO.getCallType(), serviceSpecDTO.getInputParams(), serviceSpecDTO.getOutputParams(), System.currentTimeMillis())));
            } catch (Exception e) {
                log.debug("聚合查询失败! 原因" + e);
                throw new ServiceException(ServiceException.ExceptionType.INTERNAL_FAILURE,
                        ExceptionInfoBuilder.build(ExceptionTemplate.INTERNAL_FAILURE_DATABASE,"聚合查询"));
            }

        }

        List<ThingDataDTO.ServiceDataDTO> serviceDataDTOs = ThingDataMapper.INSTANCE.toServiceDTO(servicesData);

        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, serviceDataDTOs.size());

        List<ThingDataDTO.ServiceDataDTO> paginatedList = serviceDataDTOs.stream()
                .skip(startIndex)
                .limit(endIndex - startIndex)
                .collect(Collectors.toList());

        return ResponseDTOBuilder.build(paginatedList, serviceDataDTOs.size(), pageSize, pageNum);
    }
需求3.非聚合实现的多条件复杂查询场景:
场景:product{attributes{nodetype;   而我需要进行设备的搜索,设备搜索根据nodetype筛选出一组多个产品,然后根据这组产品的id来得到这些产品下的所有设备;

核心如下:

 Query query = new Query();
        Criteria criteria = new Criteria();

        if (StringUtils.hasText(productId)) {
            // 如果提供了 productId,将其作为查询条件
            query.addCriteria(Criteria.where("product.id").is(productId));
        }

        if (StringUtils.hasText(nodeType)) {
            //获取符合 nodeType 的所有产品的 productId 列表
            Criteria nodeTypeCriteria = Criteria.where("attributes.nodeType").is(nodeType);
            List<Product> products = mongoTemplate.find(Query.query(nodeTypeCriteria), Product.class);

            if (!products.isEmpty()) {
                List<Criteria> orCriteriaList = new ArrayList<>();
                for (Product product : products) {
                    // 将ID添加到查询条件中
                    orCriteriaList.add(Criteria.where("product.id").is(product.getId()));
                }
                // 将 Criteria 列表组合成单个 $or 表达式
                criteria.orOperator(orCriteriaList.toArray(new Criteria[0]));
            }
        }


        if (StringUtils.hasText(thingStatus)) {
            // 如果提供了 thingStatus,将其作为查询条件
            query.addCriteria(Criteria.where("status").is(thingStatus));
        }

        // 如果没有提供关键字,只需执行分页查询,同时过滤掉已删除的设备
        query.addCriteria(Criteria.where("deletedAt").is(null));

        if (StringUtils.hasText(keyWords)) {
            // 如果提供了 keyWords,添加关键字搜索条件
            Pattern pattern = Pattern.compile(".*" + keyWords + ".*", Pattern.CASE_INSENSITIVE);
            criteria.orOperator(
                    Criteria.where("name").regex(pattern),
                    Criteria.where("id").regex(pattern)
            );
        }

        query.addCriteria(criteria);

        // 获取符合条件的设备总数
        long totalCount = mongoTemplate.count(query, Thing.class);

        int skip = (pageNum - 1) * pageSize;
        query.skip(skip).limit(pageSize);

        try {
            List<Thing> things = mongoTemplate.find(query, Thing.class);
            return ResponseDTOBuilder.build(ThingMapper.INSTANCE.toDTO(things), totalCount, pageSize, pageNum);
        } catch (Exception e) {
            log.error("查询设备失败" + e);
            throw new ServiceException(
                    ServiceException.ExceptionType.INTERNAL_FAILURE,
                    ExceptionInfoBuilder.build(ExceptionTemplate.INTERNAL_FAILURE_DATABASE,"查询设备")
            );
        }

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java 中使用 MongoDB 聚合查询非常简单,可以使用 MongoDB 的官方 Java 驱动程序来实现。以下是几种常见的 MongoDB 聚合查询方式: 1. 使用 AggregationAggregation 类是 MongoDB 官方 Java 驱动程序中的一个类,用于构建聚合查询。它提供了多个方法来实现聚合查询,如 match()、group()、sort() 等。下面是一个简单的聚合查询示例: ``` MongoClient mongoClient = new MongoClient(); MongoDatabase database = mongoClient.getDatabase("mydb"); MongoCollection<Document> collection = database.getCollection("mycoll"); AggregateIterable<Document> iterable = collection.aggregate( Arrays.asList( Aggregates.match(Filters.eq("status", "A")), Aggregates.group("$cust_id", sum("total", "$amount")) ) ); for (Document document : iterable) { System.out.println(document); } ``` 2. 使用 Pipeline 类 Pipeline 类是 Aggregation 类的一个子类,它提供了更多的方法来构建聚合查询,如 limit()、skip()、unwind() 等。下面是一个使用 Pipeline 类的聚合查询示例: ``` MongoClient mongoClient = new MongoClient(); MongoDatabase database = mongoClient.getDatabase("mydb"); MongoCollection<Document> collection = database.getCollection("mycoll"); List<Bson> pipeline = Arrays.asList( match(eq("status", "A")), group("$cust_id", sum("total", "$amount")) ); AggregateIterable<Document> iterable = collection.aggregate(pipeline); for (Document document : iterable) { System.out.println(document); } ``` 3. 使用 Aggregates 静态导入类 如果你使用的是 Java 8 或更高版本,可以使用静态导入 Aggregates 类来简化聚合查询的代码。下面是一个使用静态导入 Aggregates 类的聚合查询示例: ``` MongoClient mongoClient = new MongoClient(); MongoDatabase database = mongoClient.getDatabase("mydb"); MongoCollection<Document> collection = database.getCollection("mycoll"); List<Bson> pipeline = Arrays.asList( match(eq("status", "A")), group("$cust_id", sum("total", "$amount")) ); AggregateIterable<Document> iterable = collection.aggregate(pipeline); for (Document document : iterable) { System.out.println(document); } ``` 这些都是常见的 MongoDB 聚合查询方式,你可以根据自己的需求选择最适合的方式来实现聚合查询。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值