[GODRIVER-2271] Update "bson.D" BSON document literal syntax to resolve Go vet linter warnings Created: 11/Jan/22  Updated: 30/Aug/23

Status: Backlog
Project: Go Driver
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Unknown
Reporter: Matt Dale Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: size-small
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to GODRIVER-2459 Proposal: Improve a way of making fil... Backlog
is related to GODRIVER-2889 Create aggregation pipeline helpers Backlog

 Description   

Use a different syntax for declaring BSON document literals to avoid the "unkeyed fields" "go vet" linter warnings.

Reference BSON document definitions

JSON:

{
    "key1": {
        "nested": "value"
    },
    "key2": "value2",
    "key3": {
        "nested1": {
            "nested2": 14
        }
    }
}

Javascript:

{
    key1: {
        nested: "value",
    },
    key2: "value2",
    key3: {
        nested1: {
            nested2: 14,
        },
    },
}

Existing Go syntax

bson.M:

fmt.Println(bson.M{
	"key1": bson.M{
		"nested": "value",
	},
	"key2": "value2",
	"key3": bson.M{
		"nested1": bson.M{
			"nested2": 14,
		},
	},
})

bson.D:

fmt.Println(bson.D{
	{"key1", bson.D{
		{"nested", "value"},
	}},
	{"key2", "value2"},
	{"key3", bson.D{
		{"nested1", bson.D{
			{"nested2", 14},
		}},
	}},
})

bson.D with keyed fields:

fmt.Println(bson.D{
	{Key: "key1", Value: bson.D{
		{Key: "nested", Value: "value"},
	}},
	{Key: "key2", Value: "value2"},
	{Key: "key3", Value: bson.D{
		{Key: "nested1", Value: bson.D{
			{Key: "nested2", Value: 14},
		}},
	}},
})

bson.D syntax proposals

  1. bson.D with keyed parameters and short keys:

    fmt.Println(bson.D{
    	{K: "key1", V: bson.D{
    		{K: "nested", V: "value"},
    	}},
    	{K: "key2", V: "value2"},
    	{K: "key3", V: bson.D{
    		{K: "nested1", V: bson.D{
    			{K: "nested2", V: 14},
    		}},
    	}},
    })
    

  2. bson.D with bson.E as a function:

    fmt.Println(bson.D{
    	bson.E("key1", bson.D{
    		bson.E("nested", "value"),
    	}),
    	bson.E("key2", "value2"),
    	bson.E("key3", bson.D{
    		bson.E("nested1", bson.D{
    			bson.E("nested2", 14),
    		}),
    	}),
    })
    

  3. bson.D and bson.E as functions:

    fmt.Println(bson.D(
    	bson.E("key1", bson.D(
    		bson.E("nested", "value"),
    	)),
    	bson.E("key2", "value2"),
    	bson.E("key3", bson.D(
    		bson.E("nested1", bson.D(
    			bson.E("nested2", 14),
    		)),
    	)),
    ))
    

  4. bson.D as function that takes an even number of k/v arguments:

    fmt.Println(bson.D(
    	"key1", bson.D(
    		"nested", "value",
    	),
    	"key2", "value2",
    	"key3", bson.D(
    		"nested1", bson.D(
    			"nested2", 14,
    		),
    	),
    ))
    

  5. bson.D as the builder pattern:

    fmt.Println(bson.D().
    	E("key1", bson.D().
    		E("nested", "value"),
    	).
    	E("key2", "value2").
    	E("key3", bson.D().
    		E("nested1", bson.D().
    			E("nested2", 14),
    		),
    	),
    )
    

  6. JSON-to-BSON converter function (from GODRIVER-2459).

    fmt.Println(mongo.MustCompileQuery(`{
    	"key1": {
    		"nested": $1
    	},
    	"key2": $2,
    	"key3": {
    		"nested1": {
    			"nested2": $3
    		}
    	}`,
    	"$1", "value",
    	"$2", "value2",
    	"$3", 14,
    ))
    



 Comments   
Comment by Matt Dale [ 02/Sep/22 ]

An additional idea is to use a collection of Go types, one for each distinct use case. For example, we define a Filter type (map[string]any) specifically for use in a Find or FindOne, a Pipeline ([]map[string]any) type specifically for Aggregate, etc.

An advantage is that we can use different underlying types that are the easiest Go representation guaranteed to work correctly for that use case. However, it may not be straightforward to define the easiest Go representation guaranteed to work correctly, especially in aggregation pipelines where the order can matter or not matter depending on the stage operation.

E.g. using a Filter for a Find:

type Filter map[string]any
 
coll.Find(context.TODO(), Filter{"key": "value"})

E.g. using a Pipeline for an Aggregate:

type Pipeline []map[string]any
 
coll.Aggregate(context.TODO(), Pipeline{
	{"$match": Filter{"field1": "abcd", "field2": 1234}},
	{"$sort": bson.D{{"field3", 1}, {"field4": -1}}}
})

Notice that the $match operator can use a Filter because the order of fields doesn't matter. However, the $sort operator needs to use a bson.D because the order does matter. That still requires users to know a lot about what types to use for what aggregation operation.

Generated at Thu Feb 08 08:38:12 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.