BSON Type Provider

This article demonstrates how to use the BSON type provider to access .bson files in a statically typed way.

The BSON type provider provides statically typed access to BsonDocuments. It takes as input a sequence of sample documents (e.g. the output from the mongodump utility). The generated type can then be used to read files with the same structure. If the loaded file does not match the structure of the samples, then a runtime error may occur (e.g. when accessing a nonexistent field).

Introducing the provider

The type provider is located in the FSharp.Data.Bson.dll assembly. Assuming the assembly is located in the ../../../bin directory, we can load it in F# Interactive as follows:

1: 
2: 
3: 
4: 
5: 
6: 
#I "../../../bin"
#r "FSharp.Data.Bson.Runtime.dll"
#r "FSharp.Data.Bson.dll"
#r "MongoDB.Bson.dll"

open BsonProvider

Inferring a type from the samples

The BsonProvider<...> takes a string as its first static parameter, representing the path to a file containing BSON. An absolute path can be specified, or a path relative to the current working directory.

The following loads some zip code data using the provider:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
type ZipCode = BsonProvider<"../data/zipcodes.bson">

let zip0 = ZipCode.GetSamples().[0]

zip0.Id        |> ignore
zip0.City      |> ignore
zip0.Loc       |> ignore
zip0.Pop       |> ignore
zip0.State     |> ignore
zip0.BsonValue |> ignore

The generated type has multiple properties:

  • Id of type string, corresponding to the _id field
  • City of type string, corresponding to the city field
  • Loc of type float[], corresponding to the loc field
  • Pop of type int, corresponding to the pop field
  • State of type string, corresponding to the state field
  • BsonValue of type BsonValue, corresponding to the underlying BsonDocument

The provider successfully infers the type from the samples, and exposes the various fields as properties (using a PascalCase name to follow standard .NET naming conventions).

Inferring a record type

The fields need not be of a primitive type for the inference to work. In the case of a field containing an array of documents, a unifying type for the BsonArray is generated. If a certain property is missing from one document, but present in another, then it is inferred as optional.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
type Student = BsonProvider<"../data/students.bson">

let (|Homework|_|) (score:Student.Score) =
    match score.Type with
    | "homework" -> Some score.Score
    | _ -> None

let homeworkAvgs =
    Student.GetSamples()
    |> Seq.ofArray
    |> Seq.map (fun student -> student.Scores)
    |> Seq.map (Array.choose (|Homework|_|))
    |> Seq.filter (fun homeworks -> homeworks.Length > 0)
    |> Seq.map (Array.average)
    |> Seq.toArray

Summary

This article demonstrated the BsonProvider type. The provider infers the structure of a .bson file and exposes it to F# programmers in a nicely typed way.

module BsonProvider
type ZipCode = BsonProvider<...>

Full name: BsonProvider.ZipCode
Multiple items
namespace BsonProvider

--------------------
type BsonProvider

Full name: BsonProvider.BsonProvider


<summary>
                Typed representation of a BSON document.
            </summary>
            <param name='Path'>
                Location of a BSON sample file.
            </param>
            <param name='InferLimit'>
                Number of documents to use for inference. Defaults to 100.
                If this is set as zero, then all documents are used.
            </param>
            <param name='ResolutionFolder'>
                Directory used for resolving relative file references
                (at design time and in hosted execution).
            </param>
val zip0 : BsonProvider<...>

Full name: BsonProvider.zip0
BsonProvider<...>.GetSamples() : BsonProvider<...> []


Returns the entire set of sample BSON documents
property BsonProvider<...>.Id: string
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
property BsonProvider<...>.City: string
property BsonProvider<...>.Loc: float []
property BsonProvider<...>.Pop: int
property BsonProvider<...>.State: string
property Runtime.IBsonTop.BsonValue: MongoDB.Bson.BsonValue
type Student = BsonProvider<...>

Full name: BsonProvider.Student
val score : BsonProvider<...>.Score
type Score =
  inherit IBsonTop
  new : bsonValue: BsonValue -> Score + 1 overload
  member BsonValue : BsonValue
  member Score : float
  member Type : string

Full name: BsonProvider.BsonProvider,Path="../data/students.bson".Score
property BsonProvider<...>.Score.Type: string
union case Option.Some: Value: 'T -> Option<'T>
property BsonProvider<...>.Score.Score: float
union case Option.None: Option<'T>
val homeworkAvgs : float []

Full name: BsonProvider.homeworkAvgs
module Seq

from Microsoft.FSharp.Collections
val ofArray : source:'T [] -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.ofArray
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val student : BsonProvider<...>
property BsonProvider<...>.Scores: BsonProvider<...>.Score []
module Array

from Microsoft.FSharp.Collections
val choose : chooser:('T -> 'U option) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.choose
active recognizer Homework: BsonProvider<...>.Score -> float option

Full name: BsonProvider.( |Homework|_| )
val filter : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.filter
val homeworks : float []
property System.Array.Length: int
val average : array:'T [] -> 'T (requires member ( + ) and member DivideByInt and member get_Zero)

Full name: Microsoft.FSharp.Collections.Array.average
val toArray : source:seq<'T> -> 'T []

Full name: Microsoft.FSharp.Collections.Seq.toArray
Fork me on GitHub