How project DBRef on Spring MongoDB Aggregation?

 ̄綄美尐妖づ 提交于 2021-02-08 08:45:20

问题


I have the following aggregation done in a MongoDB shell to get the number of alerts of each type for each user:

db.getCollection('alerts').aggregate(

        {
            $unwind:"$son"
        },
        {
            $group:
            {
                _id:{
                    son: "$son",
                    level: "$level"
                },
                count: { $sum: 1 }
            }
        },
        {
            $group:
                {
                    _id:{ 
                        son: "$_id.son"
                    },
                    alerts: { $addToSet: {
                        level: "$_id.level",
                        count: "$count"
                    }}

                }
         }
       )

I have translated it to Spring Data MongoDB as follows:

TypedAggregation<AlertEntity> alertsAggregation = 
                Aggregation.newAggregation(AlertEntity.class,
                        unwind("$son"),
                        Aggregation.group("$son", "$level").count().as("count"),
                        Aggregation.group("$_id.son")
                            .addToSet(new BasicDBObject("level", "$_id.level").append("count", "$count")).as("alerts"));

        // Aggregation.match(Criteria.where("_id").in(sonIds)

            AggregationResults<AlertsBySonDTO> results = mongoTemplate.
                 aggregate(alertsAggregation, AlertsBySonDTO.class);

            List<AlertsBySonDTO> alertsBySonResultsList = results.getMappedResults();

            return alertsBySonResultsList;

What I have not clear and I can not get it to work, is to project the identifier and if possible the name of the user (son variable).

The resulting DTO is as follows


public final class AlertsBySonDTO implements Serializable {

    private static final long serialVersionUID = 1L;


    @JsonProperty("identity")
    private String id;

    @JsonProperty("alerts")
    private ArrayList<Map<String, String>> alerts;

}

but in the id property the entire embedded child entity.

This is the structure of the collection of alerts.

JSON alerts format:

{
    "_id" : ObjectId("59e6ff3d9ef9d46a91112890"),
    "_class" : "es.bisite.usal.bulltect.persistence.entity.AlertEntity",
    "level" : "INFO",
    "title" : "Alerta de Prueba",
    "payload" : "Alerta de Prueba",
    "create_at" : ISODate("2017-10-18T07:13:45.091Z"),
    "delivery_mode" : "PUSH_NOTIFICATION",
    "delivered" : false,
    "parent" : {
        "$ref" : "parents",
        "$id" : ObjectId("59e6ff369ef9d46a91112878")
    },
    "son" : {
        "$ref" : "children",
        "$id" : ObjectId("59e6ff389ef9d46a9111287b")
    }
}

/* 2 */
{
    "_id" : ObjectId("59e6ff6d9ef9d46a91112892"),
    "_class" : "es.bisite.usal.bulltect.persistence.entity.AlertEntity",
    "level" : "WARNING",
    "title" : "Token de acceso inv�lido.",
    "payload" : "El token de acceso YOUTUBE no es v�lido",
    "create_at" : ISODate("2017-10-18T07:14:53.449Z"),
    "delivery_mode" : "PUSH_NOTIFICATION",
    "delivered" : false,
    "parent" : {
        "$ref" : "parents",
        "$id" : ObjectId("59e6ff369ef9d46a91112878")
    },
    "son" : {
        "$ref" : "children",
        "$id" : ObjectId("59e6ff389ef9d46a9111287b")
    }
}

/* 3 */
{
    "_id" : ObjectId("59e6ff6d9ef9d46a91112893"),
    "_class" : "es.bisite.usal.bulltect.persistence.entity.AlertEntity",
    "level" : "WARNING",
    "title" : "Token de acceso inv�lido.",
    "payload" : "El token de acceso INSTAGRAM no es v�lido",
    "create_at" : ISODate("2017-10-18T07:14:53.468Z"),
    "delivery_mode" : "PUSH_NOTIFICATION",
    "delivered" : false,
    "parent" : {
        "$ref" : "parents",
        "$id" : ObjectId("59e6ff369ef9d46a91112878")
    },
    "son" : {
        "$ref" : "children",
        "$id" : ObjectId("59e6ff389ef9d46a9111287c")
    }
}

Anyone know how I can approach this?

thanks in advance


回答1:


1. With MongoDB version 3.4

These are the following collections I created to reproduce your use case:

Alerts Collection

