├── .gitattributes ├── .gitignore ├── .gitmodules ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── CodeCoverage.runsettings ├── FSharpDriver-2012.sln ├── LICENSE ├── README.md ├── src ├── FSharp.MongoDB.Bson │ ├── FSharp.MongoDB.Bson.fsproj │ └── Serialization │ │ ├── Conventions │ │ ├── DiscriminatedUnionConvention.fs │ │ ├── OptionTypeConvention.fs │ │ └── RecordTypeConvention.fs │ │ ├── FSharpTypeConventions.fs │ │ ├── FSharpValueSerializers.fs │ │ └── Serializers │ │ ├── DiscriminatedUnionSerializer.fs │ │ ├── FSharpListSerializer.fs │ │ ├── FSharpMapSerializer.fs │ │ ├── FSharpSetSerializer.fs │ │ └── OptionTypeSerializer.fs └── FSharp.MongoDB.Driver │ ├── ExpressibleMongo.fs │ ├── FSharp.MongoDB.Driver.fsproj │ ├── FluentMongo.fs │ ├── MongoBackbone.fs │ ├── MongoBackboneSettings.fs │ ├── MongoClient.fs │ ├── MongoCollection.fs │ ├── MongoDatabase.fs │ ├── MongoOperationSettings.fs │ ├── MongoScope.fs │ ├── MongoScopeOptions.fs │ ├── Properties │ └── AssemblyInfo.fs │ ├── QuotableMongo.fs │ └── Quotations │ ├── HelperPatterns.fs │ ├── QueryPatterns.fs │ └── UpdatePatterns.fs └── tests ├── FSharp.MongoDB.Bson.Tests ├── FSharp.MongoDB.Bson.Tests.fsproj ├── FSharpListSerializationTests.fs ├── FSharpMapSerializationTests.fs ├── FSharpSetSerializationTests.fs ├── FSharpValueSerializationTests.fs ├── app.config └── packages.config ├── FSharp.MongoDB.Driver.FunctionalTests ├── FSharp.MongoDB.Driver.FunctionalTests.fsproj ├── FluentMongoTests.fs ├── app.config └── packages.config └── FSharp.MongoDB.Driver.Tests ├── ExpressibleMongoTests.fs ├── FSharp.MongoDB.Driver.Tests.fsproj ├── MongoBackboneTests.fs ├── QuotableMongoTests.fs ├── TypedQuotableMongoTests.fs ├── app.config └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour, in case users don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files we want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.fs text 7 | *.md text 8 | 9 | *.dll binary 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # MSTest test Results 19 | [Tt]est[Rr]esult*/ 20 | [Bb]uild[Ll]og.* 21 | 22 | *_i.c 23 | *_p.c 24 | *.ilk 25 | *.meta 26 | *.obj 27 | *.pch 28 | *.pdb 29 | *.pgc 30 | *.pgd 31 | *.rsp 32 | *.sbr 33 | *.tlb 34 | *.tli 35 | *.tlh 36 | *.tmp 37 | *.tmp_proj 38 | *.log 39 | *.vspscc 40 | *.vssscc 41 | .builds 42 | *.pidb 43 | *.log 44 | *.scc 45 | 46 | # Visual C++ cache files 47 | ipch/ 48 | *.aps 49 | *.ncb 50 | *.opensdf 51 | *.sdf 52 | *.cachefile 53 | 54 | # Visual Studio profiler 55 | *.psess 56 | *.vsp 57 | *.vspx 58 | 59 | # Guidance Automation Toolkit 60 | *.gpState 61 | 62 | # ReSharper is a .NET coding add-in 63 | _ReSharper*/ 64 | *.[Rr]e[Ss]harper 65 | 66 | # TeamCity is a build add-in 67 | _TeamCity* 68 | 69 | # DotCover is a Code Coverage Tool 70 | *.dotCover 71 | 72 | # NCrunch 73 | *.ncrunch* 74 | .*crunch*.local.xml 75 | 76 | # Installshield output folder 77 | [Ee]xpress/ 78 | 79 | # DocProject is a documentation generator add-in 80 | DocProject/buildhelp/ 81 | DocProject/Help/*.HxT 82 | DocProject/Help/*.HxC 83 | DocProject/Help/*.hhc 84 | DocProject/Help/*.hhk 85 | DocProject/Help/*.hhp 86 | DocProject/Help/Html2 87 | DocProject/Help/html 88 | 89 | # Click-Once directory 90 | publish/ 91 | 92 | # Publish Web Output 93 | *.Publish.xml 94 | *.pubxml 95 | 96 | # NuGet Packages Directory 97 | packages/ 98 | 99 | # Windows Azure Build Output 100 | csx 101 | *.build.csdef 102 | 103 | # Windows Store app package directory 104 | AppPackages/ 105 | 106 | # Others 107 | sql/ 108 | *.Cache 109 | ClientBin/ 110 | [Ss]tyle[Cc]op.* 111 | ~$* 112 | *~ 113 | *.dbmdl 114 | *.[Pp]ublish.xml 115 | *.pfx 116 | *.publishsettings 117 | 118 | # RIA/Silverlight projects 119 | Generated_Code/ 120 | 121 | # Backup & report files from converting an old project file to a newer 122 | # Visual Studio version. Backup files are not needed, because we have git ;-) 123 | _UpgradeReport_Files/ 124 | Backup*/ 125 | UpgradeLog*.XML 126 | UpgradeLog*.htm 127 | 128 | # SQL Server files 129 | App_Data/*.mdf 130 | App_Data/*.ldf 131 | 132 | # ========================= 133 | # Windows detritus 134 | # ========================= 135 | 136 | # Windows image file caches 137 | Thumbs.db 138 | ehthumbs.db 139 | 140 | # Folder config file 141 | Desktop.ini 142 | 143 | # Recycle Bin used on file shares 144 | $RECYCLE.BIN/ 145 | 146 | # Mac crap 147 | .DS_Store 148 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/mongo-csharp-driver"] 2 | path = lib/mongo-csharp-driver 3 | url = https://github.com/mongodb/mongo-csharp-driver.git 4 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mongodb-labs/mongo-fsharp-driver-prototype/492503958d22ef1c1dc5e6aa0b144e846d36b46d/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 32 | 33 | 34 | 35 | 36 | $(SolutionDir).nuget 37 | packages.config 38 | 39 | 40 | 41 | 42 | $(NuGetToolsPath)\NuGet.exe 43 | @(PackageSource) 44 | 45 | "$(NuGetExePath)" 46 | mono --runtime=v4.0.30319 $(NuGetExePath) 47 | 48 | $(TargetDir.Trim('\\')) 49 | 50 | -RequireConsent 51 | -NonInteractive 52 | 53 | 54 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir "$(SolutionDir) " 55 | $(NuGetCommand) pack "$(ProjectPath)" -Properties Configuration=$(Configuration) $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 56 | 57 | 58 | 59 | RestorePackages; 60 | $(BuildDependsOn); 61 | 62 | 63 | 64 | 65 | $(BuildDependsOn); 66 | BuildPackage; 67 | 68 | 69 | 70 | 71 | 72 | 73 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /CodeCoverage.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | .*\.dll$ 14 | .*\.exe$ 15 | 16 | 17 | .*CPPUnitTestFramework.* 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ^FSharp\.MongoDB\.Driver\..* 27 | ^FSharp\.MongoDB\.Bson\..* 28 | 29 | 30 | 34 | .*\.CompareTo\(.* 35 | .*\.GetHashCode\(.* 36 | 37 | 38 | ^FSharp\.MongoDB\.Driver\.Tests\..* 39 | ^FSharp\.MongoDB\.Bson\.Tests\..* 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ^System.Diagnostics.DebuggerHiddenAttribute$ 48 | ^System.Diagnostics.DebuggerNonUserCodeAttribute$ 49 | ^System.Runtime.CompilerServices.CompilerGeneratedAttribute$ 50 | ^System.CodeDom.Compiler.GeneratedCodeAttribute$ 51 | ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$ 52 | 53 | 54 | 55 | 56 | 57 | 58 | .*\\atlmfc\\.* 59 | .*\\vctools\\.* 60 | .*\\public\\sdk\\.* 61 | .*\\microsoft sdks\\.* 62 | .*\\vc\\include\\.* 63 | 64 | 65 | 66 | 67 | 68 | 69 | .*microsoft.* 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | ^B77A5C561934E089$ 78 | ^B03F5F7F11D50A3A$ 79 | ^31BF3856AD364E35$ 80 | ^89845DCD8080CC91$ 81 | ^71E9BCE111E9429C$ 82 | ^8F50407C4E9E73B6$ 83 | ^E361AF139669C375$ 84 | 85 | 86 | 87 | 88 | 89 | True 90 | True 91 | True 92 | False 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /FSharpDriver-2012.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.MongoDB.Bson", "src\FSharp.MongoDB.Bson\FSharp.MongoDB.Bson.fsproj", "{3C580B01-B163-4E2D-BD4A-AFA062EF813E}" 5 | EndProject 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.MongoDB.Driver", "src\FSharp.MongoDB.Driver\FSharp.MongoDB.Driver.fsproj", "{F2F775C4-7C17-4E02-A122-123514FC15C6}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.MongoDB.Bson.Tests", "tests\FSharp.MongoDB.Bson.Tests\FSharp.MongoDB.Bson.Tests.fsproj", "{3E2AFA32-3B88-4354-A01D-31513FA9EEF9}" 9 | EndProject 10 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.MongoDB.Driver.Tests", "tests\FSharp.MongoDB.Driver.Tests\FSharp.MongoDB.Driver.Tests.fsproj", "{45DDDBC9-5807-43ED-9CDF-A67067C50969}" 11 | ProjectSection(ProjectDependencies) = postProject 12 | {F2F775C4-7C17-4E02-A122-123514FC15C6} = {F2F775C4-7C17-4E02-A122-123514FC15C6} 13 | EndProjectSection 14 | EndProject 15 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.MongoDB.Driver.FunctionalTests", "tests\FSharp.MongoDB.Driver.FunctionalTests\FSharp.MongoDB.Driver.FunctionalTests.fsproj", "{D0113E25-1D28-448B-B236-501BF8A69C77}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{886191B2-2B52-48F4-BE8F-998ED5370554}" 18 | ProjectSection(SolutionItems) = preProject 19 | .nuget\NuGet.Config = .nuget\NuGet.Config 20 | .nuget\NuGet.exe = .nuget\NuGet.exe 21 | .nuget\NuGet.targets = .nuget\NuGet.targets 22 | EndProjectSection 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {3C580B01-B163-4E2D-BD4A-AFA062EF813E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {3C580B01-B163-4E2D-BD4A-AFA062EF813E}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {3C580B01-B163-4E2D-BD4A-AFA062EF813E}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {3C580B01-B163-4E2D-BD4A-AFA062EF813E}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {F2F775C4-7C17-4E02-A122-123514FC15C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {F2F775C4-7C17-4E02-A122-123514FC15C6}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {F2F775C4-7C17-4E02-A122-123514FC15C6}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {F2F775C4-7C17-4E02-A122-123514FC15C6}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {3E2AFA32-3B88-4354-A01D-31513FA9EEF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {3E2AFA32-3B88-4354-A01D-31513FA9EEF9}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {3E2AFA32-3B88-4354-A01D-31513FA9EEF9}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {3E2AFA32-3B88-4354-A01D-31513FA9EEF9}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {45DDDBC9-5807-43ED-9CDF-A67067C50969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {45DDDBC9-5807-43ED-9CDF-A67067C50969}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {45DDDBC9-5807-43ED-9CDF-A67067C50969}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {45DDDBC9-5807-43ED-9CDF-A67067C50969}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {D0113E25-1D28-448B-B236-501BF8A69C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {D0113E25-1D28-448B-B236-501BF8A69C77}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {D0113E25-1D28-448B-B236-501BF8A69C77}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {D0113E25-1D28-448B-B236-501BF8A69C77}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright (C) 2013 MongoDB, Inc. 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MongoDB F# Driver Prototype 2 | =========================== 3 | 4 | This is a prototype MongoDB driver written for F#. The goal of this 5 | driver is to make using MongoDB from F# more natural by defining new 6 | ways to express database/collection operations that are idiomatic to 7 | the language. 8 | 9 | #### Special Notes 10 | 11 | The API and implementation are currently subject to change at any time. 12 | You **must not** use this driver in production, as it is still under 13 | development and is in no way supported by MongoDB, Inc. 14 | 15 | We absolutely encourage you to experiment with it and provide us 16 | feedback on the API, design, and implementation. Bug reports and 17 | suggestions for improvements are welcomed, as are pull requests. 18 | 19 | Dependencies 20 | ------------ 21 | 22 | * F# 3.0 23 | 24 | Building 25 | -------- 26 | 27 | The F# driver has been developed on top of the refactored [Core .NET 28 | driver](https://github.com/mongodb/mongo-csharp-driver/tree/v2.0). 29 | This new ***Core .Net driver*** is still in development as well, and 30 | hence unavailable on NuGet. Thus, the branch has been setup as a 31 | submodule. This is intended to change in the future. 32 | 33 | git submodule update --init 34 | 35 | 36 | 37 | License 38 | ------- 39 | 40 | [Apache v2.0](LICENSE) 41 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/FSharp.MongoDB.Bson.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 3c580b01-b163-4e2d-bd4a-afa062ef813e 9 | Library 10 | FSharp.MongoDB.Bson 11 | FSharp.MongoDB.Bson 12 | v4.5 13 | FSharp.MongoDB.Bson 14 | 15 | 16 | true 17 | full 18 | false 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | 3 23 | bin\Debug\FSharp.MongoDB.Bson.xml 24 | 25 | 26 | pdbonly 27 | true 28 | true 29 | bin\Release\ 30 | TRACE 31 | 3 32 | bin\Release\FSharp.MongoDB.Bson.xml 33 | 34 | 35 | 36 | True 37 | 38 | 39 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Bson.dll 40 | True 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Serialization/Conventions/RecordTypeConvention.fs 50 | 51 | 52 | Serialization/Conventions/DiscriminatedUnionConvention.fs 53 | 54 | 55 | Serialization/Conventions/OptionTypeConvention.fs 56 | 57 | 58 | Serialization/Serializers/DiscriminatedUnionSerializer.fs 59 | 60 | 61 | Serialization/Serializers/FSharpListSerializer.fs 62 | 63 | 64 | Serialization/Serializers/FSharpMapSerializer.fs 65 | 66 | 67 | Serialization/Serializers/FSharpSetSerializer.fs 68 | 69 | 70 | Serialization/Serializers/OptionTypeSerializer.fs 71 | 72 | 73 | Serialization/FSharpTypeConventions.fs 74 | 75 | 76 | Serialization/FSharpValueSerializers.fs 77 | 78 | 79 | 80 | 11 81 | 82 | 83 | 84 | 91 | 92 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Conventions/DiscriminatedUnionConvention.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Conventions 17 | 18 | open System 19 | open System.Reflection 20 | 21 | open System.Linq.Expressions 22 | 23 | open Microsoft.FSharp.Reflection 24 | 25 | open MongoDB.Bson.Serialization 26 | open MongoDB.Bson.Serialization.Conventions 27 | 28 | /// 29 | /// Convention for a discriminated union that maps the constructor and fields. 30 | /// Handles when the overall union type contains non-null union cases, 31 | /// or is a singleton union case. 32 | /// 33 | type DiscriminatedUnionConvention() = 34 | inherit ConventionBase("F# Discriminated Union") 35 | 36 | let get index array = Array.get array index 37 | 38 | let isUnion typ = FSharpType.IsUnion typ 39 | 40 | let makeDelegate (meth : MethodInfo) = 41 | let types = meth.GetParameters() |> Array.map (fun x -> x.ParameterType) 42 | Expression.GetDelegateType([| meth.ReturnType |] |> Array.append types) 43 | 44 | let mapCase (classMap : BsonClassMap) (case : UnionCaseInfo) = 45 | let fields = case.GetFields() 46 | let names = fields |> Array.map (fun x -> x.Name) 47 | 48 | classMap.SetDiscriminatorIsRequired true 49 | classMap.SetDiscriminator case.Name 50 | 51 | // Map constructor 52 | let ctor = FSharpValue.PreComputeUnionConstructorInfo(case) 53 | let del = System.Delegate.CreateDelegate(makeDelegate ctor, ctor) 54 | 55 | classMap.MapCreator(del, names) |> ignore 56 | 57 | // Map members 58 | fields |> Array.iter (fun x -> classMap.MapMember(x) |> ignore) 59 | 60 | interface IClassMapConvention with 61 | member __.Apply classMap = 62 | let typ = classMap.ClassType 63 | 64 | // handles when `typ` is a particular union case 65 | if typ.DeclaringType <> null && isUnion typ.DeclaringType then 66 | FSharpType.GetUnionCases typ 67 | |> Array.find (fun x -> x.Name = typ.Name) 68 | |> mapCase classMap 69 | 70 | // handles when `typ` is a singleton discriminated union 71 | elif isUnion typ && not typ.IsAbstract then 72 | let nested = typ.GetNestedTypes() |> Array.filter isUnion 73 | let props = typ.GetProperties() |> Array.filter (fun x -> isUnion x.PropertyType) 74 | 75 | // should not have any nested types or static properties 76 | if nested.Length = 0 && props.Length = 0 then 77 | FSharpType.GetUnionCases typ |> get 0 |> mapCase classMap 78 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Conventions/OptionTypeConvention.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Conventions 17 | 18 | open Microsoft.FSharp.Reflection 19 | 20 | open MongoDB.Bson.Serialization.Conventions 21 | 22 | /// 23 | /// Convention for option types that writes the value in the Some case 24 | /// and omits the field in the None case. 25 | /// 26 | type OptionTypeConvention() = 27 | inherit ConventionBase("F# Option Type") 28 | 29 | let isOption typ = FSharpType.IsUnion typ && typ.IsGenericType 30 | && typ.GetGenericTypeDefinition() = typedefof<_ option> 31 | 32 | interface IMemberMapConvention with 33 | member __.Apply(memberMap) = 34 | let typ = memberMap.MemberType 35 | 36 | if isOption typ then 37 | memberMap.SetDefaultValue None |> ignore 38 | memberMap.SetIgnoreIfNull true |> ignore 39 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Conventions/RecordTypeConvention.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Conventions 17 | 18 | open Microsoft.FSharp.Reflection 19 | 20 | open MongoDB.Bson.Serialization.Conventions 21 | 22 | /// 23 | /// Convention for a record type that maps the constructor and fields. 24 | /// 25 | type RecordTypeConvention() = 26 | inherit ConventionBase("F# Record Type") 27 | 28 | let isRecord typ = FSharpType.IsRecord typ 29 | 30 | interface IClassMapConvention with 31 | member __.Apply(classMap) = 32 | let typ = classMap.ClassType 33 | 34 | if FSharpType.IsRecord(typ) then 35 | let fields = FSharpType.GetRecordFields(typ) 36 | let names = fields |> Array.map (fun x -> x.Name) 37 | let types = fields |> Array.map (fun x -> x.PropertyType) 38 | 39 | // Map constructor 40 | let ctor = typ.GetConstructor(types) 41 | classMap.MapConstructor(ctor, names) |> ignore 42 | 43 | // Map members 44 | fields |> Array.iter (fun x -> classMap.MapMember(x) |> ignore) 45 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/FSharpTypeConventions.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization 17 | 18 | open MongoDB.Bson.Serialization.Conventions 19 | 20 | open FSharp.MongoDB.Bson.Serialization.Conventions 21 | 22 | [] 23 | [] 24 | /// Entry point to initialize the F# data type conventions. 25 | module Conventions = 26 | 27 | let mutable private registered = false 28 | 29 | [] 30 | /// Registers the conventions for F# data types. 31 | let register() = 32 | if not registered then 33 | registered <- true 34 | 35 | let pack = ConventionPack() 36 | 37 | pack.Add(RecordTypeConvention()) 38 | pack.Add(OptionTypeConvention()) 39 | pack.Add(DiscriminatedUnionConvention()) 40 | 41 | ConventionRegistry.Register("F# Type Conventions", pack, (fun _ -> true)) 42 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/FSharpValueSerializers.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization 17 | 18 | open Microsoft.FSharp.Reflection 19 | 20 | open MongoDB.Bson.Serialization 21 | 22 | open FSharp.MongoDB.Bson.Serialization.Serializers 23 | 24 | /// Provides (de)serialization of F# data types. 25 | /// Includes options, lists, maps, sets, records, and discriminated unions. 26 | type FSharpValueSerializationProvider() = 27 | 28 | let isUnion typ = FSharpType.IsUnion typ 29 | 30 | let isOption typ = isUnion typ && typ.IsGenericType 31 | && typ.GetGenericTypeDefinition() = typedefof<_ option> 32 | 33 | let isList typ = isUnion typ && typ.IsGenericType 34 | && typ.GetGenericTypeDefinition() = typedefof<_ list> 35 | 36 | let isMap (typ : System.Type) = 37 | typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof> 38 | 39 | let isSet (typ : System.Type) = 40 | typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof> 41 | 42 | interface IBsonSerializationProvider with 43 | member __.GetSerializer(typ : System.Type) = 44 | 45 | // Check that `typ` is an option type 46 | if isOption typ then 47 | OptionTypeSerializer(typ) :> IBsonSerializer 48 | 49 | // Check that `typ` is a list type 50 | elif isList typ then 51 | typedefof>.MakeGenericType (typ.GetGenericArguments()) 52 | |> System.Activator.CreateInstance 53 | :?> IBsonSerializer 54 | 55 | // Check that `typ` is a map type 56 | elif isMap typ then 57 | typedefof>.MakeGenericType (typ.GetGenericArguments()) 58 | |> System.Activator.CreateInstance 59 | :?> IBsonSerializer 60 | 61 | // Check that `typ` is a set type 62 | elif isSet typ then 63 | typedefof>.MakeGenericType (typ.GetGenericArguments()) 64 | |> System.Activator.CreateInstance 65 | :?> IBsonSerializer 66 | 67 | // Check that `typ` is the overall union type, and not a particular union case 68 | elif isUnion typ && typ.BaseType = typeof then 69 | let nested = typ.GetNestedTypes() |> Array.filter isUnion 70 | let props = typ.GetProperties() |> Array.filter (fun x -> isUnion x.PropertyType) 71 | 72 | // Handles non-singleton discriminated unions 73 | if nested.Length > 0 || props.Length > 0 then 74 | nested |> Array.iter (fun x -> BsonClassMap.LookupClassMap x |> ignore) 75 | DiscriminatedUnionSerializer(typ) :> IBsonSerializer 76 | 77 | // Handles singleton discriminated unions 78 | else 79 | let classMap = BsonClassMap.LookupClassMap typ 80 | BsonClassMapSerializer(classMap) :> IBsonSerializer 81 | 82 | // Otherwise, signal we do not provide serialization for this type 83 | else null 84 | 85 | [] 86 | [] 87 | /// 88 | /// Entry point to initialize the . 89 | /// 90 | module Serializers = 91 | 92 | let mutable private registered = false 93 | 94 | [] 95 | /// Registers the . 96 | let register() = 97 | if not registered then 98 | registered <- true 99 | BsonSerializer.RegisterSerializationProvider(FSharpValueSerializationProvider()) 100 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Serializers/DiscriminatedUnionSerializer.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Serializers 17 | 18 | open Microsoft.FSharp.Reflection 19 | 20 | open MongoDB.Bson.Serialization 21 | open MongoDB.Bson.Serialization.Serializers 22 | 23 | /// 24 | /// A serializer for discriminated unions. 25 | /// Handles null union cases. 26 | /// 27 | type DiscriminatedUnionSerializer(typ : System.Type) = 28 | inherit BsonBaseSerializer() 29 | 30 | let isUnion typ = FSharpType.IsUnion typ 31 | 32 | let cases = FSharpType.GetUnionCases(typ) |> Seq.map (fun x -> (x.Name, x)) |> dict 33 | 34 | override __.Serialize(writer, nominalType, value, options) = 35 | let (case, fields) = FSharpValue.GetUnionFields(value, typ) 36 | 37 | writer.WriteStartDocument() 38 | 39 | writer.WriteString("_t", case.Name) // TODO: base element name off convention 40 | 41 | writer.WriteEndDocument() 42 | 43 | override __.Deserialize(reader, nominalType, actualType, options) = 44 | let mark = reader.GetBookmark() 45 | 46 | reader.ReadStartDocument() 47 | 48 | let name = reader.ReadString "_t" // TODO: base element name off convention 49 | let union = cases.[name] 50 | 51 | // determine whether name is a null union case or not 52 | match union.GetFields() with 53 | | [| |] -> 54 | reader.ReadEndDocument() 55 | FSharpValue.MakeUnion(union, [| |]) 56 | 57 | | _ -> 58 | let case = typ.GetNestedTypes() |> Array.filter isUnion |> Array.find (fun x -> x.Name = name) 59 | 60 | reader.ReturnToBookmark mark 61 | BsonSerializer.Deserialize(reader, case, options) // defer to the class map 62 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Serializers/FSharpListSerializer.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Serializers 17 | 18 | open System.Collections.Generic 19 | 20 | open MongoDB.Bson.Serialization 21 | open MongoDB.Bson.Serialization.Serializers 22 | 23 | /// 24 | /// A serializer for F# lists. 25 | /// 26 | type FSharpListSerializer<'ElemType>() = 27 | inherit BsonBaseSerializer() 28 | 29 | let serializer = EnumerableSerializer<'ElemType>() 30 | 31 | override __.Serialize(writer, nominalType, value, options) = 32 | serializer.Serialize(writer, typeof>, value, options) 33 | 34 | override __.Deserialize(reader, nominalType, actualType, options) = 35 | // deserialize into `IEnumerable` first, then convert to a list 36 | let res = serializer.Deserialize(reader, typeof>, options) 37 | res |> unbox |> List.ofSeq<'ElemType> |> box 38 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Serializers/FSharpMapSerializer.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Serializers 17 | 18 | open System.Collections.Generic 19 | 20 | open MongoDB.Bson.Serialization 21 | open MongoDB.Bson.Serialization.Serializers 22 | 23 | /// 24 | /// A serializer for F# maps. 25 | /// 26 | type FSharpMapSerializer<'KeyType, 'ValueType when 'KeyType : comparison>() = 27 | inherit BsonBaseSerializer() 28 | 29 | let serializer = DictionarySerializer<'KeyType, 'ValueType>() 30 | 31 | override __.Serialize(writer, nominalType, value, options) = 32 | let dictValue = 33 | value 34 | :?> Map<'KeyType, 'ValueType> 35 | |> Map.toSeq 36 | |> dict 37 | 38 | serializer.Serialize(writer, typeof>, dictValue, options) 39 | 40 | override __.Deserialize(reader, nominalType, actualType, options) = 41 | // deserialize into `IDictionary` first, then convert to a map 42 | serializer.Deserialize(reader, typeof>, options) 43 | :?> IDictionary<'KeyType, 'ValueType> 44 | |> Seq.map (|KeyValue|) 45 | |> Map.ofSeq<'KeyType, 'ValueType> 46 | |> box 47 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Serializers/FSharpSetSerializer.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Serializers 17 | 18 | open System.Collections.Generic 19 | 20 | open MongoDB.Bson.Serialization 21 | open MongoDB.Bson.Serialization.Serializers 22 | 23 | /// 24 | /// A serializer for F# sets. 25 | /// 26 | type FSharpSetSerializer<'ElemType when 'ElemType : comparison>() = 27 | inherit BsonBaseSerializer() 28 | 29 | let serializer = EnumerableSerializer<'ElemType>() 30 | 31 | override __.Serialize(writer, nominalType, value, options) = 32 | serializer.Serialize(writer, typeof>, value, options) 33 | 34 | override __.Deserialize(reader, nominalType, actualType, options) = 35 | // deserialize into `IEnumerable` first, then convert to a set 36 | let res = serializer.Deserialize(reader, typeof>, options) 37 | res |> unbox |> Set.ofSeq<'ElemType> |> box 38 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Bson/Serialization/Serializers/OptionTypeSerializer.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Serialization.Serializers 17 | 18 | open Microsoft.FSharp.Reflection 19 | 20 | open MongoDB.Bson.Serialization 21 | open MongoDB.Bson.Serialization.Serializers 22 | 23 | /// 24 | /// A serializer for option types. 25 | /// Writes the value for the Some case, and null for the None case. 26 | /// 27 | type OptionTypeSerializer(typ : System.Type) = 28 | inherit BsonBaseSerializer() 29 | 30 | let cases = FSharpType.GetUnionCases(typ) |> Seq.map (fun x -> (x.Name, x)) |> dict 31 | 32 | override __.Serialize(writer, nominalType, value, options) = 33 | let value = Some (typ.GetProperty("Value").GetValue(value, [| |])) 34 | 35 | match unbox value with 36 | | Some x -> BsonSerializer.Serialize(writer, x.GetType(), x, options) 37 | | None -> BsonSerializer.Serialize(writer, typeof, null, options) 38 | 39 | override __.Deserialize(reader, nominalType, actualType, options) = 40 | let value = BsonSerializer.Deserialize(reader, typ.GenericTypeArguments.[0], options) 41 | 42 | let (case, args) = 43 | match value with 44 | | null -> (cases.["None"], [| |]) 45 | | _ -> (cases.["Some"], [| value |]) 46 | 47 | FSharpValue.MakeUnion(case, args) 48 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/FSharp.MongoDB.Driver.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | f2f775c4-7c17-4e02-a122-123514fc15c6 9 | Library 10 | MongoDB.Driver 11 | FSharp.MongoDB.Driver 12 | v4.5 13 | FSharp.MongoDB.Driver 14 | ..\..\ 15 | true 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\FSharp.MongoDB.Driver.xml 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\FSharp.MongoDB.Driver.xml 35 | 36 | 37 | 38 | True 39 | 40 | 41 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Bson.dll 42 | True 43 | 44 | 45 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Driver.Core.dll 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Properties/AssemblyInfo.fs 55 | 56 | 57 | Quotations/HelperPatterns.fs 58 | 59 | 60 | Quotations/QueryPatterns.fs 61 | 62 | 63 | Quotations/UpdatePatterns.fs 64 | 65 | 66 | MongoOperationSettings.fs 67 | 68 | 69 | MongoBackboneSettings.fs 70 | 71 | 72 | MongoBackbone.fs 73 | 74 | 75 | MongoScopeOptions.fs 76 | 77 | 78 | MongoScope.fs 79 | 80 | 81 | MongoCollection.fs 82 | 83 | 84 | FluentMongo.fs 85 | 86 | 87 | MongoDatabase.fs 88 | 89 | 90 | MongoClient.fs 91 | 92 | 93 | QuotableMongo.fs 94 | 95 | 96 | ExpressibleMongo.fs 97 | 98 | 99 | 100 | 101 | FSharp.MongoDB.Bson 102 | {3c580b01-b163-4e2d-bd4a-afa062ef813e} 103 | True 104 | 105 | 106 | 107 | 11 108 | 109 | 110 | 111 | 118 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/FluentMongo.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open MongoDB.Bson 19 | 20 | open MongoDB.Driver.Core 21 | open MongoDB.Driver.Core.Protocol 22 | open MongoDB.Driver.Core.Protocol.Messages 23 | 24 | [] 25 | module Fluent = 26 | 27 | open Helpers 28 | 29 | [] 30 | [] 31 | /// Basic operations on the type. 32 | module Scope = 33 | 34 | /// 35 | /// Returns a new with the specified filter. 36 | /// 37 | let find query scope = 38 | { scope with Query = Some query } 39 | 40 | /// 41 | /// Returns a new with the projection applied. 42 | /// 43 | let fields project scope = 44 | { scope with Project = Some project } 45 | 46 | /// 47 | /// Returns a new with the sort order applied. 48 | /// 49 | let sort order scope = 50 | { scope with Sort = Some order } 51 | 52 | /// 53 | /// Returns a new with the limit applied. 54 | /// Used with the number of documents found, updated, or removed. 55 | /// 56 | let limit n scope = 57 | { scope with Limit = n } 58 | 59 | /// 60 | /// Returns a new with the skip applied. 61 | /// 62 | let skip n scope = 63 | { scope with Skip = n } 64 | 65 | /// 66 | /// Returns a new with the query options applied. 67 | /// 68 | let withQueryOptions options scope = 69 | { scope with QueryOptions = options } 70 | 71 | /// 72 | /// Returns a new with the read preference applied. 73 | /// 74 | let withReadPreference readPref scope = 75 | { scope with ReadPreference = readPref } 76 | 77 | /// 78 | /// Returns a new with the write options applied. 79 | /// 80 | let withWriteOptions options scope = 81 | { scope with WriteOptions = options } 82 | 83 | /// 84 | /// Returns the number of documents satisfying the predicate 85 | /// of the current . 86 | /// 87 | let count (scope : Scope<'DocType>) = 88 | let backbone = scope.Backbone 89 | let db = scope.Database 90 | let clctn = scope.Collection 91 | 92 | let cmd = BsonDocument("count", BsonString(clctn)) 93 | 94 | match scope.Query with 95 | | Some x -> cmd.Add("query", x) |> ignore 96 | | None -> () 97 | 98 | let limit = scope.Limit 99 | let skip = scope.Skip 100 | 101 | cmd.AddRange([ BsonElement("limit", BsonInt32(limit)) 102 | BsonElement("skip", BsonInt32(skip)) ]) |> ignore 103 | 104 | backbone.Run db cmd 105 | 106 | /// 107 | /// Removes all of the documents satisfying the predicate 108 | /// of the current . 109 | /// 110 | let remove (scope : Scope<'DocType>) = 111 | let backbone = scope.Backbone 112 | let db = scope.Database 113 | let clctn = scope.Collection 114 | 115 | // Raise error if sort has been specified 116 | if scope.Sort.IsSome then failwith "sort has been specified" 117 | 118 | // Raise error if limit has been specified (as other than 1) 119 | if scope.Limit <> 0 && scope.Limit <> 1 then failwith "limit has been specified" 120 | 121 | // Raise error if skip has been specified 122 | if scope.Skip <> 0 then failwith "skip has been specified" 123 | 124 | let query = makeQueryDoc scope.Query None scope.QueryOptions 125 | if scope.WriteOptions.Isolated then query.Add("$isolated", BsonInt32(1)) |> ignore 126 | 127 | let flags = DeleteFlags.None 128 | let settings = { Operation.DefaultSettings.remove with WriteConcern = Some scope.WriteOptions.WriteConcern } 129 | 130 | backbone.Remove db clctn query flags settings 131 | 132 | /// 133 | /// Removes a single document satisfying the predicate 134 | /// of the current . 135 | /// 136 | let removeOne (scope : Scope<'DocType>) = 137 | let backbone = scope.Backbone 138 | let db = scope.Database 139 | let clctn = scope.Collection 140 | 141 | // Raise error if sort has been specified 142 | if scope.Sort.IsSome then failwith "sort has been specified" 143 | 144 | // Ignore limit 145 | 146 | // Raise error if skip has been specified 147 | if scope.Skip <> 0 then failwith "skip has been specified" 148 | 149 | let query = makeQueryDoc scope.Query None scope.QueryOptions 150 | if scope.WriteOptions.Isolated then query.Add("$isolated", BsonInt32(1)) |> ignore 151 | 152 | let flags = DeleteFlags.Single 153 | let settings = { Operation.DefaultSettings.remove with WriteConcern = Some scope.WriteOptions.WriteConcern } 154 | 155 | backbone.Remove db clctn query flags settings 156 | 157 | /// 158 | /// Updates all of the documents satisfying the predicate 159 | /// of the current 160 | /// according to the supplied update modifiers. 161 | /// 162 | let update update (scope : Scope<'DocType>) = 163 | let backbone = scope.Backbone 164 | let db = scope.Database 165 | let clctn = scope.Collection 166 | 167 | // Raise error if sort has been specified 168 | if scope.Sort.IsSome then failwith "sort has been specified" 169 | 170 | // Raise error if limit has been specified (as other than 1) 171 | if scope.Limit <> 0 && scope.Limit <> 1 then failwith "limit has been specified" 172 | 173 | // Raise error if skip has been specified 174 | if scope.Skip <> 0 then failwith "skip has been specified" 175 | 176 | let query = makeQueryDoc scope.Query None scope.QueryOptions 177 | if scope.WriteOptions.Isolated then query.Add("$isolated", BsonInt32(1)) |> ignore 178 | 179 | let flags = UpdateFlags.Multi 180 | let settings = { Operation.DefaultSettings.update with CheckUpdateDocument = Some true 181 | WriteConcern = Some scope.WriteOptions.WriteConcern } 182 | 183 | backbone.Update db clctn query update flags settings 184 | 185 | /// 186 | /// Updates a single document satisfying the predicate 187 | /// of the current 188 | /// according to the supplied update modifiers. 189 | /// 190 | let updateOne update (scope : Scope<'DocType>) = 191 | let backbone = scope.Backbone 192 | let db = scope.Database 193 | let clctn = scope.Collection 194 | 195 | // Raise error if sort has been specified 196 | if scope.Sort.IsSome then failwith "sort has been specified" 197 | 198 | // Ignore limit 199 | 200 | // Raise error if skip has been specified 201 | if scope.Skip <> 0 then failwith "skip has been specified" 202 | 203 | let query = makeQueryDoc scope.Query None scope.QueryOptions 204 | 205 | let flags = UpdateFlags.None 206 | let settings = { Operation.DefaultSettings.update with CheckUpdateDocument = Some true 207 | WriteConcern = Some scope.WriteOptions.WriteConcern } 208 | 209 | backbone.Update db clctn query update flags settings 210 | 211 | /// 212 | /// Replaces all of the documents satisfying the predicate 213 | /// of the current 214 | /// according to the supplied update modifiers. 215 | /// 216 | let replace update (scope : Scope<'DocType>) = 217 | let backbone = scope.Backbone 218 | let db = scope.Database 219 | let clctn = scope.Collection 220 | 221 | // Raise error if sort has been specified 222 | if scope.Sort.IsSome then failwith "sort has been specified" 223 | 224 | // Raise error if limit has been specified (as other than 1) 225 | if scope.Limit <> 0 && scope.Limit <> 1 then failwith "limit has been specified" 226 | 227 | // Raise error if skip has been specified 228 | if scope.Skip <> 0 then failwith "skip has been specified" 229 | 230 | let query = makeQueryDoc scope.Query None scope.QueryOptions 231 | if scope.WriteOptions.Isolated then query.Add("$isolated", BsonInt32(1)) |> ignore 232 | 233 | let flags = UpdateFlags.Multi 234 | let settings = { Operation.DefaultSettings.update with CheckUpdateDocument = Some false 235 | WriteConcern = Some scope.WriteOptions.WriteConcern } 236 | 237 | backbone.Update db clctn query update flags settings 238 | 239 | /// 240 | /// Replaces a single document satisfying the predicate 241 | /// of the current 242 | /// according to the supplied update modifiers. 243 | /// 244 | let replaceOne update (scope : Scope<'DocType>) = 245 | let backbone = scope.Backbone 246 | let db = scope.Database 247 | let clctn = scope.Collection 248 | 249 | // Raise error if sort has been specified 250 | if scope.Sort.IsSome then failwith "sort has been specified" 251 | 252 | // Ignore limit 253 | 254 | // Raise error if skip has been specified 255 | if scope.Skip <> 0 then failwith "skip has been specified" 256 | 257 | let query = makeQueryDoc scope.Query None scope.QueryOptions 258 | 259 | let flags = UpdateFlags.None 260 | let settings = { Operation.DefaultSettings.update with CheckUpdateDocument = Some false 261 | WriteConcern = Some scope.WriteOptions.WriteConcern } 262 | 263 | backbone.Update db clctn query update flags settings 264 | 265 | /// 266 | /// Returns information about the query plan, 267 | /// as represented by the current . 268 | /// 269 | let explain (scope : Scope<'DocType>) = 270 | let backbone = scope.Backbone 271 | let db = scope.Database 272 | let clctn = scope.Collection 273 | 274 | let query = makeQueryDoc scope.Query scope.Sort scope.QueryOptions 275 | 276 | let project = 277 | match scope.Project with 278 | | Some x -> x 279 | | None -> null 280 | 281 | query.Add("$explain", BsonInt32(1)) |> ignore 282 | 283 | let limit = scope.Limit 284 | let skip = scope.Skip 285 | 286 | let flags = QueryFlags.None 287 | let settings = Operation.DefaultSettings.query 288 | 289 | let res = backbone.Find db clctn query project limit skip flags settings 290 | use iter = res.GetEnumerator() 291 | 292 | if not (iter.MoveNext()) then raise <| MongoOperationException("explain command missing response document") 293 | iter.Current 294 | 295 | /// 296 | /// Performs text search with the specified phrase 297 | /// using the current . 298 | /// 299 | let textSearch text (scope : Scope<'DocType>) = 300 | let backbone = scope.Backbone 301 | let db = scope.Database 302 | let clctn = scope.Collection 303 | 304 | let cmd = makeTextSearchDoc clctn text scope.Query scope.Project scope.Limit { Language = None } 305 | 306 | backbone.Run db cmd 307 | 308 | /// 309 | /// Performs text search with the specified phrase and options 310 | /// using the current . 311 | /// 312 | let textSearchWithOptions text (options : Scope.TextSearchOptions) (scope : Scope<'DocType>) = 313 | let backbone = scope.Backbone 314 | let db = scope.Database 315 | let clctn = scope.Collection 316 | 317 | let cmd = makeTextSearchDoc clctn text scope.Query scope.Project scope.Limit options 318 | 319 | backbone.Run db cmd 320 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoBackbone.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open MongoDB.Bson 19 | open MongoDB.Bson.Serialization 20 | 21 | open MongoDB.Driver.Core 22 | open MongoDB.Driver.Core.Connections 23 | open MongoDB.Driver.Core.Diagnostics 24 | open MongoDB.Driver.Core.Events 25 | open MongoDB.Driver.Core.Operations 26 | open MongoDB.Driver.Core.Protocol.Messages 27 | open MongoDB.Driver.Core.Sessions 28 | 29 | /// The system that handles both database- and collection-level operations 30 | /// on a cluster. Meant to provide a consistent interface for use by 31 | /// and , 32 | /// rather than have a dependency on the Core .NET driver in those modules too. 33 | type internal MongoBackbone(settings : Backbone.AllSettings) = 34 | 35 | let eventPublisher = EventPublisher() 36 | 37 | // builds up the network stream settings 38 | let networkStreamSettings = 39 | NetworkStreamSettings.Create(fun builder -> 40 | // applies the connect timeout (if specified) 41 | match settings.Stream.ConnectTimeout with 42 | | Some x -> builder.SetConnectTimeout x 43 | | None -> () 44 | 45 | // applies the read timeout (if specified) 46 | match settings.Stream.ReadTimeout with 47 | | Some x -> builder.SetReadTimeout x 48 | | None -> () 49 | 50 | // applies the write timeout (if specified) 51 | match settings.Stream.WriteTimeout with 52 | | Some x -> builder.SetWriteTimeout x 53 | | None -> () 54 | 55 | // applies the TCP receive buffer size (if specified) 56 | match settings.Stream.TcpReceiveBufferSize with 57 | | Some x -> builder.SetTcpReceiveBufferSize x 58 | | None -> () 59 | 60 | // applies the TCP send buffer size (if specified) 61 | match settings.Stream.TcpSendBufferSize with 62 | | Some x -> builder.SetTcpSendBufferSize x 63 | | None -> () 64 | ) 65 | 66 | // uses default stream connection settings; does not support credentialing 67 | let streamConnectionSettings = StreamConnectionSettings.Defaults 68 | 69 | // builds up the connection pool settings 70 | let connectionPoolSettings = 71 | ConnectionPoolSettings.Create(fun builder -> 72 | // applies the maximum idle time of a connection (if specified) 73 | match settings.ConnectionPool.ConnectionMaxIdleTime with 74 | | Some x -> builder.SetConnectionMaxIdleTime x 75 | | None -> () 76 | 77 | // applies the maximum life time of a connection (if specified) 78 | match settings.ConnectionPool.ConnectionMaxLifeTime with 79 | | Some x -> builder.SetConnectionMaxLifeTime x 80 | | None -> () 81 | 82 | // applies the maximum connection pool size (if specified) 83 | match settings.ConnectionPool.MaxSize with 84 | | Some x -> builder.SetMaxSize x 85 | | None -> () 86 | 87 | // applies the minimum connection pool size (if specified) 88 | match settings.ConnectionPool.MinSize with 89 | | Some x -> builder.SetMinSize x 90 | | None -> () 91 | 92 | // applies the frequence at which to maintain 93 | // the connection pool size (if specified) 94 | match settings.ConnectionPool.SizeMaintenanceFrequency with 95 | | Some x -> builder.SetSizeMaintenanceFrequency x 96 | | None -> () 97 | 98 | // applies the maximum size of the wait queue (if specified) 99 | match settings.ConnectionPool.WaitQueueSize with 100 | | Some x -> builder.SetWaitQueueSize x 101 | | None -> () 102 | ) 103 | 104 | // builds up the clusterable server settings 105 | let clusterableServerSettings = 106 | ClusterableServerSettings.Create(fun builder -> 107 | // applies the connect retry frequency (if specified) 108 | match settings.ClusterableServer.ConnectRetryFrequency with 109 | | Some x -> builder.SetConnectRetryFrequency x 110 | | None -> () 111 | 112 | // applies the heartbeat frequence (if specified) 113 | match settings.ClusterableServer.HeartbeatFrequency with 114 | | Some x -> builder.SetHeartbeatFrequency x 115 | | None -> () 116 | 117 | // applies the default maximum document size (if specified) 118 | match settings.ClusterableServer.MaxDocumentSizeDefault with 119 | | Some x -> builder.SetMaxDocumentSizeDefault x 120 | | None -> () 121 | 122 | // applies the default maximum message size (if specified) 123 | match settings.ClusterableServer.MaxMessageSizeDefault with 124 | | Some x -> builder.SetMaxMessageSizeDefault x 125 | | None -> () 126 | ) 127 | 128 | // builds up the cluster settings 129 | let clusterSettings = ClusterSettings.Create(fun builder -> 130 | builder.AddHosts(settings.Hosts) // adds the list of hosts 131 | 132 | // applies the replica set name (if specified) 133 | match settings.ReplicaSet with 134 | | Some x -> builder.SetReplicaSetName x 135 | | None -> () 136 | ) 137 | 138 | // creates all of the factories from their settings 139 | let streamFactory = NetworkStreamFactory(networkStreamSettings, DnsCache()) 140 | let connFactory = StreamConnectionFactory(streamConnectionSettings, streamFactory, 141 | eventPublisher) 142 | 143 | let connPoolFactory = ConnectionPoolFactory(connectionPoolSettings, connFactory, 144 | eventPublisher) 145 | 146 | let channelFactory = ConnectionPoolChannelProviderFactory(connPoolFactory, 147 | eventPublisher) 148 | 149 | let nodeFactory = ClusterableServerFactory(clusterableServerSettings, channelFactory, 150 | connFactory, eventPublisher) 151 | 152 | let clusterFactory = ClusterFactory(nodeFactory) 153 | let cluster = clusterFactory.Create(clusterSettings) 154 | 155 | // initializes the cluster 156 | do cluster.Initialize() 157 | 158 | /// A session for use on an entire cluster. 159 | member internal x.Session = new ClusterSession(cluster) 160 | 161 | [] 162 | /// Basic operations on the cluster. 163 | module internal Operations = 164 | 165 | [] 166 | /// Database-level operations. 167 | module DatabaseOps = 168 | 169 | type MongoBackbone with 170 | 171 | /// Runs a command on the specified database. 172 | /// The database name. 173 | /// The command to execute. 174 | member x.Run db cmd = 175 | 176 | let database = DatabaseNamespace(db) 177 | 178 | let commandOp = 179 | GenericCommandOperation( 180 | Database = database, 181 | Command = cmd, 182 | Session = x.Session) 183 | 184 | commandOp.Execute() 185 | 186 | /// Drops the specified database. 187 | /// The database name 188 | member x.DropDatabase db = 189 | 190 | let cmd = BsonDocument("dropDatabase", BsonInt32(1)) 191 | x.Run db cmd 192 | 193 | [] 194 | /// Collection-level operations. 195 | module CollectionOps = 196 | 197 | type MongoBackbone with 198 | 199 | /// Drops the specified collection. 200 | /// The database name. 201 | /// The collection name. 202 | member x.DropCollection db clctn = 203 | 204 | let cmd = BsonDocument("drop", BsonString(clctn)) 205 | x.Run db cmd 206 | 207 | /// Inserts a batch of documents into the specified collection. 208 | /// The database name. 209 | /// The collection name. 210 | member x.BulkInsert db clctn (docs : seq<'DocType>) flags (settings : Operation.InsertSettings) = 211 | 212 | let collection = CollectionNamespace(db, clctn) 213 | 214 | let insertOp = 215 | InsertOperation( 216 | Collection = collection, 217 | Documents = docs, 218 | DocumentType = typeof<'DocType>, 219 | Flags = flags, 220 | Session = x.Session) 221 | 222 | // applies the reader settings to the operation (if specified) 223 | match settings.ReaderSettings with 224 | | Some x -> insertOp.ReaderSettings <- x 225 | | None -> () 226 | 227 | // applies the writer settings to the operation (if specified) 228 | match settings.WriterSettings with 229 | | Some x -> insertOp.WriterSettings <- x 230 | | None -> () 231 | 232 | // applies the write concern to the operation (if specified) 233 | match settings.WriteConcern with 234 | | Some x -> insertOp.WriteConcern <- x 235 | | None -> () 236 | 237 | // applies whether to assign an _id (if specified) 238 | match settings.AssignIdOnInsert with 239 | | Some x -> insertOp.AssignIdOnInsert <- x 240 | | None -> () 241 | 242 | // applies whether to check the inserted documents (if specified) 243 | match settings.CheckInsertDocuments with 244 | | Some x -> insertOp.CheckInsertDocuments <- x 245 | | None -> () 246 | 247 | insertOp.Execute() 248 | 249 | /// Inserts a single document into the specified collection. 250 | /// The database name. 251 | /// The collection name. 252 | member x.Insert db clctn doc flags settings = 253 | let res = x.BulkInsert db clctn [ doc ] flags settings 254 | use iter = res.GetEnumerator() 255 | 256 | if not (iter.MoveNext()) then raise <| MongoOperationException("insert command missing write concern result") 257 | iter.Current 258 | 259 | /// 260 | /// Returns a sequence of 'DocType documents 261 | /// from the specified collection that satisfy the predicate. 262 | /// 263 | /// The database name. 264 | /// The collection name. 265 | member x.Find<'DocType> db clctn query project limit skip flags (settings : Operation.QuerySettings) = 266 | 267 | let collection = CollectionNamespace(db, clctn) 268 | 269 | let queryOp = 270 | QueryOperation<'DocType>( 271 | Collection = collection, 272 | Query = query, 273 | Fields = project, 274 | Limit = limit, 275 | Skip = skip, 276 | Flags = flags, 277 | Session = x.Session) 278 | 279 | // applies the reader settings to the operation (if specified) 280 | match settings.ReaderSettings with 281 | | Some x -> queryOp.ReaderSettings <- x 282 | | None -> () 283 | 284 | // applies the writer settings to the operation (if specified) 285 | match settings.WriterSettings with 286 | | Some x -> queryOp.WriterSettings <- x 287 | | None -> () 288 | 289 | // applies the batch size to the operation (if specified) 290 | match settings.BatchSize with 291 | | Some x -> queryOp.BatchSize <- x 292 | | None -> () 293 | 294 | queryOp :> seq<'DocType> 295 | 296 | /// 297 | /// Updates the documents of the specified collection 298 | /// that satisfy the predicate. 299 | /// 300 | /// The database name. 301 | /// The collection name. 302 | member x.Update db clctn query update flags (settings : Operation.UpdateSettings) = 303 | 304 | let collection = CollectionNamespace(db, clctn) 305 | 306 | let updateOp = 307 | UpdateOperation( 308 | Collection = collection, 309 | Query = query, 310 | Update = update, 311 | Flags = flags, 312 | Session = x.Session) 313 | 314 | // applies the reader settings to the operation (if specified) 315 | match settings.ReaderSettings with 316 | | Some x -> updateOp.ReaderSettings <- x 317 | | None -> () 318 | 319 | // applies the writer settings to the operation (if specified) 320 | match settings.WriterSettings with 321 | | Some x -> updateOp.WriterSettings <- x 322 | | None -> () 323 | 324 | // applies the write concern to the operation (if specified) 325 | match settings.WriteConcern with 326 | | Some x -> updateOp.WriteConcern <- x 327 | | None -> () 328 | 329 | // applies whether to check the update document (if specified) 330 | match settings.CheckUpdateDocument with 331 | | Some x -> updateOp.CheckUpdateDocument <- x 332 | | None -> () 333 | 334 | updateOp.Execute() 335 | 336 | /// 337 | /// Removes the documents from the specified collection 338 | /// that satisfy the predicate. 339 | /// 340 | /// The database name. 341 | /// The collection name. 342 | member x.Remove db clctn query flags (settings : Operation.RemoveSettings) = 343 | 344 | let collection = CollectionNamespace(db, clctn) 345 | 346 | let removeOp = 347 | RemoveOperation( 348 | Collection = collection, 349 | Query = query, 350 | Flags = flags, 351 | Session = x.Session) 352 | 353 | // applies the reader settings to the operation (if specified) 354 | match settings.ReaderSettings with 355 | | Some x -> removeOp.ReaderSettings <- x 356 | | None -> () 357 | 358 | // applies the writer settings to the operation (if specified) 359 | match settings.WriterSettings with 360 | | Some x -> removeOp.WriterSettings <- x 361 | | None -> () 362 | 363 | // applies the write concern to the operation (if specified) 364 | match settings.WriteConcern with 365 | | Some x -> removeOp.WriteConcern <- x 366 | | None -> () 367 | 368 | removeOp.Execute() 369 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoBackboneSettings.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open System 19 | open System.Net 20 | 21 | [] 22 | /// Provides configuration of the . 23 | module Backbone = 24 | 25 | [] 26 | /// Settings for the . 27 | type StreamSettings = { 28 | ConnectTimeout : TimeSpan option 29 | ReadTimeout : TimeSpan option 30 | TcpReceiveBufferSize : int option 31 | TcpSendBufferSize : int option 32 | WriteTimeout : TimeSpan option 33 | } 34 | 35 | [] 36 | /// Settings for the . 37 | type ConnectionPoolSettings = { 38 | ConnectionMaxIdleTime : TimeSpan option 39 | ConnectionMaxLifeTime : TimeSpan option 40 | MaxSize : int option 41 | MinSize : int option 42 | SizeMaintenanceFrequency : TimeSpan option 43 | WaitQueueSize : int option 44 | } 45 | 46 | [] 47 | /// Settings for the . 48 | type ClusterableServerSettings = { 49 | ConnectRetryFrequency : TimeSpan option 50 | HeartbeatFrequency : TimeSpan option 51 | MaxDocumentSizeDefault : int option 52 | MaxMessageSizeDefault : int option 53 | } 54 | 55 | [] 56 | /// Combined settings for the . 57 | type AllSettings = { 58 | Stream : StreamSettings 59 | ConnectionPool : ConnectionPoolSettings 60 | ClusterableServer : ClusterableServerSettings 61 | Hosts : DnsEndPoint list 62 | ReplicaSet : string option 63 | } 64 | 65 | [] 66 | /// Contains the default settings for the various factories 67 | /// and . 68 | module DefaultSettings = 69 | 70 | /// The default settings. 71 | /// Designed to override defaults for only the specific fields. 72 | let stream = { 73 | StreamSettings.ConnectTimeout = None 74 | StreamSettings.ReadTimeout = None 75 | StreamSettings.TcpReceiveBufferSize = None 76 | StreamSettings.TcpSendBufferSize = None 77 | StreamSettings.WriteTimeout = None 78 | } 79 | 80 | /// The default settings. 81 | /// Designed to override defaults for only the specific fields. 82 | let connectionPool = { 83 | ConnectionPoolSettings.ConnectionMaxIdleTime = None 84 | ConnectionPoolSettings.ConnectionMaxLifeTime = None 85 | ConnectionPoolSettings.MaxSize = None 86 | ConnectionPoolSettings.MinSize = None 87 | ConnectionPoolSettings.SizeMaintenanceFrequency = None 88 | ConnectionPoolSettings.WaitQueueSize = None 89 | } 90 | 91 | /// The default settings. 92 | /// Designed to override defaults for only the specific fields. 93 | let clusterableServer = { 94 | ClusterableServerSettings.ConnectRetryFrequency = None 95 | ClusterableServerSettings.HeartbeatFrequency = None 96 | ClusterableServerSettings.MaxDocumentSizeDefault = None 97 | ClusterableServerSettings.MaxMessageSizeDefault = None 98 | } 99 | 100 | /// The default settings. 101 | /// Uses the default settings of the other factories, 102 | /// and connects to a standalone server on localhost at the default port. 103 | let all = { 104 | AllSettings.Stream = stream 105 | AllSettings.ConnectionPool = connectionPool 106 | AllSettings.ClusterableServer = clusterableServer 107 | AllSettings.Hosts = [ DnsEndPoint("localhost", 27017) ] 108 | AllSettings.ReplicaSet = None 109 | } 110 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoClient.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open System.Net 19 | 20 | open FSharp.MongoDB.Bson.Serialization 21 | 22 | [] 23 | /// Represents a client of the cluster. 24 | type IMongoClient = 25 | /// Returns the specified database. 26 | /// The database name. 27 | abstract member GetDatabase : string -> IMongoDatabase 28 | 29 | [] 30 | [] 31 | /// Provides configuration of the . 32 | module Client = 33 | 34 | [] 35 | /// Settings for the . 36 | type Settings = { 37 | Stream : Backbone.StreamSettings 38 | ConnectionPool : Backbone.ConnectionPoolSettings 39 | ClusterableServer : Backbone.ClusterableServerSettings 40 | } 41 | 42 | /// The default settings for the . 43 | let defaultSettings = { 44 | Settings.Stream = Backbone.DefaultSettings.stream 45 | Settings.ConnectionPool = Backbone.DefaultSettings.connectionPool 46 | Settings.ClusterableServer = Backbone.DefaultSettings.clusterableServer 47 | } 48 | 49 | type MongoClient = 50 | 51 | val private backbone : MongoBackbone 52 | 53 | new (?settings0 : Client.Settings) = 54 | let settings = defaultArg settings0 Client.defaultSettings 55 | 56 | MongoClient({ Backbone.DefaultSettings.all with Stream = settings.Stream 57 | ConnectionPool = settings.ConnectionPool 58 | ClusterableServer = settings.ClusterableServer }) 59 | 60 | new (hosts : DnsEndPoint list, ?settings0 : Client.Settings) = 61 | let settings = defaultArg settings0 Client.defaultSettings 62 | 63 | MongoClient({ Backbone.DefaultSettings.all with Stream = settings.Stream 64 | ConnectionPool = settings.ConnectionPool 65 | ClusterableServer = settings.ClusterableServer 66 | Hosts = hosts }) 67 | 68 | new (replicaSet : string, hosts : DnsEndPoint list, ?settings0 : Client.Settings) = 69 | let settings = defaultArg settings0 Client.defaultSettings 70 | 71 | MongoClient({ Backbone.DefaultSettings.all with Stream = settings.Stream 72 | ConnectionPool = settings.ConnectionPool 73 | ClusterableServer = settings.ClusterableServer 74 | Hosts = hosts 75 | ReplicaSet = Some replicaSet }) 76 | 77 | private new (settings : Backbone.AllSettings) = 78 | do Conventions.register() 79 | do Serializers.register() 80 | 81 | { backbone = MongoBackbone(settings) } 82 | 83 | interface IMongoClient with 84 | member x.GetDatabase db = MongoDatabase(x.backbone, db) :> IMongoDatabase 85 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoCollection.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open System.Collections 19 | open System.Collections.Generic 20 | 21 | open MongoDB.Bson 22 | open MongoDB.Bson.Serialization 23 | 24 | open MongoDB.Driver.Core 25 | open MongoDB.Driver.Core.Protocol 26 | open MongoDB.Driver.Core.Protocol.Messages 27 | 28 | [] 29 | type IMongoCollection<'DocType> = 30 | inherit IEnumerable<'DocType> 31 | 32 | abstract member Drop : unit -> CommandResult 33 | 34 | abstract member Insert : 'DocType -> WriteConcernResult 35 | 36 | abstract member Find : unit -> Scope<'DocType> 37 | 38 | abstract member Find : 'DocTypeOrExample -> Scope<'DocType> 39 | 40 | abstract member Save : 'DocType -> WriteConcernResult 41 | 42 | type MongoCollection<'DocType> = 43 | 44 | val private backbone : MongoBackbone 45 | val private db : string 46 | val private clctn : string 47 | 48 | internal new (backbone, db, clctn) = { 49 | backbone = backbone 50 | db = db 51 | clctn = clctn 52 | } 53 | 54 | interface IMongoCollection<'DocType> with 55 | member x.Drop () = x.backbone.DropCollection x.db x.clctn 56 | 57 | member x.Insert doc = 58 | let options = Scope.DefaultOptions.writeOptions 59 | 60 | let flags = InsertFlags.None 61 | let settings = { Operation.DefaultSettings.insert with WriteConcern = Some options.WriteConcern } 62 | 63 | x.backbone.Insert x.db x.clctn doc flags settings 64 | 65 | member x.Find () = 66 | (x :> IMongoCollection<'DocType>).Find(BsonDocument()) 67 | 68 | member x.Find (query0 : 'DocTypeOrExample) = 69 | let query = (box query0).ToBsonDocument(query0.GetType()) 70 | 71 | { 72 | Backbone = x.backbone 73 | Database = x.db 74 | Collection = x.clctn 75 | 76 | Query = Some query 77 | Project = None 78 | Sort = None 79 | 80 | Limit = 0 81 | Skip = 0 82 | 83 | QueryOptions = Scope.DefaultOptions.queryOptions 84 | ReadPreference = ReadPreference.Primary 85 | WriteOptions = Scope.DefaultOptions.writeOptions 86 | } 87 | 88 | member x.Save doc = 89 | let options = Scope.DefaultOptions.writeOptions 90 | 91 | let idProvider = 92 | match BsonSerializer.LookupSerializer(doc.GetType()) with 93 | | :? IBsonIdProvider as x -> x 94 | | _ -> failwithf "could not find id provider for document type %O" <| doc.GetType() 95 | 96 | let id = ref null 97 | let idType = ref null 98 | let idGenerator = ref null 99 | 100 | if idProvider.GetDocumentId(doc, id, idType, idGenerator) then // document has an id 101 | // Perform an upsert 102 | let query = BsonDocument("_id", BsonValue.Create(!id)) 103 | let update = doc 104 | 105 | let flags = UpdateFlags.Upsert 106 | let settings = { Operation.DefaultSettings.update with WriteConcern = Some options.WriteConcern } 107 | 108 | x.backbone.Update x.db x.clctn query update flags settings 109 | else // document does not have an id 110 | // Perform an insert 111 | let flags = InsertFlags.None 112 | let settings = { Operation.DefaultSettings.insert with WriteConcern = Some options.WriteConcern 113 | AssignIdOnInsert = Some true} 114 | 115 | x.backbone.Insert x.db x.clctn doc flags settings 116 | 117 | interface IEnumerable<'DocType> with 118 | member x.GetEnumerator() = (x :> IMongoCollection<'DocType>).Find().Get() 119 | 120 | interface IEnumerable with 121 | member x.GetEnumerator() = (x :> IEnumerable<'DocType>).GetEnumerator() :> IEnumerator 122 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoDatabase.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open MongoDB.Bson 19 | 20 | open MongoDB.Driver.Core 21 | 22 | [] 23 | /// Represents a database of the cluster. 24 | type IMongoDatabase = 25 | /// Drops the specified database. 26 | abstract member Drop : unit -> CommandResult 27 | 28 | /// Returns the specified collection. 29 | /// The collection name. 30 | /// Returns 31 | abstract member GetCollection : string -> IMongoCollection 32 | 33 | /// 34 | /// Returns the specified collection, parametrized by the generic type. 35 | /// 36 | /// The collection name. 37 | abstract member GetCollection<'DocType> : string -> IMongoCollection<'DocType> 38 | 39 | type internal MongoDatabase = 40 | 41 | val private backbone : MongoBackbone 42 | val private db : string 43 | 44 | internal new (backbone, db) = { 45 | backbone = backbone 46 | db = db 47 | } 48 | 49 | interface IMongoDatabase with 50 | member x.Drop () = x.backbone.DropDatabase x.db 51 | 52 | member x.GetCollection clctn = 53 | (x :> IMongoDatabase).GetCollection clctn 54 | 55 | member x.GetCollection<'DocType> clctn = 56 | MongoCollection<'DocType>(x.backbone, x.db, clctn) :> IMongoCollection<'DocType> 57 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoOperationSettings.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open MongoDB.Bson.IO 19 | 20 | open MongoDB.Driver.Core 21 | 22 | [] 23 | /// Provides configuration of the database- and collection-level operations. 24 | module Operation = 25 | 26 | [] 27 | /// Settings for the . 28 | type CommandSettings = { 29 | ReaderSettings : BsonBinaryReaderSettings option 30 | WriterSettings : BsonBinaryWriterSettings option 31 | } 32 | 33 | [] 34 | /// Settings for the . 35 | type InsertSettings = { 36 | ReaderSettings : BsonBinaryReaderSettings option 37 | WriterSettings : BsonBinaryWriterSettings option 38 | WriteConcern : WriteConcern option 39 | AssignIdOnInsert : bool option 40 | CheckInsertDocuments : bool option 41 | } 42 | 43 | [] 44 | /// Settings for the . 45 | type QuerySettings = { 46 | ReaderSettings : BsonBinaryReaderSettings option 47 | WriterSettings : BsonBinaryWriterSettings option 48 | BatchSize : int option 49 | } 50 | 51 | [] 52 | /// Settings for the . 53 | type UpdateSettings = { 54 | ReaderSettings : BsonBinaryReaderSettings option 55 | WriterSettings : BsonBinaryWriterSettings option 56 | WriteConcern : WriteConcern option 57 | CheckUpdateDocument : bool option 58 | } 59 | 60 | [] 61 | /// Settings for the . 62 | type RemoveSettings = { 63 | ReaderSettings : BsonBinaryReaderSettings option 64 | WriterSettings : BsonBinaryWriterSettings option 65 | WriteConcern : WriteConcern option 66 | } 67 | 68 | [] 69 | module DefaultSettings = 70 | 71 | /// The default settings for the . 72 | /// Designed to override defaults for only the specific fields. 73 | let command = { 74 | CommandSettings.ReaderSettings = None 75 | CommandSettings.WriterSettings = None 76 | } 77 | 78 | /// The defaults settings for the . 79 | /// Designed to override defaults for only the specific fields. 80 | let insert = { 81 | InsertSettings.ReaderSettings = None 82 | InsertSettings.WriterSettings = None 83 | InsertSettings.WriteConcern = None 84 | InsertSettings.AssignIdOnInsert = None 85 | InsertSettings.CheckInsertDocuments = None 86 | } 87 | 88 | /// The defaults settings for the . 89 | /// Designed to override defaults for only the specific fields. 90 | let query = { 91 | QuerySettings.ReaderSettings = None 92 | QuerySettings.WriterSettings = None 93 | QuerySettings.BatchSize = None 94 | } 95 | 96 | /// The defaults settings for the . 97 | /// Designed to override defaults for only the specific fields. 98 | let update = { 99 | UpdateSettings.ReaderSettings = None 100 | UpdateSettings.WriterSettings = None 101 | UpdateSettings.WriteConcern = None 102 | UpdateSettings.CheckUpdateDocument = None 103 | } 104 | 105 | /// The defaults settings for the . 106 | /// Designed to override defaults for only the specific fields. 107 | let remove = { 108 | RemoveSettings.ReaderSettings = None 109 | RemoveSettings.WriterSettings = None 110 | RemoveSettings.WriteConcern = None 111 | } 112 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoScope.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open System.Collections 19 | open System.Collections.Generic 20 | 21 | open MongoDB.Bson 22 | 23 | open MongoDB.Driver.Core 24 | open MongoDB.Driver.Core.Protocol 25 | open MongoDB.Driver.Core.Protocol.Messages 26 | 27 | [] 28 | module private Helpers = 29 | 30 | // version of BsonDocument.Add that can be chained in a pipeline. 31 | let addElem name value (doc : BsonDocument) = 32 | match value with 33 | | Some x -> doc.Add(name, BsonValue.Create x) 34 | | None -> doc 35 | 36 | // prepares a $query enclosed query document 37 | let makeQueryDoc query sort (options : Scope.QueryOptions) = 38 | match sort with 39 | | None when options = Scope.DefaultOptions.queryOptions -> 40 | match query with 41 | | Some x -> x 42 | | None -> BsonDocument() 43 | 44 | | _ -> 45 | match query with 46 | | Some x -> 47 | BsonDocument("$query", x) 48 | |> addElem "$orderby" sort 49 | |> addElem "$comment" options.Comment 50 | |> addElem "$hint" options.Hint 51 | |> addElem "$maxScan" options.MaxScan 52 | |> addElem "$max" options.Max 53 | |> addElem "$min" options.Min 54 | |> addElem "$snapshot" options.Snapshot 55 | 56 | | None -> failwith "unset query" 57 | 58 | // prepares a text search document 59 | let makeTextSearchDoc clctn text query project limit (options : Scope.TextSearchOptions) = 60 | BsonDocument([ BsonElement("text", BsonString(clctn)) 61 | BsonElement("search", BsonString(text)) 62 | BsonElement("limit", BsonInt32(limit)) ]) 63 | |> addElem "filter" query 64 | |> addElem "project" project 65 | |> addElem "language" options.Language 66 | 67 | /// Represents a view over a particular collection. 68 | /// Immutable structure that is able to be chained with other methods 69 | /// that can invoke operations against the database. 70 | type Scope<'DocType> = private { 71 | Backbone : MongoBackbone 72 | Database : string 73 | Collection : string 74 | 75 | Query : BsonDocument option 76 | Project : BsonDocument option 77 | Sort : BsonDocument option 78 | 79 | Limit : int 80 | Skip : int 81 | 82 | QueryOptions : Scope.QueryOptions 83 | ReadPreference : ReadPreference 84 | WriteOptions : Scope.WriteOptions 85 | } with 86 | /// Executes the find operation with the previously supplied settings. 87 | /// Returns an enumerator for explicit iteration, rather than use in a for loop. 88 | member x.Get (?flags0) = // TODO: change to take a CursorOptions instance 89 | let flags = defaultArg flags0 QueryFlags.None 90 | 91 | let backbone = x.Backbone 92 | let db = x.Database 93 | let clctn = x.Collection 94 | 95 | let query = makeQueryDoc x.Query x.Sort x.QueryOptions 96 | 97 | let project = 98 | match x.Project with 99 | | Some x -> x 100 | | None -> null 101 | 102 | let limit = x.Limit 103 | let skip = x.Skip 104 | 105 | let settings = Operation.DefaultSettings.query 106 | 107 | let cursor = backbone.Find<'DocType> db clctn query project limit skip flags settings 108 | cursor.GetEnumerator() 109 | 110 | interface IEnumerable<'DocType> with 111 | member x.GetEnumerator() = x.Get() 112 | 113 | interface IEnumerable with 114 | member x.GetEnumerator() = (x :> IEnumerable<'DocType>).GetEnumerator() :> IEnumerator 115 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/MongoScopeOptions.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open System.Collections 19 | open System.Collections.Generic 20 | 21 | open MongoDB.Bson 22 | open MongoDB.Bson.Serialization 23 | 24 | open MongoDB.Driver.Core 25 | open MongoDB.Driver.Core.Protocol 26 | 27 | [] 28 | [] 29 | module Scope = 30 | 31 | type QueryOptions = { 32 | Comment : string option 33 | Hint : BsonDocument option 34 | MaxScan : int option 35 | Max : obj option 36 | Min : obj option 37 | Snapshot : bool option 38 | } 39 | 40 | type WriteOptions = { 41 | Isolated : bool 42 | WriteConcern : WriteConcern 43 | Others : (string * obj) list 44 | } 45 | 46 | type TextSearchOptions = { 47 | Language : string option 48 | } 49 | 50 | [] 51 | module DefaultOptions = 52 | let queryOptions = { 53 | Comment = None 54 | Hint = None 55 | MaxScan = None 56 | Max = None 57 | Min = None 58 | Snapshot = None 59 | } 60 | 61 | let writeOptions = { 62 | Isolated = false 63 | WriteConcern = WriteConcern.Acknowledged 64 | Others = [] 65 | } 66 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/Properties/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver 17 | 18 | open System.Runtime.CompilerServices 19 | 20 | [] 21 | 22 | () -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/QuotableMongo.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver.Quotations 17 | 18 | open Microsoft.FSharp.Quotations 19 | open Microsoft.FSharp.Quotations.Patterns 20 | open Microsoft.FSharp.Quotations.DerivedPatterns 21 | open Microsoft.FSharp.Reflection 22 | 23 | open MongoDB.Bson 24 | 25 | [] 26 | /// 27 | /// Contains definition of bson function to parse quotations. 28 | /// 29 | module Impl = 30 | 31 | let private queryParser = Query.parser 32 | 33 | let private updateParser = Update.parser 34 | 35 | /// 36 | /// Traverses an expression and constructs an equivalent BsonDocument based on it structure. 37 | /// When the quotation has a type signature of 'a -> bool, then it is parsed as a query. 38 | /// When the quotation has a type signature of 'a -> unit list 39 | /// or 'a -> 'a, then it is parsed as an update. 40 | /// 41 | /// The code quotation to traverse. 42 | /// A BsonDocument representing the query or update operation. 43 | let bson (q : Expr<'a -> 'b>) = 44 | match box q with 45 | | :? Expr<'a -> bool> as q -> 46 | match q with 47 | | ExprShape.ShapeLambda (v, body) -> 48 | match queryParser v body with 49 | | Some x -> BsonDocument x 50 | | None -> failwithf "unable to parse query\n%A" body 51 | 52 | | _ -> failwith "expected lambda expression" 53 | 54 | | :? Expr<'a -> unit list> as q -> 55 | match q with 56 | | ExprShape.ShapeLambda (v, List (exprs, _)) -> 57 | 58 | let parse = function 59 | | SetField (var, field, value) when var = v -> updateParser var field value 60 | 61 | | SpecificCall <@ (|>) @> (_, _, [ SpecificCall <@ (|>) @> (_, _, [ Var (var); Let (_, List (values, _), Lambda (_, SpecificCall <@ Update.rename @> _)) ]) 62 | Lambda (_, SpecificCall <@ ignore @> _) ]) when var = v -> 63 | let zipTransform expr = 64 | match expr with 65 | | NewTuple ([ String (left); String (right) ]) -> BsonElement(left, BsonString(right)) 66 | | _ -> failwith "expected (string * string) tuple" 67 | 68 | Some (BsonElement("$rename", BsonDocument(List.map (unbox >> zipTransform) values))) 69 | 70 | | _ -> failwith "unrecognized pattern" 71 | 72 | let doc = BsonDocument() 73 | 74 | exprs |> Seq.cast 75 | |> Seq.iter (fun q -> 76 | match parse q with 77 | | Some elem -> 78 | if not (doc.Contains elem.Name) then 79 | doc.Add (elem.Name, BsonDocument()) |> ignore 80 | 81 | doc.[elem.Name].AsBsonDocument.AddRange elem.Value.AsBsonDocument |> ignore 82 | | None -> () // TODO: raise exception 83 | ) 84 | 85 | doc 86 | 87 | | _ -> failwith "expected lambda expression" 88 | 89 | | :? Expr<'a -> 'a> as q -> 90 | match q with 91 | | ExprShape.ShapeLambda (v, body) -> 92 | // handles the { x with foo = ...; bar = ...; ... } form 93 | // 94 | // NOTE: a special case exists for when the first field 95 | // (in the record type definition) is modified, 96 | // as no Let expression is used because it is the first constructor argument 97 | // 98 | // REVIEW: rename this active pattern to something a bit more meaningful? 99 | // e.g. MakeRecord, UpdateRecord 100 | let rec (|NestedLet|_|) expr = 101 | match expr with 102 | // case for when the first record field is unmodified 103 | | NewRecord (typ, PropertyGet _ :: _) -> None 104 | 105 | // case for when the first record field is modified 106 | | NewRecord (typ, value :: _) -> 107 | let field = FSharpType.GetRecordFields(typ).[0] 108 | Some([ field.Name ], [ value ]) 109 | 110 | // case for when multiple record fields are modified 111 | | Let (field, value, NestedLet (restFields, restValues)) -> 112 | Some(field.Name :: restFields, value :: restValues) 113 | 114 | // case for when single (or final in series of lets) is modified 115 | // TODO: verify the field (index) of the record type 116 | | Let (field, value, _) -> Some([ field.Name ], [ value ]) 117 | | _ -> None 118 | 119 | match body with 120 | | NestedLet (fields, values) -> 121 | let values = values |> List.filter (fun x -> match x with | PropertyGet _ -> false | _ -> true) 122 | let elems = List.map2 (updateParser v) fields values 123 | 124 | let doc = BsonDocument() 125 | 126 | List.map2 (updateParser v) fields values 127 | |> List.iter (fun x -> 128 | match x with 129 | | Some elem -> 130 | if not (doc.Contains elem.Name) then 131 | doc.Add (elem.Name, BsonDocument()) |> ignore 132 | 133 | doc.[elem.Name].AsBsonDocument.AddRange elem.Value.AsBsonDocument |> ignore 134 | | None -> () // TODO: raise exception 135 | ) 136 | 137 | doc 138 | 139 | | _ -> failwith "expected nested let or new record expression" 140 | 141 | | _ -> failwith "expected lambda expression" 142 | 143 | | _ -> failwith "unrecognized expression" 144 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/Quotations/HelperPatterns.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver.Quotations 17 | 18 | open Microsoft.FSharp.Quotations 19 | open Microsoft.FSharp.Quotations.Patterns 20 | open Microsoft.FSharp.Quotations.DerivedPatterns 21 | open Microsoft.FSharp.Reflection 22 | 23 | open MongoDB.Bson 24 | 25 | [] 26 | module CustomOps = 27 | 28 | let (?) (doc : BsonDocument) (field : string) = 29 | unbox doc.[field] 30 | 31 | let (?<-) (doc : BsonDocument) (field : string) value = 32 | doc.[field] = unbox value |> ignore 33 | 34 | let (=~) input pattern = 35 | System.Text.RegularExpressions.Regex.IsMatch(input, pattern) 36 | 37 | [] 38 | module internal Helpers = 39 | 40 | let inline isGenericTypeDefinedFrom<'a> (typ : System.Type) = 41 | typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<'a> 42 | 43 | let inline isListUnionCase (uci : UnionCaseInfo) = 44 | uci.DeclaringType |> isGenericTypeDefinedFrom> 45 | 46 | let private makeGenericListType typ = 47 | typedefof>.MakeGenericType [| typ |] 48 | 49 | let rec (|List|_|) = function 50 | | NewUnionCase (uci, args) when isListUnionCase uci -> 51 | match args with 52 | | [ Value (head, typ); List (tail, _) ] -> Some (head :: tail, typ) 53 | | [ List (head, typ); List (tail, _) ] -> Some (box head :: tail, makeGenericListType typ) 54 | | [ head; List (tail, _) ] -> Some (box head :: tail, typeof) 55 | | [] -> Some ([], typedefof<_>) 56 | | _ -> None 57 | 58 | | _ -> None 59 | 60 | let (|ValueOrList|_|) = function 61 | | Value (value, typ) -> Some (value, typ) 62 | | List (values, typ) -> Some (box values, makeGenericListType typ) 63 | | _ -> None 64 | 65 | let rec (|GetProperty|_|) = function 66 | | PropertyGet (Some (Var (var)), prop, []) -> 67 | Some (var, prop.Name) 68 | 69 | | PropertyGet (Some (GetProperty (var, subdoc)), prop, []) -> 70 | Some (var, sprintf "%s.%s" subdoc prop.Name) 71 | 72 | | _ -> None 73 | 74 | let rec (|CallDynamic|_|) = function 75 | | SpecificCall <@ (?) @> (_, _, [ Var (var); String (field) ]) 76 | | Coerce (CallDynamic (var, field), _) -> 77 | Some (var, field) 78 | 79 | | SpecificCall <@ (?) @> (_, _, [ CallDynamic (var, subdoc); String (field) ]) 80 | | SpecificCall <@ (?) @> (_, _, [ GetProperty (var, subdoc); String (field) ]) -> 81 | Some (var, sprintf "%s.%s" subdoc field) 82 | 83 | | _ -> None 84 | 85 | let (|GetField|_|) = function 86 | | CallDynamic (var, field) -> Some (var, field) 87 | | GetProperty (var, field) -> Some (var, field) 88 | | _ -> None 89 | 90 | let (|CallDynamicAssignment|_|) = function 91 | | SpecificCall <@ (?<-) @> (_, _, [ Var (var); String (field); value ]) -> 92 | Some (var, field, value) 93 | 94 | | SpecificCall <@ (?<-) @> (_, _, [ GetField (var, subdoc); String (field); value ]) -> 95 | Some (var, sprintf "%s.%s" subdoc field, value) 96 | 97 | | _ -> None 98 | 99 | let (|SetProperty|_|) = function 100 | | PropertySet (Some (Var (var)), prop, [], value) -> 101 | Some (var, prop.Name, value) 102 | 103 | | PropertySet (Some (GetProperty (var, subdoc)), prop, [], value) -> 104 | Some (var, sprintf "%s.%s" subdoc prop.Name, value) 105 | 106 | | _ -> None 107 | 108 | let (|SetField|_|) = function 109 | | CallDynamicAssignment (var, field, value) -> Some (var, field, value) 110 | | SetProperty (var, field, value) -> Some (var, field, value) 111 | | _ -> None 112 | 113 | let (|InfixOp|_|) op = function 114 | | SpecificCall <@ %op @> (_, _, [ lhs; rhs ]) -> Some (lhs, rhs) 115 | | _ -> None 116 | 117 | let (|CallForwardPipe|_|) = function 118 | | InfixOp <@ (|>) @> (x, f) -> Some (x, f) 119 | | _ -> None 120 | 121 | let (|CallForwardCompose|_|) = function 122 | | InfixOp <@ (>>) @> (x, f) -> Some (x, f) 123 | | _ -> None 124 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/Quotations/QueryPatterns.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver.Quotations 17 | 18 | open Microsoft.FSharp.Quotations 19 | open Microsoft.FSharp.Quotations.Patterns 20 | open Microsoft.FSharp.Quotations.DerivedPatterns 21 | open Microsoft.FSharp.Reflection 22 | 23 | open MongoDB.Bson 24 | 25 | [] 26 | module Query = 27 | 28 | let all (x : 'a list) (y : 'a list) : bool = invalidOp "not implemented" 29 | 30 | let in' (x : 'a list) (y : 'a) : bool = invalidOp "not implemented" 31 | 32 | let nin (x : 'a list) (y : 'a) : bool = invalidOp "not implemented" 33 | 34 | let nor (x : bool list) : bool = invalidOp "not implemented" 35 | 36 | let exists x : bool = invalidOp "not implemented" 37 | 38 | let nexists x : bool = invalidOp "not implemented" 39 | 40 | let type' (x : BsonType) y : bool = invalidOp "not implemented" 41 | 42 | let where (x : string) y : bool = invalidOp "not implemented" 43 | 44 | let elemMatch (x : 'a -> bool) (y : 'a list) : bool = invalidOp "not implemented" 45 | 46 | let size (x : int) y : bool = invalidOp "not implemented" 47 | 48 | let private toDoc (elem : BsonElement) = BsonDocument elem 49 | 50 | let rec internal parser v q = 51 | let (|Comparison|_|) op = function 52 | | InfixOp op (GetField (var, field), ValueOrList (value, _)) when var = v -> 53 | Some(field, value) 54 | | _ -> None 55 | 56 | match q with 57 | | Comparison <@ (=) @> (field, value) -> 58 | Some (BsonElement(field, BsonValue.Create value)) 59 | 60 | | InfixOp <@ (=) @> (InfixOp <@ (%) @> (GetField (var, field), Int32 (divisor)), 61 | Int32 (remainder)) when var = v -> 62 | Some (BsonElement(field, BsonDocument("$mod", BsonArray([ divisor; remainder ])))) 63 | 64 | | Comparison <@ (<>) @> (field, value) -> 65 | Some (BsonElement(field, BsonDocument("$ne", BsonValue.Create value))) 66 | 67 | | Comparison <@ (>) @> (field, value) -> 68 | Some (BsonElement(field, BsonDocument("$gt", BsonValue.Create value))) 69 | 70 | | Comparison <@ (>=) @> (field, value) -> 71 | Some (BsonElement(field, BsonDocument("$gte", BsonValue.Create value))) 72 | 73 | | Comparison <@ (<) @> (field, value) -> 74 | Some (BsonElement(field, BsonDocument("$lt", BsonValue.Create value))) 75 | 76 | | Comparison <@ (<=) @> (field, value) -> 77 | Some (BsonElement(field, BsonDocument("$lte", BsonValue.Create value))) 78 | 79 | | InfixOp <@ (=~) @> (GetField (var, field), String (pcre)) when var = v -> 80 | let index = pcre.LastIndexOf('/') 81 | let regex = pcre.Substring(1, index - 1) 82 | let options = pcre.Substring(index + 1) 83 | Some (BsonElement(field, BsonDocument([ BsonElement("$regex", BsonString(regex)) 84 | BsonElement("$options", BsonString(options)) ]))) 85 | 86 | | CallForwardPipe (Var (var), expr) when var = v -> 87 | match expr with 88 | | Let (_, String (js), Lambda (_, SpecificCall <@ where @> _)) -> 89 | Some (BsonElement("$where", BsonString(js))) 90 | 91 | | _ -> None 92 | 93 | | CallForwardPipe (GetField (var, field), expr) when var = v -> 94 | match expr with 95 | | Let (_, List (value, _), Lambda (_, SpecificCall <@ all @> _)) -> 96 | Some (BsonElement(field, BsonDocument("$all", BsonValue.Create value))) 97 | 98 | | Let (_, List (value, _), Lambda (_, SpecificCall <@ in' @> _)) -> 99 | Some (BsonElement(field, BsonDocument("$in", BsonValue.Create value))) 100 | 101 | | Let (_, List (value, _), Lambda (_, SpecificCall <@ nin @> _)) -> 102 | Some (BsonElement(field, BsonDocument("$nin", BsonValue.Create value))) 103 | 104 | | Lambda(_, SpecificCall <@ exists @> _) -> 105 | Some (BsonElement(field, BsonDocument("$exists", BsonBoolean(true)))) 106 | 107 | | Lambda(_, SpecificCall <@ nexists @> _) -> 108 | Some (BsonElement(field, BsonDocument("$exists", BsonBoolean(false)))) 109 | 110 | | Let (_, Value (value, _), Lambda (_, SpecificCall <@ type' @> _)) -> 111 | let typ = value :?> BsonType 112 | Some (BsonElement(field, BsonDocument("$type", BsonValue.Create typ))) 113 | 114 | | Let (_, Lambda (v, q), Lambda (_, SpecificCall <@ elemMatch @> _)) -> 115 | match parser v q with 116 | | Some elem -> 117 | let doc = 118 | if elem.Name = "$and" then // { $and: [ cond1; cond2; ... ] } -> { cond1, cond2, ... } 119 | elem.Value.AsBsonArray.Values 120 | |> Seq.cast 121 | |> Seq.map (fun (x : BsonDocument) -> x.GetElement 0) 122 | |> (fun x -> BsonDocument x) 123 | 124 | else toDoc elem 125 | 126 | Some (BsonElement(field, BsonDocument("$elemMatch", doc))) 127 | 128 | | None -> None 129 | 130 | | Let (_, Int32 (value), Lambda(_, SpecificCall <@ size @> _)) -> 131 | Some (BsonElement(field, BsonDocument("$size", BsonInt32(value)))) 132 | 133 | | _ -> None 134 | 135 | | SpecificCall <@ nor @> (_, _, [ List (exprs, _) ]) -> 136 | let elems = exprs |> Seq.cast 137 | |> Seq.fold (fun res q -> 138 | match res with 139 | | Some tail -> 140 | match parser v q with 141 | | Some elem -> Some (toDoc elem :: tail) 142 | | None -> None 143 | | None -> None) (Some []) 144 | |> Option.map List.rev 145 | 146 | match elems with 147 | | Some x -> Some (BsonElement("$nor", BsonArray(x))) 148 | | None -> None 149 | 150 | | SpecificCall <@ not @> (_, _, [ expr ]) -> 151 | match parser v expr with 152 | | Some elem -> 153 | elem.Value <- BsonDocument("$not", elem.Value) 154 | Some elem 155 | | None -> None 156 | 157 | | AndAlso (lhs, rhs) -> 158 | match parser v lhs with 159 | | Some lhsElem -> 160 | match parser v rhs with 161 | | Some rhsElem -> 162 | if lhsElem.Name = "$and" then 163 | lhsElem.Value.AsBsonArray.Add(toDoc rhsElem) |> ignore 164 | Some lhsElem 165 | 166 | else Some (BsonElement("$and", BsonArray([ lhsElem; rhsElem ] |> Seq.map toDoc))) 167 | 168 | | None -> None 169 | 170 | | None -> None 171 | 172 | | OrElse (lhs, rhs) -> 173 | match parser v lhs with 174 | | Some lhsElem -> 175 | match parser v rhs with 176 | | Some rhsElem -> 177 | if lhsElem.Name = "$or" then 178 | lhsElem.Value.AsBsonArray.Add(toDoc rhsElem) |> ignore 179 | Some lhsElem 180 | 181 | else Some (BsonElement("$or", BsonArray([ lhsElem; rhsElem ] |> Seq.map toDoc))) 182 | 183 | | None -> None 184 | 185 | | None -> None 186 | 187 | | _ -> None 188 | -------------------------------------------------------------------------------- /src/FSharp.MongoDB.Driver/Quotations/UpdatePatterns.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver.Quotations 17 | 18 | open Microsoft.FSharp.Quotations 19 | open Microsoft.FSharp.Quotations.Patterns 20 | open Microsoft.FSharp.Quotations.DerivedPatterns 21 | open Microsoft.FSharp.Reflection 22 | 23 | open MongoDB.Bson 24 | 25 | [] 26 | module Update = 27 | 28 | let rename (x : (string * string) list) (y : 'a) : 'a = invalidOp "not implemented" 29 | 30 | let setOnInsert (x : unit list) (y : 'a) : 'a = invalidOp "not implemented" 31 | 32 | let addToSet (x : 'a) (y : 'a list) : 'a list = invalidOp "not implemented" 33 | 34 | let popleft (x : 'a list) : 'a list = invalidOp "not implemented" 35 | 36 | let popright (x : 'a list) : 'a list = invalidOp "not implemented" 37 | 38 | let pull (x : 'a -> bool) (y : 'a list) : 'a list = invalidOp "not implemented" 39 | 40 | let pullAll (x : 'a list) (y : 'a list) : 'a list = invalidOp "not implemented" 41 | 42 | let push (x : 'a) (y : 'a list) : 'a list = invalidOp "not implemented" 43 | 44 | let addToSetEach (x : 'a list) (y : 'a list) : 'a list = invalidOp "not implemented" 45 | 46 | let pushEach (x : 'a list) (y : 'a list) : 'a list = invalidOp "not implemented" 47 | 48 | let slice (x : int) (y : 'a list) : 'a list = invalidOp "not implemented" 49 | 50 | let sortListBy (x : 'a -> 'b) (y : 'a list) : 'a list = invalidOp "not implemented" 51 | 52 | let sortListByDescending (x : 'a -> 'b) (y : 'a list) : 'a list = invalidOp "not implemented" 53 | 54 | let thenListBy (x : 'a -> 'b) (y : 'a list) : 'a list = invalidOp "not implemented" 55 | 56 | let thenListByDescending (x : 'a -> 'b) (y : 'a list) : 'a list = invalidOp "not implemented" 57 | 58 | let private toDoc (elem : BsonElement) = BsonDocument(elem) 59 | 60 | let private queryParser = Query.parser 61 | 62 | let internal parser v field q = 63 | let rec (|DeSugared|_|) v f op = function 64 | | Lambda (_, SpecificCall <@ %op @> _) -> Some [] 65 | | Let (_, value, DeSugared v f op (rest)) -> Some (value :: rest) 66 | 67 | | CallForwardPipe (GetField (var, field), 68 | DeSugared v f op (values)) when var = v && field = f -> 69 | Some values 70 | 71 | | InfixOp op (GetField (var, field), value) when var = v && field = f -> 72 | Some [ value ] 73 | 74 | | _ -> None 75 | 76 | let (|PushEach|_|) var field = function 77 | | DeSugared var field <@ pushEach @> ([ List (values, _) ]) -> 78 | let array = 79 | values 80 | |> List.map (fun x -> 81 | try 82 | BsonValue.Create x 83 | with 84 | | :? System.ArgumentException -> x.ToBsonDocument(x.GetType()) :> BsonValue) 85 | 86 | let elem = BsonElement("$push", BsonDocument(field, BsonDocument("$each", BsonArray(array)))) 87 | Some elem 88 | 89 | | _ -> None 90 | 91 | let rec (|PushEachWithModifiers|_|) var array = function 92 | | CallForwardPipe (PushEach var array (elem), Let (_, Int32 (value), Lambda (_, SpecificCall <@ slice @> _))) 93 | | CallForwardPipe (PushEachWithModifiers var array (elem), Let (_, Int32 (value), Lambda (_, SpecificCall <@ slice @> _))) -> 94 | elem.Value.[0].AsBsonDocument.Set("$slice", BsonInt32(value)) |> ignore // e.g. { $push: { : { $each ... } } 95 | Some elem 96 | 97 | | CallForwardCompose (PushEach var array (elem), Let (_, Int32(value), Lambda (_, SpecificCall <@ slice @> _))) 98 | | CallForwardCompose (PushEachWithModifiers var array (elem), Let (_, Int32(value), Lambda (_, SpecificCall <@ slice @> _))) -> 99 | elem.Value.[0].AsBsonDocument.Set("$slice", BsonInt32(value)) |> ignore // e.g. { $push: { : { $each ... } } 100 | Some elem 101 | 102 | | CallForwardPipe (PushEach var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListBy @> _))) 103 | | CallForwardPipe (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListBy @> _))) -> 104 | // overwrite, since using last assigned value 105 | elem.Value.[0].AsBsonDocument.Set("$sort", BsonDocument(field, BsonInt32(1))) |> ignore // e.g. { $push: { : { $each ... } } 106 | Some elem 107 | 108 | | CallForwardCompose (PushEach var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListBy @> _))) 109 | | CallForwardCompose (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListBy @> _))) -> 110 | // overwrite, since using last assigned value 111 | elem.Value.[0].AsBsonDocument.Set("$sort", BsonDocument(field, BsonInt32(1))) |> ignore // e.g. { $push: { : { $each ... } } 112 | Some elem 113 | 114 | | CallForwardPipe (PushEach var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListByDescending @> _))) 115 | | CallForwardPipe (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListByDescending @> _))) -> 116 | // overwrite, since using last assigned value 117 | elem.Value.[0].AsBsonDocument.Set("$sort", BsonDocument(field, BsonInt32(-1))) |> ignore // e.g. { $push: { : { $each ... } } 118 | Some elem 119 | 120 | | CallForwardCompose (PushEach var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListByDescending @> _))) 121 | | CallForwardCompose (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ sortListByDescending @> _))) -> 122 | // overwrite, since using last assigned value 123 | elem.Value.[0].AsBsonDocument.Set("$sort", BsonDocument(field, BsonInt32(-1))) |> ignore // e.g. { $push: { : { $each ... } } 124 | Some elem 125 | 126 | | CallForwardPipe (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ thenListBy @> _))) 127 | | CallForwardCompose (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ thenListBy @> _))) -> 128 | // append to $sort document 129 | let sort = elem.Value.[0].AsBsonDocument.GetValue("$sort").AsBsonDocument 130 | // overwrite, since using last assigned value 131 | sort.Set(field, BsonInt32(1)) |> ignore 132 | Some elem 133 | 134 | | CallForwardPipe (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ thenListByDescending @> _))) 135 | | CallForwardCompose (PushEachWithModifiers var array (elem), Let (_, Lambda (_, GetField (_, field)), Lambda (_, SpecificCall <@ thenListByDescending @> _))) -> 136 | // append to $sort document 137 | let sort = elem.Value.[0].AsBsonDocument.GetValue("$sort").AsBsonDocument 138 | // overwrite, since using last assigned value 139 | sort.Set(field, BsonInt32(-1)) |> ignore 140 | Some elem 141 | 142 | | _ -> None 143 | 144 | match q with 145 | | DeSugared v field <@ (+) @> ([ Int32 (value) ]) -> 146 | Some (BsonElement("$inc", BsonDocument(field, BsonInt32(value)))) 147 | 148 | | DeSugared v field <@ (-) @> ([ Int32 (value) ]) -> 149 | Some (BsonElement("$inc", BsonDocument(field, BsonInt32(-value)))) 150 | 151 | | Value (value, _) -> 152 | Some (BsonElement("$set", BsonDocument(field, BsonValue.Create value))) 153 | 154 | | List (values, _) -> 155 | Some (BsonElement("$set", BsonDocument(field, BsonArray(values)))) 156 | 157 | | NewUnionCase (uci, []) when uci.DeclaringType |> isGenericTypeDefinedFrom> -> 158 | Some (BsonElement("$unset", BsonDocument(field, BsonInt32(1)))) 159 | 160 | | DeSugared v field <@ addToSet @> ([ Value (value, _) ]) -> 161 | Some (BsonElement("$addToSet", BsonDocument(field, BsonValue.Create value))) 162 | 163 | | DeSugared v field <@ addToSet @> ([ List (values, _) ]) -> 164 | Some (BsonElement("$addToSet", BsonDocument(field, BsonArray(values)))) 165 | 166 | | DeSugared v field <@ popleft @> ([]) -> 167 | Some (BsonElement("$pop", BsonDocument(field, BsonInt32(-1)))) 168 | 169 | | DeSugared v field <@ popright @> ([]) -> 170 | Some (BsonElement("$pop", BsonDocument(field, BsonInt32(1)))) 171 | 172 | | DeSugared v field <@ pull @> ([ Lambda (v, q) ]) -> 173 | match queryParser v q with 174 | | Some elem -> Some (BsonElement("$pull", BsonDocument(field, toDoc elem))) 175 | | None -> None 176 | 177 | | DeSugared v field <@ pullAll @> ([ List (values, _) ]) -> 178 | Some (BsonElement("$pullAll", BsonDocument(field, BsonArray(values)))) 179 | 180 | | DeSugared v field <@ push @> ([ Value (value, _) ]) -> 181 | Some (BsonElement("$push", BsonDocument(field, BsonValue.Create value))) 182 | 183 | | DeSugared v field <@ push @> ([ List (values, _) ]) -> 184 | Some (BsonElement("$push", BsonDocument(field, BsonArray(values)))) 185 | 186 | | DeSugared v field <@ addToSetEach @> ([ List (values, _) ]) -> 187 | Some (BsonElement("$addToSet", BsonDocument(field, BsonDocument("$each", BsonArray(values))))) 188 | 189 | | PushEach v field (elem) -> Some elem 190 | 191 | | PushEachWithModifiers v field (elem) -> Some elem 192 | 193 | | DeSugared v field <@ (&&&) @> ([ Int32 (value) ]) -> 194 | Some (BsonElement("$bit", BsonDocument(field, BsonDocument("and", BsonInt32(value))))) 195 | 196 | | DeSugared v field <@ (|||) @> ([ Int32 (value) ]) -> 197 | Some (BsonElement("$bit", BsonDocument(field, BsonDocument("or", BsonInt32(value))))) 198 | 199 | | _ -> None 200 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Bson.Tests/FSharp.MongoDB.Bson.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 3e2afa32-3b88-4354-a01d-31513fa9eef9 9 | Library 10 | FSharp.MongoDB.Bson.Tests 11 | FSharp.MongoDB.Bson.Tests 12 | v4.5 13 | FSharp.MongoDB.Bson.Tests 14 | 15 | 16 | true 17 | full 18 | false 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | 3 23 | bin\Debug\FSharp.MongoDB.Bson.Tests.xml 24 | 25 | 26 | pdbonly 27 | true 28 | true 29 | bin\Release\ 30 | TRACE 31 | 3 32 | bin\Release\FSharp.MongoDB.Bson.Tests.xml 33 | 34 | 35 | 36 | True 37 | 38 | 39 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Bson.dll 40 | True 41 | 42 | 43 | 44 | ..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll 45 | True 46 | 47 | 48 | 49 | 50 | 51 | ..\..\packages\Unquote.2.2.2\lib\net40\Unquote.dll 52 | True 53 | 54 | 55 | 56 | 57 | FSharpValueSerializationTests.fs 58 | 59 | 60 | FSharpListSerializationTests.fs 61 | 62 | 63 | FSharpMapSerializationTests.fs 64 | 65 | 66 | FSharpSetSerializationTests.fs 67 | 68 | 69 | 70 | 71 | 72 | 73 | FSharp.MongoDB.Bson 74 | {3c580b01-b163-4e2d-bd4a-afa062ef813e} 75 | True 76 | 77 | 78 | 79 | 11 80 | 81 | 82 | 83 | 90 | 91 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Bson.Tests/FSharpListSerializationTests.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Tests 17 | 18 | open MongoDB.Bson 19 | open MongoDB.Bson.IO 20 | open MongoDB.Bson.Serialization 21 | 22 | open NUnit.Framework 23 | open Swensen.Unquote 24 | 25 | open FSharp.MongoDB.Bson.Serialization 26 | 27 | module FSharpListSerialization = 28 | 29 | do Conventions.register() 30 | do Serializers.register() 31 | 32 | let serialize value = 33 | let doc = BsonDocument() 34 | let writer = new BsonDocumentWriter(doc, BsonDocumentWriterSettings.Defaults) 35 | BsonSerializer.Serialize(writer, value.GetType(), value, null) 36 | doc 37 | 38 | let deserialize doc (typ : System.Type) = 39 | let reader = new BsonDocumentReader(doc, BsonDocumentReaderSettings.Defaults) 40 | unbox (BsonSerializer.Deserialize(reader, typ, null)) 41 | 42 | [] 43 | module RecordType = 44 | 45 | type Primitive = { 46 | bool : bool list 47 | int : int list 48 | string : string list 49 | float : float list 50 | } 51 | 52 | [] 53 | let ``test serialize primitive lists in record type empty``() = 54 | let value = { bool = [] 55 | int = [] 56 | string = [] 57 | float = [] } 58 | 59 | let result = <@ serialize value @> 60 | let expected = <@ BsonDocument([ BsonElement("bool", BsonArray([] : bool list)) 61 | BsonElement("int", BsonArray([] : int list)) 62 | BsonElement("string", BsonArray([] : string list)) 63 | BsonElement("float", BsonArray([] : float list)) ]) @> 64 | 65 | test <@ %result = %expected @> 66 | 67 | [] 68 | let ``test deserialize primitive lists in record type empty``() = 69 | let doc = BsonDocument([ BsonElement("bool", BsonArray([] : bool list)) 70 | BsonElement("int", BsonArray([] : int list)) 71 | BsonElement("string", BsonArray([] : string list)) 72 | BsonElement("float", BsonArray([] : float list)) ]) 73 | 74 | let result = <@ deserialize doc typeof @> 75 | let expected = <@ { bool = [] 76 | int = [] 77 | string = [] 78 | float = [] } @> 79 | 80 | test <@ %result = %expected @> 81 | 82 | [] 83 | let ``test serialize primitive lists in record type singleton``() = 84 | let value = { bool = [ false ] 85 | int = [ 0 ] 86 | string = [ "0.0" ] 87 | float = [ 0.0 ] } 88 | 89 | let result = <@ serialize value @> 90 | let expected = <@ BsonDocument([ BsonElement("bool", BsonArray([ false ])) 91 | BsonElement("int", BsonArray([ 0 ])) 92 | BsonElement("string", BsonArray([ "0.0" ])) 93 | BsonElement("float", BsonArray([ 0.0 ])) ]) @> 94 | 95 | test <@ %result = %expected @> 96 | 97 | [] 98 | let ``test deserialize primitive lists in record type singleton``() = 99 | let doc = BsonDocument([ BsonElement("bool", BsonArray([ false ])) 100 | BsonElement("int", BsonArray([ 0 ])) 101 | BsonElement("string", BsonArray([ "0.0" ])) 102 | BsonElement("float", BsonArray([ 0.0 ])) ]) 103 | 104 | let result = <@ deserialize doc typeof @> 105 | let expected = <@ { bool = [ false ] 106 | int = [ 0 ] 107 | string = [ "0.0" ] 108 | float = [ 0.0 ] } @> 109 | 110 | test <@ %result = %expected @> 111 | 112 | [] 113 | let ``test serialize primitive lists in record type``() = 114 | let value = { bool = [ true; false; false ] 115 | int = [ 1; 0; 0 ] 116 | string = [ "1.0"; "0.0"; "0.0" ] 117 | float = [ 1.0; 0.0; 0.0 ] } 118 | 119 | let result = <@ serialize value @> 120 | let expected = <@ BsonDocument([ BsonElement("bool", BsonArray([ true; false; false ])) 121 | BsonElement("int", BsonArray([ 1; 0; 0 ])) 122 | BsonElement("string", BsonArray([ "1.0"; "0.0"; "0.0" ])) 123 | BsonElement("float", BsonArray([ 1.0; 0.0; 0.0 ])) ]) @> 124 | 125 | test <@ %result = %expected @> 126 | 127 | [] 128 | let ``test deserialize primitive lists in record type``() = 129 | let doc = BsonDocument([ BsonElement("bool", BsonArray([ true; false; false ])) 130 | BsonElement("int", BsonArray([ 1; 0; 0 ])) 131 | BsonElement("string", BsonArray([ "1.0"; "0.0"; "0.0" ])) 132 | BsonElement("float", BsonArray([ 1.0; 0.0; 0.0 ])) ]) 133 | 134 | let result = <@ deserialize doc typeof @> 135 | let expected = <@ { bool = [ true; false; false ] 136 | int = [ 1; 0; 0 ] 137 | string = [ "1.0"; "0.0"; "0.0" ] 138 | float = [ 1.0; 0.0; 0.0 ] } @> 139 | 140 | test <@ %result = %expected @> 141 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Bson.Tests/FSharpMapSerializationTests.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Tests 17 | 18 | open MongoDB.Bson 19 | open MongoDB.Bson.IO 20 | open MongoDB.Bson.Serialization 21 | 22 | open NUnit.Framework 23 | open Swensen.Unquote 24 | 25 | open FSharp.MongoDB.Bson.Serialization 26 | 27 | module FSharpMapSerialization = 28 | 29 | do Conventions.register() 30 | do Serializers.register() 31 | 32 | let serialize value = 33 | let doc = BsonDocument() 34 | let writer = new BsonDocumentWriter(doc, BsonDocumentWriterSettings.Defaults) 35 | BsonSerializer.Serialize(writer, value.GetType(), value, null) 36 | doc 37 | 38 | let deserialize doc (typ : System.Type) = 39 | let reader = new BsonDocumentReader(doc, BsonDocumentReaderSettings.Defaults) 40 | unbox (BsonSerializer.Deserialize(reader, typ, null)) 41 | 42 | [] 43 | module RecordType = 44 | 45 | type Primitive = { 46 | bool : Map 47 | int : Map 48 | string : Map 49 | float : Map 50 | } 51 | 52 | [] 53 | let ``test serialize primitive maps in record type empty``() = 54 | let value = { bool = [] |> Map.ofList 55 | int = [] |> Map.ofList 56 | string = [] |> Map.ofList 57 | float = [] |> Map.ofList } 58 | 59 | let result = <@ serialize value @> 60 | let expected = <@ BsonDocument([ BsonElement("bool", BsonDocument()) 61 | BsonElement("int", BsonDocument()) 62 | BsonElement("string", BsonDocument()) 63 | BsonElement("float", BsonDocument()) ]) @> 64 | 65 | test <@ %result = %expected @> 66 | 67 | [] 68 | let ``test deserialize primitive maps in record type empty``() = 69 | let doc = BsonDocument([ BsonElement("bool", BsonDocument()) 70 | BsonElement("int", BsonDocument()) 71 | BsonElement("string", BsonDocument()) 72 | BsonElement("float", BsonDocument()) ]) 73 | 74 | let result = <@ deserialize doc typeof @> 75 | let expected = <@ { bool = [] |> Map.ofList 76 | int = [] |> Map.ofList 77 | string = [] |> Map.ofList 78 | float = [] |> Map.ofList } @> 79 | 80 | test <@ %result = %expected @> 81 | 82 | [] 83 | let ``test serialize primitive maps in record type singleton``() = 84 | let value = { bool = [ ("a", false) ] |> Map.ofList 85 | int = [ ("a", 0) ] |> Map.ofList 86 | string = [ ("a", "0.0") ] |> Map.ofList 87 | float = [ ("a", 0.0) ] |> Map.ofList } 88 | 89 | let result = <@ serialize value @> 90 | let expected = <@ BsonDocument([ BsonElement("bool", BsonDocument("a", BsonBoolean(false))) 91 | BsonElement("int", BsonDocument("a", BsonInt32(0))) 92 | BsonElement("string", BsonDocument("a", BsonString("0.0"))) 93 | BsonElement("float", BsonDocument("a", BsonDouble(0.0))) ]) @> 94 | 95 | test <@ %result = %expected @> 96 | 97 | [] 98 | let ``test deserialize primitive maps in record type singleton``() = 99 | let doc = BsonDocument([ BsonElement("bool", BsonDocument("a", BsonBoolean(false))) 100 | BsonElement("int", BsonDocument("a", BsonInt32(0))) 101 | BsonElement("string", BsonDocument("a", BsonString("0.0"))) 102 | BsonElement("float", BsonDocument("a", BsonDouble(0.0))) ]) 103 | 104 | let result = <@ deserialize doc typeof @> 105 | let expected = <@ { bool = [ ("a", false) ] |> Map.ofList 106 | int = [ ("a", 0) ] |> Map.ofList 107 | string = [ ("a", "0.0") ] |> Map.ofList 108 | float = [ ("a", 0.0) ] |> Map.ofList } @> 109 | 110 | test <@ %result = %expected @> 111 | 112 | [] 113 | let ``test serialize primitive maps in record type``() = 114 | let value = { bool = [ ("b", true); ("a", false); ("c", false) ] |> Map.ofList 115 | int = [ ("b", 1); ("a", 0); ("c", 2) ] |> Map.ofList 116 | string = [ ("b", "1.0"); ("a", "0.0"); ("c", "2.0") ] |> Map.ofList 117 | float = [ ("b", 1.0); ("a", 0.0); ("c", 2.0) ] |> Map.ofList } 118 | 119 | let result = <@ serialize value @> 120 | let expected = <@ BsonDocument([ BsonElement("bool", BsonDocument([ BsonElement("a", BsonBoolean(false)) 121 | BsonElement("b", BsonBoolean(true)) 122 | BsonElement("c", BsonBoolean(false)) ])) 123 | BsonElement("int", BsonDocument([ BsonElement("a", BsonInt32(0)) 124 | BsonElement("b", BsonInt32(1)) 125 | BsonElement("c", BsonInt32(2)) ])) 126 | BsonElement("string", BsonDocument([ BsonElement("a", BsonString("0.0")) 127 | BsonElement("b", BsonString("1.0")) 128 | BsonElement("c", BsonString("2.0")) ])) 129 | BsonElement("float", BsonDocument([ BsonElement("a", BsonDouble(0.0)) 130 | BsonElement("b", BsonDouble(1.0)) 131 | BsonElement("c", BsonDouble(2.0)) ])) ]) @> 132 | 133 | test <@ %result = %expected @> 134 | 135 | [] 136 | let ``test deserialize primitive maps in record type``() = 137 | let doc = BsonDocument([ BsonElement("bool", BsonDocument([ BsonElement("a", BsonBoolean(false)) 138 | BsonElement("b", BsonBoolean(true)) 139 | BsonElement("c", BsonBoolean(false)) ])) 140 | BsonElement("int", BsonDocument([ BsonElement("a", BsonInt32(0)) 141 | BsonElement("b", BsonInt32(1)) 142 | BsonElement("c", BsonInt32(2)) ])) 143 | BsonElement("string", BsonDocument([ BsonElement("a", BsonString("0.0")) 144 | BsonElement("b", BsonString("1.0")) 145 | BsonElement("c", BsonString("2.0")) ])) 146 | BsonElement("float", BsonDocument([ BsonElement("a", BsonDouble(0.0)) 147 | BsonElement("b", BsonDouble(1.0)) 148 | BsonElement("c", BsonDouble(2.0)) ])) ]) 149 | 150 | let result = <@ deserialize doc typeof @> 151 | let expected = <@ { bool = [ ("b", true); ("a", false); ("c", false) ] |> Map.ofList 152 | int = [ ("b", 1); ("a", 0); ("c", 2) ] |> Map.ofList 153 | string = [ ("b", "1.0"); ("a", "0.0"); ("c", "2.0") ] |> Map.ofList 154 | float = [ ("b", 1.0); ("a", 0.0); ("c", 2.0) ] |> Map.ofList } @> 155 | 156 | test <@ %result = %expected @> 157 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Bson.Tests/FSharpSetSerializationTests.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Bson.Tests 17 | 18 | open MongoDB.Bson 19 | open MongoDB.Bson.IO 20 | open MongoDB.Bson.Serialization 21 | 22 | open NUnit.Framework 23 | open Swensen.Unquote 24 | 25 | open FSharp.MongoDB.Bson.Serialization 26 | 27 | module FSharpSetSerialization = 28 | 29 | do Conventions.register() 30 | do Serializers.register() 31 | 32 | let serialize value = 33 | let doc = BsonDocument() 34 | let writer = new BsonDocumentWriter(doc, BsonDocumentWriterSettings.Defaults) 35 | BsonSerializer.Serialize(writer, value.GetType(), value, null) 36 | doc 37 | 38 | let deserialize doc (typ : System.Type) = 39 | let reader = new BsonDocumentReader(doc, BsonDocumentReaderSettings.Defaults) 40 | unbox (BsonSerializer.Deserialize(reader, typ, null)) 41 | 42 | [] 43 | module RecordType = 44 | 45 | type Primitive = { 46 | bool : Set 47 | int : Set 48 | string : Set 49 | float : Set 50 | } 51 | 52 | [] 53 | let ``test serialize primitive sets in record type empty``() = 54 | let value = { bool = [] |> Set.ofList 55 | int = [] |> Set.ofList 56 | string = [] |> Set.ofList 57 | float = [] |> Set.ofList } 58 | 59 | let result = <@ serialize value @> 60 | let expected = <@ BsonDocument([ BsonElement("bool", BsonArray([] : bool list)) 61 | BsonElement("int", BsonArray([] : int list)) 62 | BsonElement("string", BsonArray([] : string list)) 63 | BsonElement("float", BsonArray([] : float list)) ]) @> 64 | 65 | test <@ %result = %expected @> 66 | 67 | [] 68 | let ``test deserialize primitive sets in record type empty``() = 69 | let doc = BsonDocument([ BsonElement("bool", BsonArray([] : bool list)) 70 | BsonElement("int", BsonArray([] : int list)) 71 | BsonElement("string", BsonArray([] : string list)) 72 | BsonElement("float", BsonArray([] : float list)) ]) 73 | 74 | let result = <@ deserialize doc typeof @> 75 | let expected = <@ { bool = [] |> Set.ofList 76 | int = [] |> Set.ofList 77 | string = [] |> Set.ofList 78 | float = [] |> Set.ofList } @> 79 | 80 | test <@ %result = %expected @> 81 | 82 | [] 83 | let ``test serialize primitive sets in record type singleton``() = 84 | let value = { bool = [ false ] |> Set.ofList 85 | int = [ 0 ] |> Set.ofList 86 | string = [ "0.0" ] |> Set.ofList 87 | float = [ 0.0 ] |> Set.ofList } 88 | 89 | let result = <@ serialize value @> 90 | let expected = <@ BsonDocument([ BsonElement("bool", BsonArray([ false ])) 91 | BsonElement("int", BsonArray([ 0 ])) 92 | BsonElement("string", BsonArray([ "0.0" ])) 93 | BsonElement("float", BsonArray([ 0.0 ])) ]) @> 94 | 95 | test <@ %result = %expected @> 96 | 97 | [] 98 | let ``test deserialize primitive sets in record type singleton``() = 99 | let doc = BsonDocument([ BsonElement("bool", BsonArray([ false ])) 100 | BsonElement("int", BsonArray([ 0 ])) 101 | BsonElement("string", BsonArray([ "0.0" ])) 102 | BsonElement("float", BsonArray([ 0.0 ])) ]) 103 | 104 | let result = <@ deserialize doc typeof @> 105 | let expected = <@ { bool = [ false ] |> Set.ofList 106 | int = [ 0 ] |> Set.ofList 107 | string = [ "0.0" ] |> Set.ofList 108 | float = [ 0.0 ] |> Set.ofList } @> 109 | 110 | test <@ %result = %expected @> 111 | 112 | [] 113 | let ``test serialize primitive sets in record type``() = 114 | let value = { bool = [ true; false; false ] |> Set.ofList 115 | int = [ 1; 0; 2 ] |> Set.ofList 116 | string = [ "1.0"; "0.0"; "2.0" ] |> Set.ofList 117 | float = [ 1.0; 0.0; 2.0 ] |> Set.ofList } 118 | 119 | let result = <@ serialize value @> 120 | let expected = <@ BsonDocument([ BsonElement("bool", BsonArray([ false; true ])) 121 | BsonElement("int", BsonArray([ 0; 1; 2 ])) 122 | BsonElement("string", BsonArray([ "0.0"; "1.0"; "2.0" ])) 123 | BsonElement("float", BsonArray([ 0.0; 1.0; 2.0 ])) ]) @> 124 | 125 | test <@ %result = %expected @> 126 | 127 | [] 128 | let ``test deserialize primitive sets in record type``() = 129 | let doc = BsonDocument([ BsonElement("bool", BsonArray([ true; false; false ])) 130 | BsonElement("int", BsonArray([ 1; 0; 2 ])) 131 | BsonElement("string", BsonArray([ "1.0"; "0.0"; "2.0" ])) 132 | BsonElement("float", BsonArray([ 1.0; 0.0; 2.0 ])) ]) 133 | 134 | let result = <@ deserialize doc typeof @> 135 | let expected = <@ { bool = [ false; false; true ] |> Set.ofList 136 | int = [ 0; 1; 2 ] |> Set.ofList 137 | string = [ "0.0"; "1.0"; "2.0" ] |> Set.ofList 138 | float = [ 0.0; 1.0; 2.0 ] |> Set.ofList } @> 139 | 140 | test <@ %result = %expected @> 141 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Bson.Tests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Bson.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.FunctionalTests/FSharp.MongoDB.Driver.FunctionalTests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | d0113e25-1d28-448b-b236-501bf8a69c77 9 | Library 10 | FSharp.MongoDB.Driver.FunctionalTests 11 | FSharp.MongoDB.Driver.FunctionalTests 12 | v4.5 13 | FSharp.MongoDB.Driver.FunctionalTests 14 | 15 | 16 | true 17 | full 18 | false 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | 3 23 | bin\Debug\FSharp.MongoDB.Driver.FunctionalTests.xml 24 | 25 | 26 | pdbonly 27 | true 28 | true 29 | bin\Release\ 30 | TRACE 31 | 3 32 | bin\Release\FSharp.MongoDB.Driver.FunctionalTests.xml 33 | 34 | 35 | 36 | True 37 | 38 | 39 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Bson.dll 40 | True 41 | 42 | 43 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Driver.Core.dll 44 | True 45 | 46 | 47 | 48 | ..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll 49 | True 50 | 51 | 52 | 53 | 54 | 55 | ..\..\packages\Unquote.2.2.2\lib\net40\Unquote.dll 56 | True 57 | 58 | 59 | 60 | 61 | FluentMongoTests.fs 62 | 63 | 64 | 65 | 66 | 67 | 68 | FSharp.MongoDB.Driver 69 | {f2f775c4-7c17-4e02-a122-123514fc15c6} 70 | True 71 | 72 | 73 | 74 | 11 75 | 76 | 77 | 84 | 85 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.FunctionalTests/FluentMongoTests.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver.Tests 17 | 18 | open MongoDB.Bson 19 | 20 | open NUnit.Framework 21 | open Swensen.Unquote 22 | 23 | open FSharp.MongoDB.Driver 24 | 25 | module FluentMongo = 26 | 27 | [] 28 | module private Configuration = 29 | 30 | let private client = MongoClient() :> IMongoClient 31 | 32 | let private db = "fsharpdriverfunctionaltests" 33 | 34 | let internal database = client.GetDatabase db 35 | 36 | [] 37 | module InsertOps = 38 | 39 | let private clctn = "insertops" 40 | 41 | let private collection = database.GetCollection clctn 42 | 43 | [] 44 | let ``drop collection``() = 45 | collection.Drop() |> ignore 46 | 47 | [] 48 | let ``test fluent api insert``() = 49 | let doc = BsonDocument([ BsonElement("item", BsonString("card")) 50 | BsonElement("qty", BsonInt32(15)) ]) 51 | 52 | collection.Insert doc |> ignore 53 | 54 | let res = collection.Find doc |> Seq.toList 55 | 56 | res.Length =? 1 // should only have single document in collection 57 | let found = res.Head 58 | 59 | // Check that found document contains correct values for elements of inserted document 60 | for elem in doc do 61 | test <@ elem.Value = found.[elem.Name] @> 62 | 63 | // Check that found document contains no other fields than previously examined 64 | test <@ doc.ElementCount = found.ElementCount @> 65 | 66 | [] 67 | module UpdateOps = 68 | 69 | let private clctn = "updateops" 70 | 71 | let private collection = database.GetCollection clctn 72 | 73 | [] 74 | let ``drop collection``() = 75 | collection.Drop() |> ignore 76 | 77 | [] 78 | let ``test fluent api update reusing scope``() = 79 | let doc = BsonDocument([ BsonElement("item", BsonString("card")) 80 | BsonElement("qty", BsonInt32(15)) ]) 81 | 82 | collection.Insert doc |> ignore 83 | 84 | let scope = collection.Find (BsonDocument("_id", doc.["_id"])) 85 | 86 | let checkInsert (scope : Scope) = 87 | let res = scope |> Seq.toList 88 | 89 | res.Length =? 1 // should only have single document in collection 90 | let found = res.Head 91 | 92 | // Check that found document contains correct values for elements of inserted document 93 | for elem in doc do 94 | test <@ elem.Value = found.[elem.Name] @> 95 | 96 | // Check that found document contains no other fields than previously examined 97 | test <@ doc.ElementCount = found.ElementCount @> 98 | 99 | checkInsert scope 100 | 101 | scope |> Scope.update (BsonDocument("$inc", BsonDocument("qty", BsonInt32(-1)))) |> ignore 102 | 103 | let checkUpdate (scope : Scope) = 104 | let res = scope |> Seq.toList 105 | 106 | res.Length =? 1 // should only have single document in collection 107 | let found = res.Head 108 | 109 | // Check that found document contains correct values for elements of updated document 110 | for elem in doc do 111 | if elem.Name = "qty" then 112 | test <@ elem.Value.AsInt32 - 1 = found.[elem.Name].AsInt32 @> 113 | else 114 | test <@ elem.Value = found.[elem.Name] @> 115 | 116 | checkUpdate scope 117 | 118 | [] 119 | module RemoveOps = 120 | 121 | let private clctn = "removeops" 122 | 123 | let private collection = database.GetCollection clctn 124 | 125 | [] 126 | let ``drop collection``() = 127 | collection.Drop() |> ignore 128 | 129 | [] 130 | let ``test fluent api remove single``() = 131 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 132 | BsonElement("item", BsonString("pencil")); 133 | BsonElement("qty", BsonInt32(50)); 134 | BsonElement("type", BsonString("no.2")) ]); 135 | BsonDocument([ BsonElement("item", BsonString("pen")); 136 | BsonElement("qty", BsonInt32(20)) ]); 137 | BsonDocument([ BsonElement("item", BsonString("eraser")); 138 | BsonElement("qty", BsonInt32(25)) ]) ] 139 | 140 | for x in docs do 141 | collection.Insert x |> ignore 142 | 143 | let scope = collection.Find (BsonDocument("_id", BsonInt32(11))) 144 | 145 | let checkInsert (scope : Scope) = 146 | let res = scope |> Seq.toList 147 | 148 | res.Length =? 1 // should only have single document in collection 149 | let found = res.Head 150 | 151 | let doc = docs.[0] 152 | 153 | // Check that found document contains correct values for elements of inserted document 154 | for elem in doc do 155 | test <@ elem.Value = found.[elem.Name] @> 156 | 157 | // Check that found document contains no other fields than previously examined 158 | test <@ doc.ElementCount = found.ElementCount @> 159 | 160 | checkInsert scope 161 | 162 | scope |> Scope.removeOne |> ignore 163 | 164 | let removeQuery = BsonDocument("_id", BsonInt32(11)) 165 | 166 | let checkRemove (scope : Scope) = 167 | let res = scope |> Seq.toList 168 | 169 | res.Length =? 0 // should not find any documents 170 | 171 | checkRemove scope 172 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.FunctionalTests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.FunctionalTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.Tests/FSharp.MongoDB.Driver.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 45dddbc9-5807-43ed-9cdf-a67067c50969 9 | Library 10 | FSharp.MongoDB.Driver.Tests 11 | FSharp.MongoDB.Driver.Tests 12 | v4.5 13 | FSharp.MongoDB.Driver.Tests 14 | ..\..\ 15 | true 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\FSharp.MongoDB.Driver.Tests.xml 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\FSharp.MongoDB.Driver.Tests.xml 35 | 36 | 37 | 38 | True 39 | 40 | 41 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Bson.dll 42 | True 43 | 44 | 45 | ..\..\lib\mongo-csharp-driver\MongoDB.Driver.Core\bin\Debug\MongoDB.Driver.Core.dll 46 | 47 | 48 | 49 | ..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll 50 | True 51 | 52 | 53 | 54 | 55 | 56 | ..\..\packages\Unquote.2.2.2\lib\net40\Unquote.dll 57 | True 58 | 59 | 60 | 61 | 62 | ExpressibleMongoTests.fs 63 | 64 | 65 | MongoBackboneTests.fs 66 | 67 | 68 | QuotableMongoTests.fs 69 | 70 | 71 | TypedQuotableMongoTests.fs 72 | 73 | 74 | 75 | 76 | 77 | 78 | FSharp.MongoDB.Driver 79 | {f2f775c4-7c17-4e02-a122-123514fc15c6} 80 | True 81 | 82 | 83 | 84 | 11 85 | 86 | 87 | 88 | 95 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.Tests/MongoBackboneTests.fs: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 MongoDB, Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | *) 15 | 16 | namespace FSharp.MongoDB.Driver.Tests 17 | 18 | open System.Net 19 | 20 | open MongoDB.Bson 21 | 22 | open MongoDB.Driver.Core 23 | open MongoDB.Driver.Core.Protocol 24 | open MongoDB.Driver.Core.Protocol.Messages 25 | 26 | open NUnit.Framework 27 | open Swensen.Unquote 28 | 29 | open FSharp.MongoDB.Driver 30 | 31 | module MongoBackbone = 32 | 33 | [] 34 | module Configuration = 35 | 36 | let internal backbone = MongoBackbone(Backbone.DefaultSettings.all) 37 | 38 | let db = "fsharpdrivertests" 39 | 40 | [] 41 | module Exceptions = 42 | 43 | let clctn = "failureops" 44 | 45 | [] 46 | let ``drop collection``() = 47 | backbone.DropCollection db clctn|> ignore 48 | 49 | [] 50 | let ``succeed when iterate through cursor twice``() = 51 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 52 | BsonElement("item", BsonString("pencil")); 53 | BsonElement("qty", BsonInt32(50)); 54 | BsonElement("type", BsonString("no.2")) ]); 55 | BsonDocument([ BsonElement("item", BsonString("pen")); 56 | BsonElement("qty", BsonInt32(20)) ]); 57 | BsonDocument([ BsonElement("item", BsonString("eraser")); 58 | BsonElement("qty", BsonInt32(25)) ]) ] 59 | 60 | let insertFlags = InsertFlags.None 61 | let insertSettings = Operation.DefaultSettings.insert 62 | 63 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 64 | 65 | let query = BsonDocument() 66 | let project = BsonDocument() 67 | 68 | let queryFlags = QueryFlags.None 69 | let querySettings = Operation.DefaultSettings.query 70 | 71 | let res = backbone.Find db clctn query project 0 0 queryFlags querySettings 72 | 73 | for _ in [1 .. 2] do for _ in res do () 74 | 75 | [] 76 | [)>] 77 | let ``fail when insert duplicate keys``() = 78 | let id = 11 // make clear that all the documents use the same _id 79 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(id)); 80 | BsonElement("item", BsonString("pencil")); 81 | BsonElement("qty", BsonInt32(50)); 82 | BsonElement("type", BsonString("no.2")) ]); 83 | BsonDocument([ BsonElement("_id", BsonInt32(id)); 84 | BsonElement("item", BsonString("pen")); 85 | BsonElement("qty", BsonInt32(20)) ]); 86 | BsonDocument([ BsonElement("_id", BsonInt32(id)); 87 | BsonElement("item", BsonString("eraser")); 88 | BsonElement("qty", BsonInt32(25)) ]) ] 89 | 90 | let insertFlags = InsertFlags.None 91 | let insertSettings = { Operation.DefaultSettings.insert with WriteConcern = Some WriteConcern.Acknowledged } 92 | 93 | try 94 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 95 | with 96 | | :? System.AggregateException as exn -> 97 | for inner in exn.InnerExceptions do 98 | if inner.GetType() = typeof then raise inner 99 | reraise() // unexpected (inner) exception 100 | 101 | [] 102 | [)>] 103 | let ``fail when invalid update command``() = 104 | let doc = BsonDocument([ BsonElement("item", BsonString("card")); 105 | BsonElement("qty", BsonInt32(15)) ]) 106 | 107 | let insertFlags = InsertFlags.None 108 | let insertSettings = Operation.DefaultSettings.insert 109 | 110 | backbone.Insert db clctn doc insertFlags insertSettings |> ignore 111 | 112 | let updateQuery = BsonDocument() 113 | let update = BsonDocument("qty", BsonDocument("$inc", BsonInt32(-1))) // careful here 114 | 115 | let updateFlags = UpdateFlags.None 116 | let updateSettings = { Operation.DefaultSettings.update with CheckUpdateDocument = Some true } 117 | 118 | try 119 | backbone.Update db clctn updateQuery update updateFlags updateSettings |> ignore 120 | with 121 | | :? System.AggregateException as exn -> 122 | for inner in exn.InnerExceptions do 123 | if inner.GetType() = typeof then raise inner 124 | reraise() // unexpected (inner) exception 125 | 126 | [] 127 | module InsertOps = 128 | 129 | let clctn = "insertops" 130 | 131 | [] 132 | let ``drop collection``() = 133 | backbone.DropCollection db clctn |> ignore 134 | 135 | [] 136 | let ``test insert without _id``() = 137 | let doc = BsonDocument([ BsonElement("item", BsonString("card")); 138 | BsonElement("qty", BsonInt32(15)) ]) 139 | 140 | let insertFlags = InsertFlags.None 141 | let insertSettings = { Operation.DefaultSettings.insert with AssignIdOnInsert = Some false } 142 | 143 | backbone.Insert db clctn doc insertFlags insertSettings |> ignore 144 | 145 | let query = BsonDocument() 146 | let project = BsonDocument() 147 | 148 | let queryFlags = QueryFlags.None 149 | let querySettings = Operation.DefaultSettings.query 150 | 151 | let res = backbone.Find db clctn query project 0 0 queryFlags querySettings |> Seq.toList 152 | 153 | res.Length =? 1 // should only have single document in collection 154 | let found = res.Head 155 | 156 | // Check that an _id field was added to the document 157 | test <@ found.Contains "_id" = true @> 158 | 159 | // Check that found document contains correct values for elements of inserted document 160 | for elem in doc do 161 | test <@ elem.Value = found.[elem.Name] @> 162 | 163 | // Check that found document contains no other fields than previously examined 164 | test <@ doc.ElementCount + 1 = found.ElementCount @> 165 | 166 | [] 167 | let ``test insert with _id``() = 168 | let doc = BsonDocument([ BsonElement("_id", BsonInt32(10)); 169 | BsonElement("item", BsonString("box")); 170 | BsonElement("qty", BsonInt32(15)) ]) 171 | 172 | let insertFlags = InsertFlags.None 173 | let insertSettings = Operation.DefaultSettings.insert 174 | 175 | backbone.Insert db clctn doc insertFlags insertSettings |> ignore 176 | 177 | let query = BsonDocument() 178 | let project = BsonDocument() 179 | 180 | let queryFlags = QueryFlags.None 181 | let querySettings = Operation.DefaultSettings.query 182 | 183 | let res = backbone.Find db clctn query project 0 0 queryFlags querySettings |> Seq.toList 184 | 185 | res.Length =? 1 // should only have single document in collection 186 | let found = res.Head 187 | 188 | // Check that found document contains correct values for elements of inserted document 189 | for elem in doc do 190 | test <@ elem.Value = found.[elem.Name] @> 191 | 192 | // Check that found document contains no other fields than previously examined 193 | test <@ doc.ElementCount = found.ElementCount @> 194 | 195 | [] 196 | let ``test bulk insert``() = 197 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 198 | BsonElement("item", BsonString("pencil")); 199 | BsonElement("qty", BsonInt32(50)); 200 | BsonElement("type", BsonString("no.2")) ]); 201 | BsonDocument([ BsonElement("item", BsonString("pen")); 202 | BsonElement("qty", BsonInt32(20)) ]); 203 | BsonDocument([ BsonElement("item", BsonString("eraser")); 204 | BsonElement("qty", BsonInt32(25)) ]) ] 205 | 206 | let insertFlags = InsertFlags.None 207 | let insertSettings = Operation.DefaultSettings.insert 208 | 209 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 210 | 211 | let query = BsonDocument() 212 | let project = BsonDocument() 213 | 214 | let queryFlags = QueryFlags.None 215 | let querySettings = Operation.DefaultSettings.query 216 | 217 | let res = backbone.Find db clctn query project 0 0 queryFlags querySettings |> Seq.toList 218 | 219 | res.Length =? docs.Length // should only have these document in collection 220 | for (doc, found) in List.zip docs res do 221 | // Check that found document contains correct values for elements of inserted document 222 | for elem in doc do 223 | test <@ elem.Value = found.[elem.Name] @> 224 | 225 | // Check that found document contains no other fields than previously examined 226 | test <@ doc.ElementCount = found.ElementCount @> 227 | 228 | [] 229 | module UpdateOps = 230 | 231 | let clctn = "updateops" 232 | 233 | [] 234 | let ``drop collection``() = 235 | backbone.DropCollection db clctn |> ignore 236 | 237 | [] 238 | let ``test update single``() = 239 | let doc = BsonDocument([ BsonElement("item", BsonString("card")); 240 | BsonElement("qty", BsonInt32(15)) ]) 241 | 242 | let insertFlags = InsertFlags.None 243 | let insertSettings = { Operation.DefaultSettings.insert with AssignIdOnInsert = Some false } 244 | 245 | backbone.Insert db clctn doc insertFlags insertSettings |> ignore 246 | 247 | let updateQuery = BsonDocument() 248 | let update = BsonDocument("$inc", BsonDocument("qty", BsonInt32(-1))) 249 | 250 | let updateFlags = UpdateFlags.None 251 | let updateSettings = Operation.DefaultSettings.update 252 | 253 | backbone.Update db clctn updateQuery update updateFlags updateSettings |> ignore 254 | 255 | let findQuery = BsonDocument() 256 | let project = BsonDocument() 257 | 258 | let queryFlags = QueryFlags.None 259 | let querySettings = Operation.DefaultSettings.query 260 | 261 | let res = backbone.Find db clctn findQuery project 0 0 queryFlags querySettings |> Seq.toList 262 | 263 | res.Length =? 1 // should only have single document in collection 264 | let found = res.Head 265 | 266 | // Check that found document contains correct values for elements of updated document 267 | for elem in doc do 268 | if elem.Name = "qty" then 269 | test <@ elem.Value.AsInt32 - 1 = found.[elem.Name].AsInt32 @> 270 | else 271 | test <@ elem.Value = found.[elem.Name] @> 272 | 273 | [] 274 | let ``test update multi``() = 275 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 276 | BsonElement("item", BsonString("pencil")); 277 | BsonElement("qty", BsonInt32(50)); 278 | BsonElement("type", BsonString("no.2")) ]); 279 | BsonDocument([ BsonElement("item", BsonString("pen")); 280 | BsonElement("qty", BsonInt32(20)) ]); 281 | BsonDocument([ BsonElement("item", BsonString("eraser")); 282 | BsonElement("qty", BsonInt32(25)) ]) ] 283 | 284 | let insertFlags = InsertFlags.None 285 | let insertSettings = Operation.DefaultSettings.insert 286 | 287 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 288 | 289 | let updateQuery = BsonDocument() 290 | let update = BsonDocument("$inc", BsonDocument("qty", BsonInt32(-1))) 291 | 292 | let updateFlags = UpdateFlags.Multi 293 | let updateSettings = Operation.DefaultSettings.update 294 | 295 | backbone.Update db clctn updateQuery update updateFlags updateSettings |> ignore 296 | 297 | let findQuery = BsonDocument() 298 | let project = BsonDocument() 299 | 300 | let queryFlags = QueryFlags.None 301 | let querySettings = Operation.DefaultSettings.query 302 | 303 | let res = backbone.Find db clctn findQuery project 0 0 queryFlags querySettings |> Seq.toList 304 | 305 | res.Length =? docs.Length // should only have these document in collection 306 | for (doc, found) in List.zip docs res do 307 | // Check that found document contains correct values for elements of inserted document 308 | for elem in doc do 309 | if elem.Name = "qty" then 310 | test <@ elem.Value.AsInt32 - 1 = found.[elem.Name].AsInt32 @> 311 | else 312 | test <@ elem.Value = found.[elem.Name] @> 313 | 314 | [] 315 | let ``test upsert``() = 316 | let updateQuery = BsonDocument([ BsonElement("item", BsonString("card")); 317 | BsonElement("qty", BsonInt32(15)) ]) 318 | let update = BsonDocument("$inc", BsonDocument("qty", BsonInt32(-1))) 319 | 320 | let updateFlags = UpdateFlags.Upsert 321 | let updateSettings = Operation.DefaultSettings.update 322 | 323 | backbone.Update db clctn updateQuery update updateFlags updateSettings |> ignore 324 | 325 | let findQuery = BsonDocument() 326 | let project = BsonDocument() 327 | 328 | let queryFlags = QueryFlags.None 329 | let querySettings = Operation.DefaultSettings.query 330 | 331 | let res = backbone.Find db clctn findQuery project 0 0 queryFlags querySettings |> Seq.toList 332 | 333 | res.Length =? 1 // should only have single document in collection 334 | let found = res.Head 335 | 336 | // Check that found document contains correct values for elements of updated document 337 | for elem in updateQuery do 338 | if elem.Name = "qty" then 339 | test <@ elem.Value.AsInt32 - 1 = found.[elem.Name].AsInt32 @> 340 | else 341 | test <@ elem.Value = found.[elem.Name] @> 342 | 343 | [] 344 | module RemoveOps = 345 | 346 | let clctn = "removeops" 347 | 348 | [] 349 | let ``drop collection``() = 350 | backbone.DropCollection db clctn |> ignore 351 | 352 | [] 353 | let ``test remove single``() = 354 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 355 | BsonElement("item", BsonString("pencil")); 356 | BsonElement("qty", BsonInt32(50)); 357 | BsonElement("type", BsonString("no.2")) ]); 358 | BsonDocument([ BsonElement("item", BsonString("pen")); 359 | BsonElement("qty", BsonInt32(20)) ]); 360 | BsonDocument([ BsonElement("item", BsonString("eraser")); 361 | BsonElement("qty", BsonInt32(25)) ]) ] 362 | 363 | let insertFlags = InsertFlags.None 364 | let insertSettings = { Operation.DefaultSettings.insert with AssignIdOnInsert = Some true } 365 | 366 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 367 | 368 | let removeQuery = BsonDocument("_id", BsonInt32(11)) 369 | 370 | let deleteFlags = DeleteFlags.Single 371 | let removeSettings = Operation.DefaultSettings.remove 372 | 373 | backbone.Remove db clctn removeQuery deleteFlags removeSettings |> ignore 374 | 375 | let findQuery = BsonDocument() 376 | let project = BsonDocument() 377 | 378 | let queryFlags = QueryFlags.None 379 | let querySettings = Operation.DefaultSettings.query 380 | 381 | let res = backbone.Find db clctn findQuery project 0 0 queryFlags querySettings |> Seq.toList 382 | 383 | res.Length =? docs.Length - 1 // should have removed a single document from the collection 384 | for (doc, found) in List.zip (docs |> List.tail) res do 385 | // Check that found document contains correct values for elements of non-removed document 386 | for elem in doc do 387 | test <@ elem.Value = found.[elem.Name] @> 388 | 389 | // Check that found document contains no other fields than previously examined 390 | test <@ doc.ElementCount = found.ElementCount @> 391 | 392 | [] 393 | let ``test remove multi``() = 394 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 395 | BsonElement("item", BsonString("pencil")); 396 | BsonElement("qty", BsonInt32(50)); 397 | BsonElement("type", BsonString("no.2")) ]); 398 | BsonDocument([ BsonElement("item", BsonString("pen")); 399 | BsonElement("qty", BsonInt32(20)) ]); 400 | BsonDocument([ BsonElement("item", BsonString("eraser")); 401 | BsonElement("qty", BsonInt32(25)) ]) ] 402 | 403 | let insertFlags = InsertFlags.None 404 | let insertSettings = { Operation.DefaultSettings.insert with AssignIdOnInsert = Some true } 405 | 406 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 407 | 408 | let removeQuery = BsonDocument("qty", BsonDocument("$lte", BsonInt32(25))) 409 | 410 | let deleteFlags = DeleteFlags.None 411 | let removeSettings = Operation.DefaultSettings.remove 412 | 413 | backbone.Remove db clctn removeQuery deleteFlags removeSettings |> ignore 414 | 415 | let findQuery = BsonDocument() 416 | let project = BsonDocument() 417 | 418 | let queryFlags = QueryFlags.None 419 | let querySettings = Operation.DefaultSettings.query 420 | 421 | let res = backbone.Find db clctn findQuery project 0 0 queryFlags querySettings |> Seq.toList 422 | 423 | res.Length =? 1 // should have removed multiple documents from the collection 424 | let found = res |> List.head 425 | 426 | let doc = docs |> List.head 427 | 428 | // Check that found document contains correct values for elements of non-removed document 429 | for elem in doc do 430 | test <@ elem.Value = found.[elem.Name] @> 431 | 432 | // Check that found document contains no other fields than previously examined 433 | test <@ doc.ElementCount = found.ElementCount @> 434 | 435 | [] 436 | let ``test remove all``() = 437 | let docs = [ BsonDocument([ BsonElement("_id", BsonInt32(11)); 438 | BsonElement("item", BsonString("pencil")); 439 | BsonElement("qty", BsonInt32(50)); 440 | BsonElement("type", BsonString("no.2")) ]); 441 | BsonDocument([ BsonElement("item", BsonString("pen")); 442 | BsonElement("qty", BsonInt32(20)) ]); 443 | BsonDocument([ BsonElement("item", BsonString("eraser")); 444 | BsonElement("qty", BsonInt32(25)) ]) ] 445 | 446 | let insertFlags = InsertFlags.None 447 | let insertSettings = { Operation.DefaultSettings.insert with AssignIdOnInsert = Some true } 448 | 449 | backbone.BulkInsert db clctn docs insertFlags insertSettings |> ignore 450 | 451 | let removeQuery = BsonDocument() 452 | 453 | let deleteFlags = DeleteFlags.None 454 | let removeSettings = Operation.DefaultSettings.remove 455 | 456 | backbone.Remove db clctn removeQuery deleteFlags removeSettings |> ignore 457 | 458 | let findQuery = BsonDocument() 459 | let project = BsonDocument() 460 | 461 | let queryFlags = QueryFlags.None 462 | let querySettings = Operation.DefaultSettings.query 463 | 464 | let res = backbone.Find db clctn findQuery project 0 0 queryFlags querySettings |> Seq.toList 465 | 466 | res.Length =? 0 // should have removed all documents from the collection 467 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.Tests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/FSharp.MongoDB.Driver.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------