Skip to content

Commit 0f8dd3d

Browse files
committed
adding upsert support
1 parent e340a55 commit 0f8dd3d

5 files changed

Lines changed: 107 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- New CI/CD pipeline
88
- Primitive support for pagination [#23](https://github.com/aaronpowell/FSharp.CosmosDb/issues/23)
9+
- Upsert support [#43](https://github.com/aaronpowell/FSharp.CosmosDb/issues/43)
910

1011
### Changed
1112

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ let insertUsers data =
3838
|> Cosmos.execAsync
3939
```
4040

41+
### Upsert
42+
43+
```fsharp
44+
open FSharp.CosmosDb
45+
46+
let connStr = "..."
47+
48+
let insertUsers data =
49+
connStr
50+
|> Cosmos.fromConnectionString
51+
|> Cosmos.database "UserDb"
52+
|> Cosmos.container "UserContainer"
53+
|> Cosmos.upsertMany<User> data
54+
|> Cosmos.execAsync
55+
```
56+
4157
### Update
4258

4359
```fsharp
@@ -173,3 +189,4 @@ Add the following settings (globally or in the workspace):
173189

174190
- Zaid Ajaj for the [Npgsql Analyzer](https://github.com/Zaid-Ajaj/Npgsql.FSharp.Analyzer). Without this I wouldn't have been able to work out how to do it (and there's some code lifted from there)
175191
- [Krzysztof Cieślak](https://twitter.com/k_cieslak) for the amazing Ionide plugin
192+
- [Isaac Abraham](https://twitter.com/isaac_abraham) for helping fix the parser

src/FSharp.CosmosDb/Cosmos.fs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ module Cosmos =
6868
let insert<'T> (value: 'T) op =
6969
Insert { Connection = op; Values = [ value ] }
7070

71+
// --- INSERT --- //
72+
73+
let upsertMany<'T> (values: 'T list) op =
74+
Upsert { Connection = op; Values = values }
75+
76+
let upsert<'T> (value: 'T) op =
77+
Upsert { Connection = op; Values = [ value ] }
78+
7179
// --- UPDATE --- //
7280

7381
let update<'T> id partitionKey (updater: 'T -> 'T) op =
@@ -116,6 +124,7 @@ module Cosmos =
116124
| Insert op -> OperationHandling.execInsert getClient op
117125
| Update op -> OperationHandling.execUpdate getClient op
118126
| Delete op -> OperationHandling.execDelete getClient op
127+
| Upsert op -> OperationHandling.execUpsert getClient op
119128

120129
let execBatchAsync<'T> (op: ContainerOperation<'T>) =
121130
match op with

src/FSharp.CosmosDb/OperationHandling.fs

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ let execQueryInternal (getClient: ConnectionOperation -> CosmosClient) (op: Quer
1515
let! containerName = connInfo.ContainerName
1616

1717
let db = client.GetDatabase databaseId
18+
1819
let container = db.GetContainer containerName
1920

2021
let! query = op.Query
22+
2123
let qd =
2224
op.Parameters
2325
|> List.fold (fun (qd: QueryDefinition) (key, value) -> qd.WithParameter(key, value))
@@ -52,9 +54,59 @@ let execInsert (getClient: ConnectionOperation -> CosmosClient) (op: InsertOp<'T
5254
let! containerName = connInfo.ContainerName
5355

5456
let db = client.GetDatabase databaseId
57+
58+
let container = db.GetContainer containerName
59+
60+
let partitionKey =
61+
PartitionKeyAttributeTools.findPartitionKey<'T> ()
62+
63+
let getPartitionKeyValue single =
64+
match partitionKey with
65+
| Some propertyInfo ->
66+
let value = propertyInfo.GetValue(single)
67+
Nullable(PartitionKey(value.ToString()))
68+
| None -> Nullable()
69+
70+
return match op.Values with
71+
| [ single ] ->
72+
let pk = getPartitionKeyValue single
73+
[ container.CreateItemAsync<'T>(single, pk)
74+
|> Async.AwaitTask ]
75+
| _ ->
76+
op.Values
77+
|> List.map (fun single ->
78+
let pk = getPartitionKeyValue single
79+
container.CreateItemAsync<'T>(single, pk)
80+
|> Async.AwaitTask)
81+
}
82+
83+
match result with
84+
| Some result ->
85+
result
86+
|> List.map (fun item ->
87+
async {
88+
let! value = item
89+
return value.Value
90+
})
91+
|> AsyncSeq.ofSeqAsync
92+
| None ->
93+
failwith "Unable to construct a query as some values are missing across the database, container name and query"
94+
95+
let execUpsert (getClient: ConnectionOperation -> CosmosClient) (op: UpsertOp<'T>) =
96+
let connInfo = op.Connection
97+
let client = getClient connInfo
98+
99+
let result =
100+
maybe {
101+
let! databaseId = connInfo.DatabaseId
102+
let! containerName = connInfo.ContainerName
103+
104+
let db = client.GetDatabase databaseId
105+
55106
let container = db.GetContainer containerName
56107

57-
let partitionKey = PartitionKeyAttributeTools.findPartitionKey<'T>()
108+
let partitionKey =
109+
PartitionKeyAttributeTools.findPartitionKey<'T> ()
58110

59111
let getPartitionKeyValue single =
60112
match partitionKey with
@@ -66,12 +118,14 @@ let execInsert (getClient: ConnectionOperation -> CosmosClient) (op: InsertOp<'T
66118
return match op.Values with
67119
| [ single ] ->
68120
let pk = getPartitionKeyValue single
69-
[ container.CreateItemAsync<'T>(single, pk) |> Async.AwaitTask ]
121+
[ container.UpsertItemAsync<'T>(single, pk)
122+
|> Async.AwaitTask ]
70123
| _ ->
71124
op.Values
72125
|> List.map (fun single ->
73126
let pk = getPartitionKeyValue single
74-
container.CreateItemAsync<'T>(single, pk) |> Async.AwaitTask)
127+
container.UpsertItemAsync<'T>(single, pk)
128+
|> Async.AwaitTask)
75129
}
76130

77131
match result with
@@ -80,7 +134,8 @@ let execInsert (getClient: ConnectionOperation -> CosmosClient) (op: InsertOp<'T
80134
|> List.map (fun item ->
81135
async {
82136
let! value = item
83-
return value.Value })
137+
return value.Value
138+
})
84139
|> AsyncSeq.ofSeqAsync
85140
| None ->
86141
failwith "Unable to construct a query as some values are missing across the database, container name and query"
@@ -95,19 +150,23 @@ let execUpdate (getClient: ConnectionOperation -> CosmosClient) (op: UpdateOp<'T
95150
let! containerName = connInfo.ContainerName
96151

97152
let db = client.GetDatabase databaseId
153+
98154
let container = db.GetContainer containerName
99155

100-
return (container, container.ReadItemAsync(op.Id, PartitionKey op.PartitionKey) |> Async.AwaitTask)
156+
return (container,
157+
container.ReadItemAsync(op.Id, PartitionKey op.PartitionKey)
158+
|> Async.AwaitTask)
101159
}
102160

103161
match result with
104-
| Some(container, result) ->
162+
| Some (container, result) ->
105163
[ async {
106164
let! currentItemResponse = result
107165

108166
let newItem = op.Updater currentItemResponse.Value
109167

110-
let partitionKey = PartitionKeyAttributeTools.findPartitionKey<'T>()
168+
let partitionKey =
169+
PartitionKeyAttributeTools.findPartitionKey<'T> ()
111170

112171
let pk =
113172
match partitionKey with
@@ -116,7 +175,9 @@ let execUpdate (getClient: ConnectionOperation -> CosmosClient) (op: UpdateOp<'T
116175
Nullable(PartitionKey(value.ToString()))
117176
| None -> Nullable()
118177

119-
let! newItemResponse = container.ReplaceItemAsync(newItem, op.Id, pk) |> Async.AwaitTask
178+
let! newItemResponse =
179+
container.ReplaceItemAsync(newItem, op.Id, pk)
180+
|> Async.AwaitTask
120181

121182
return newItemResponse.Value
122183
} ]
@@ -134,15 +195,19 @@ let execDelete (getClient: ConnectionOperation -> CosmosClient) (op: DeleteOp<'T
134195
let! containerName = connInfo.ContainerName
135196

136197
let db = client.GetDatabase databaseId
198+
137199
let container = db.GetContainer containerName
138200

139-
return container.DeleteItemAsync(op.Id, PartitionKey op.PartitionKey) |> Async.AwaitTask
201+
return container.DeleteItemAsync(op.Id, PartitionKey op.PartitionKey)
202+
|> Async.AwaitTask
140203
}
141204

142205
match result with
143206
| Some result ->
144207
[ async {
145208
let! currentItemResponse = result
146-
return currentItemResponse.Value } ] |> AsyncSeq.ofSeqAsync
209+
return currentItemResponse.Value
210+
} ]
211+
|> AsyncSeq.ofSeqAsync
147212

148213
| None -> failwith "Unable to read from the container to get the item for updating"

src/FSharp.CosmosDb/Types.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ type InsertOp<'T> =
2121
{ Connection: ConnectionOperation
2222
Values: 'T list }
2323

24+
type UpsertOp<'T> =
25+
{ Connection: ConnectionOperation
26+
Values: 'T list }
27+
2428
type UpdateOp<'T> =
2529
{ Connection: ConnectionOperation
2630
Id: string
@@ -37,3 +41,4 @@ type ContainerOperation<'T> =
3741
| Insert of InsertOp<'T>
3842
| Update of UpdateOp<'T>
3943
| Delete of DeleteOp<'T>
44+
| Upsert of UpsertOp<'T>

0 commit comments

Comments
 (0)