{ 
    "_id" : ObjectId("59e6ff3d9ef9d46a91112890"), 
    "_class" : "es.bisite.usal.bulltect.persistence.entity.AlertEntity", 
    "level" : "INFO", 
    "title" : "Alerta de Prueba", 
    "payload" : "Alerta de Prueba", 
    "create_at" : ISODate("2017-10-18T07:13:45.091+0000"), 
    "delivery_mode" : "PUSH_NOTIFICATION", 
    "delivered" : false, 
    "parent" : DBRef("parents", ObjectId("59e6ff369ef9d46a91112878")), 
    "son" : DBRef("children", ObjectId("59e72ff0572ae72d8c063666"))
}
{ 
    "_id" : ObjectId("59e6ff6d9ef9d46a91112892"), 
    "_class" : "es.bisite.usal.bulltect.persistence.entity.AlertEntity", 
    "level" : "WARNING", 
    "title" : "Token de acceso inv�lido.", 
    "payload" : "El token de acceso YOUTUBE no es valido", 
    "create_at" : ISODate("2017-10-18T07:14:53.449+0000"), 
    "delivery_mode" : "PUSH_NOTIFICATION", 
    "delivered" : false, 
    "parent" : DBRef("parents", ObjectId("59e6ff369ef9d46a91112878")), 
    "son" : DBRef("children", ObjectId("59e72ff0572ae72d8c063666"))
}
{ 
    "_id" : ObjectId("59e6ff6d9ef9d46a91112893"), 
    "_class" : "es.bisite.usal.bulltect.persistence.entity.AlertEntity", 
    "level" : "WARNING", 
    "title" : "Token de acceso inv�lido.", 
    "payload" : "El token de acceso INSTAGRAM no es v�lido", 
    "create_at" : ISODate("2017-10-18T07:14:53.468+0000"), 
    "delivery_mode" : "PUSH_NOTIFICATION", 
    "delivered" : false, 
    "parent" : DBRef("parents", ObjectId("59e6ff369ef9d46a91112878")), 
    "son" : DBRef("children", ObjectId("59e72ffb572ae72d8c063669"))
}

Notice I changed the OBjectIds of the sons reference to match the children collection I created.

Children collection

{ 
    "_id" : ObjectId("59e72ff0572ae72d8c063666"), 
    "name" : "Bob"
}
{ 
    "_id" : ObjectId("59e72ffb572ae72d8c063669"), 
    "name" : "Tim"
}

Since you are using a reference you can't just access a field from the other collection. So I think you are missing some aggregation steps.

I did the following:

db.getCollection('alerts').aggregate(
{
            $unwind:"$son"
        },
        {
            $group:
            {
                _id:{
                    son: "$son",
                    level: "$level"
                },
                count: { $sum: 1 }
            }
        },
        {
            $group:
                {
                    _id:{ 
                        son: "$_id.son"
                    },
                    alerts: { $addToSet: {
                        level: "$_id.level",
                        count: "$count"
                    }}

                }
         },
        { $addFields: { sonsArray: { $objectToArray: "$_id.son" } } },
        { $match: { "sonsArray.k": "$id"}  },
        { $lookup: { from: "children", localField: "sonsArray.v", foreignField: "_id", as: "name" } }
)

And got the following results as json:

{ 
    "_id" : {
        "son" : DBRef("children", ObjectId("59e72ffb572ae72d8c063669"))
    }, 
    "alerts" : [
        {
            "level" : "WARNING", 
            "count" : NumberInt(1)
        }
    ], 
    "sonsArray" : [
        {
            "k" : "$ref", 
            "v" : "children"
        }, 
        {
            "k" : "$id", 
            "v" : ObjectId("59e72ffb572ae72d8c063669")
        }
    ], 
    "name" : [
        {
            "_id" : ObjectId("59e72ffb572ae72d8c063669"), 
            "name" : "Tim"
        }
    ]
}
{ 
    "_id" : {
        "son" : DBRef("children", ObjectId("59e72ff0572ae72d8c063666"))
    }, 
    "alerts" : [
        {
            "level" : "INFO", 
            "count" : NumberInt(1)
        }, 
        {
            "level" : "WARNING", 
            "count" : NumberInt(1)
        }
    ], 
    "sonsArray" : [
        {
            "k" : "$ref", 
            "v" : "children"
        }, 
        {
            "k" : "$id", 
            "v" : ObjectId("59e72ff0572ae72d8c063666")
        }
    ], 
    "name" : [
        {
            "_id" : ObjectId("59e72ff0572ae72d8c063666"), 
            "name" : "Bob"
        }
    ]
}

If you want to get rid of the fields that where additionally created like sonsArray etc. you can do add a $project pipeline to clean your result.


2. If you have older versions of mongodb and you can change your data structure.

If instead of using a reference like this:

"son" : DBRef("children", ObjectId("59e72ffb572ae72d8c063669"))

you can add the objectId of the son/s as an array like this:

"sonId" : [
        ObjectId("59e72ff0572ae72d8c063666")
    ]

then you can do your aggregation as follows:

db.getCollection('alerts').aggregate(
{
            $unwind:"$sonId"
        },
        {
            $group:
            {
                _id:{
                    sonId: "$sonId",
                    level: "$level"
                },
                count: { $sum: 1 }
            }
        },
        {
            $group:
                {
                    _id:{ 
                        sonId: "$_id.sonId"
                    },
                    alerts: { $addToSet: {
                        level: "$_id.level",
                        count: "$count"
                    }}

                }
         },
        { $lookup: { from: "children", localField: "_id.sonId", foreignField: "_id", as: "son" } }
)

Is that something you are looking for?



来源:https://stackoverflow.com/questions/46790733/how-project-dbref-on-spring-mongodb-aggregation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!