Decoding NumberFacet results in go struct

I’ve run into the following problem:

I want to facet my search results by some duration that I store in weeks in a numeric field. But I am having issues decoding the bucket results to a proper go type. The issue is that mongodb returns both number and string in the buckets array for numeric facets, and I cannot

My facet definition looks like the following:

var facets = bson.M{
    // ...
	"duration": bson.M{
				"type":       "number",
				"path":       "durationInWeeks",
				"boundaries": bson.A{0, 52, 104, 156, 208},
				"default":    "208", // can't set int default; setting string causes problems in decoding. go fml :(
	},
    // ...
}

My aggregation looks like the following. The call to cursor.All errors here.


// Now run a separate searchMeta to get the counts
	searchStageForCount := bson.D{{"$searchMeta", bson.M{
		"index": "my_index",
		"facet": bson.M{
			"facets": facets,
			"operator": bson.D{
				{"compound", getAllClauses(req)},
			},
		},
	}}}

	// run pipeline
	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{searchStageForCount})
	if err != nil {
		return nil, "", err
	}

	var resultsForCount []ResultMeta
	if err = cursor.All(ctx, &resultsForCount); err != nil {
		log.Println("debug: ", err.Error()) // this is failing currently
		return nil, "", err
	}
//

I experiemented with various types for ID. My ResultMeta struct and its associated types are as follows:


type ResultMeta struct {
	Count CountOption  `bson:"count"`
	Facet FacetOptions `bson:"facet"`
}

type FacetOptions struct {
     // ...
    duration SomeBuckets `bson:"duration" `
    // ...
}

type SomeBuckets struct { Buckets []SomeBucket `bson:"buckets"`}

type SomeBucket {
    ID int32 `bson:"_id"` // int variation, best case, need to omit "default" bucket
    // ID primitive.ObjectID `bson:"_id"` // fails for int keys... cannot convert int32 to object id
    // ID string `bson:"_id"` // string variation, ... cannot convert int32 to string
    Count int32 `bson:"count"`
}
// ...

Is there any way to get around this?

I am a noob in goland.

I ened up solving this by using interface{} type and writing a custom unmarshalBSON for this struct:

If anyone does end up on the same issue. Here’s what I did:

func (ib *SomeBucket) UnmarshalBSON(data []byte) error {
	var temp struct {
		ID    interface{} `bson:"_id"`
		Count int32       `bson:"count"`
	}
	if err := bson.Unmarshal(data, &temp); err != nil {
		return err
	}
	switch id := temp.ID.(type) {
	case int32:
		ib.ID = id
	case string:
                //I'm converting it to int because I know I will only put parsable ints as default bucket. Converting them to string types might be more flexible
		if val, err := strconv.Atoi(id); err != nil {
			return err
		} else {
			ib.ID = int32(val) 
		}
	default:
		return errors.New(fmt.Sprintf("invalid type for ID : Expected string or int, got: %v", id))
	}

	ib.Count = temp.Count
	return nil
}

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.