Search code examples
mongodbgomgo

Is there a better way to create a dynamic match on mongo?


I'm in the process of creating a view which shows all types of data. Currently I'm using tons of if statements to true and create a match parameter for all the different type of request. I really don't think writing out 120 possible if statements is the best way.. plus its getting hard to keep off. I'm hoping someone can point in the right direction. This is what I have so far.

func GetAllHourly(dbsession *mgo.Session, year, month, day, site, size, network, region string, code int) (items []MassAggregation, err error) {
    defer dbsession.Close()
    var match bson.M
    if network == "openx3" {
        network = "openx"
    }

    group := bson.M{"$group": bson.M{"_id": bson.M{"aws_region": "$aws_region", "http_request_status": "$http_request_status", "hour": "$hour", "network": "$network", "site": "$site", "size": "$size", "zone": "$zone", "extra": "$extra"}, "total": bson.M{"$sum": "$count"}}}
    if site == "" && size == "" && network == "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day}}
    } else if site != "" && size == "" && network == "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site}}
    } else if site != "" && size != "" && network == "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size}}
    } else if site != "" && size != "" && network != "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
    } else if site != "" && size != "" && network != "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
    } else if site != "" && size != "" && network != "" && region != "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "http_request_status": code}}
    } else if site == "" && size != "" && network == "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size}}
    } else if site == "" && size != "" && network != "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
    } else if site == "" && size != "" && network != "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
    } else if site == "" && size == "" && network != "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
    } else if site == "" && size == "" && network != "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
    } else if site == "" && size == "" && network != "" && region != "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "http_request_status": code}}
    } else if site == "" && size == "" && network == "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "aws_region": region}}
    } else if site == "" && size == "" && network == "" && region != "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "aws_region": region, "http_request_status": code}}
    } else if site == "" && size == "" && network == "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "http_request_status": code}}
    } else if site != "" && size == "" && network == "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "http_request_status": code}}
    } else if site != "" && size == "" && network == "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "aws_region": region}}
    } else if site != "" && size == "" && network != "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
    } else if site == "" && size != "" && network == "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "http_request_status": code}}
    } else if site == "" && size != "" && network == "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "aws_region": region}}
    } else if site == "" && size != "" && network != "" && region == "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "size": size, "network": &bson.RegEx{Pattern: network, Options: "i"}}}
    } else if site == "" && size == "" && network != "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "http_request_status": code}}
    } else if site == "" && size == "" && network != "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region}}
    } else if site != "" && size != "" && network != "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "size": size, "http_request_status": code}}
    } else if site != "" && size != "" && network == "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "site": site, "size": size, "http_request_status": code}}
    } else if site == "" && size != "" && network != "" && region == "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "size": size, "http_request_status": code}}
    } else if site != "" && size == "" && network != "" && region != "" && code == -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "site": site}}
    } else if site != "" && size == "" && network != "" && region != "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "site": site, "http_request_status": code}}
    } else if site != "" && size == "" && network == "" && region != "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "aws_region": region, "site": site, "http_request_status": code}}
    } else if site == "" && size != "" && network != "" && region != "" && code != -1 {
        match = bson.M{"$match": bson.M{"year": year, "month": month, "day": day, "network": &bson.RegEx{Pattern: network, Options: "i"}, "aws_region": region, "size": size, "http_request_status": code}}
    }

    operations := []bson.M{match, group}
    err = dbsession.DB("logs").C("prod").Pipe(operations).All(&items)
    return
}

As you can see it's unruly.. but i haven't found an alternative yet. I'm hoping I just haven't found the answer.

UPDATE: I've changed my approach a bit. I'm still curious if this is the best way.

func GetAllHourly(dbsession *mgo.Session, year, month, day, site, size, network, region string, code int) (items []MassAggregation, err error) {
    defer dbsession.Close()
    matches := []bson.M{bson.M{"$match": bson.M{"year": year, "month": month, "day": day}}}
    if network == "openx3" {
        network = "openx"
    }

    if site != "" {
        matches = append(matches, bson.M{"$match": bson.M{"site": site}})
    }
    if size != "" {
        matches = append(matches, bson.M{"$match": bson.M{"size": size}})
    }
    if region != "" {
        matches = append(matches, bson.M{"$match": bson.M{"aws_region": region}})
    }
    if code != -1 {
        matches = append(matches, bson.M{"$match": bson.M{"http_request_status": code}})
    }
    if network != "" {
        matches = append(matches, bson.M{"$match": bson.M{"network": &bson.RegEx{Pattern: network, Options: "i"}}})
    }
    group := bson.M{"$group": bson.M{"_id": bson.M{"aws_region": "$aws_region", "http_request_status": "$http_request_status", "hour": "$hour", "network": "$network", "site": "$site", "size": "$size", "zone": "$zone", "extra": "$extra"}, "total": bson.M{"$sum": "$count"}}}
    var operations []bson.M
    for _, match := range matches {
        operations = append(operations, match)
    }
    operations = append(operations, group)
    err = dbsession.DB("logs").C("prod").Pipe(operations).All(&items)
    return
}

Solution

  • bson.M{} is just named type for map[string]interface{} as you can see in docs: http://godoc.org/labix.org/v2/mgo/bson#M

    So, why don't you just use it as dictionary to build custom query? You'll have much less code:

    query := bson.M{}
    
    if site != "" {
           query["site"] = site
        }
        if size != "" {
           query["size"] = size
        }
    
    }
    
    // Then use query variable for querying mongodb