1
+ namespace DocumentDB . Samples . Queries . Spatial
2
+ {
3
+ using System ;
4
+ using System . Collections . Generic ;
5
+ using System . Configuration ;
6
+ using System . IO ;
7
+ using System . Linq ;
8
+ using System . Threading . Tasks ;
9
+ using DocumentDB . Samples . Shared ;
10
+ using Microsoft . Azure . Documents ;
11
+ using Microsoft . Azure . Documents . Client ;
12
+ using Microsoft . Azure . Documents . Linq ;
13
+ using Microsoft . Azure . Documents . Spatial ;
14
+ using Newtonsoft . Json ;
15
+ using Newtonsoft . Json . Serialization ;
16
+
17
+ /// <summary>
18
+ /// This sample demonstrates the use of geospatial indexing and querying with Azure DocumentDB. We
19
+ /// look at how to store Points using the classes in the Microsoft.Azure.Documents.Spatial namespace,
20
+ /// how to enable a collection for geospatial indexing, and how to query for WITHIN and DISTANCE
21
+ /// using SQL and LINQ.
22
+ /// </summary>
23
+ public class Program
24
+ {
25
+ /// <summary>
26
+ /// Gets the database ID to use for the demo.
27
+ /// </summary>
28
+ private static readonly string DatabaseId = ConfigurationManager . AppSettings [ "DatabaseId" ] ;
29
+
30
+ /// <summary>
31
+ /// Gets the collection ID to use for the demo.
32
+ /// </summary>
33
+ private static readonly string CollectionId = ConfigurationManager . AppSettings [ "CollectionId" ] ;
34
+
35
+ /// <summary>
36
+ /// Gets the DocumentDB endpoint to use for the demo.
37
+ /// </summary>
38
+ private static readonly string EndpointUrl = ConfigurationManager . AppSettings [ "EndPointUrl" ] ;
39
+
40
+ /// <summary>
41
+ /// Gets the DocumentDB authorization key to use for the demo.
42
+ /// </summary>
43
+ private static readonly string AuthorizationKey = ConfigurationManager . AppSettings [ "AuthorizationKey" ] ;
44
+
45
+ /// <summary>
46
+ /// Gets an indexing policy with spatial enabled. You can also configure just certain paths for spatial indexing, e.g. Path = "/location/?"
47
+ /// </summary>
48
+ private static readonly IndexingPolicy IndexingPolicyWithSpatialEnabled = new IndexingPolicy
49
+ {
50
+ IncludedPaths = new System . Collections . ObjectModel . Collection < IncludedPath > ( )
51
+ {
52
+ new IncludedPath
53
+ {
54
+ Path = "/*" ,
55
+ Indexes = new System . Collections . ObjectModel . Collection < Index > ( )
56
+ {
57
+ new SpatialIndex ( DataType . Point ) ,
58
+ new RangeIndex ( DataType . Number ) { Precision = - 1 } ,
59
+ new RangeIndex ( DataType . String ) { Precision = - 1 }
60
+ }
61
+ }
62
+ }
63
+ } ;
64
+
65
+ /// <summary>
66
+ /// Gets the client to use.
67
+ /// </summary>
68
+ private static DocumentClient client ;
69
+
70
+ /// <summary>
71
+ /// The main method to use.
72
+ /// </summary>
73
+ /// <param name="args">The command line arguments.</param>
74
+ public static void Main ( string [ ] args )
75
+ {
76
+ try
77
+ {
78
+ // Get a Document client
79
+ using ( client = new DocumentClient ( new Uri ( EndpointUrl ) , AuthorizationKey ) )
80
+ {
81
+ RunDemoAsync ( DatabaseId , CollectionId ) . Wait ( ) ;
82
+ }
83
+ }
84
+ catch ( DocumentClientException de )
85
+ {
86
+ Exception baseException = de . GetBaseException ( ) ;
87
+ Console . WriteLine ( "{0} error occurred: {1}, Message: {2}" , de . StatusCode , de . Message , baseException . Message ) ;
88
+ }
89
+ catch ( Exception e )
90
+ {
91
+ Exception baseException = e . GetBaseException ( ) ;
92
+ Console . WriteLine ( "Error: {0}, Message: {1}" , e . Message , baseException . Message ) ;
93
+ }
94
+ finally
95
+ {
96
+ Console . WriteLine ( "End of demo, press any key to exit." ) ;
97
+ Console . ReadKey ( ) ;
98
+ }
99
+ }
100
+
101
+ /// <summary>
102
+ /// Run the geospatial demo.
103
+ /// </summary>
104
+ /// <param name="databaseId">The database Id.</param>
105
+ /// <param name="collectionId">The collection Id.</param>
106
+ /// <returns>The Task for asynchronous execution.</returns>
107
+ private static async Task RunDemoAsync ( string databaseId , string collectionId )
108
+ {
109
+ Database database = await GetDatabaseAsync ( databaseId ) ;
110
+
111
+ // Create a new collection, or modify an existing one to enable spatial indexing.
112
+ DocumentCollection collection = await GetCollection ( database . SelfLink , collectionId ) ;
113
+
114
+ await CreateDocumentAsync ( collection . SelfLink , "{ firstName: 'Ryan', lastName: 'Crawcour', address: { country: 'NZ' }}" ) ;
115
+ await CreateDocumentAsync ( collection . SelfLink , "{ firstName: 'Aravind', lastName: 'Ramachandran', address: { city: 'Kirkland', state: 'WA' }}" ) ;
116
+ await CreateDocumentAsync ( collection . SelfLink , "{ firstName: 'Andrew', lastName: 'Liu', address: { city: 'Seattle', state: 'WA' }}" ) ;
117
+
118
+ // Run a simple filter query
119
+ object filterQueryResult = await QueryScalar (
120
+ collection . SelfLink ,
121
+ @"__.filter(function(person) { return person.firstName === 'Andrew'; })" ) ;
122
+
123
+ Console . WriteLine ( "Filter query returned: {0}" , filterQueryResult ) ;
124
+
125
+ // Run a map (projection) query
126
+ object projectionQueryResult = await QueryScalar (
127
+ collection . SelfLink ,
128
+ @"__.map(function(person) { return { familyName: person.lastName, address: person.address }; })" ) ;
129
+
130
+ Console . WriteLine ( "Projection query returned: {0}" , JsonConvert . SerializeObject ( projectionQueryResult , Formatting . Indented ) ) ;
131
+
132
+ // Run a query using filter and map (using chaining)
133
+ object chainQueryResult = await QueryScalar ( collection . SelfLink ,
134
+ @"__.chain()
135
+ .filter(function(person) { return person.firstName === 'Andrew'; })
136
+ .map(function(person) { return { familyName: person.lastName, address: person.address }; })
137
+ .value()" ) ;
138
+
139
+ Console . WriteLine ( "Chain (filter & projection) query returned: {0}" , JsonConvert . SerializeObject ( chainQueryResult , Formatting . Indented ) ) ;
140
+
141
+ // Run a chained filter, map and sorting (using chaining)
142
+ object sortQueryResult = await QueryScalar ( collection . SelfLink ,
143
+ @"__.chain()
144
+ .filter(function(person) { return person.firstName === 'Andrew' || person.firstName === 'Ryan'; })
145
+ .sortBy(function(person) { return person.lastName; })
146
+ .map(function(person) { return { familyName: person.lastName, address: person.address }; })
147
+ .value()" ) ;
148
+
149
+ Console . WriteLine ( "Chain (filter, sort & projection) query returned: {0}" , JsonConvert . SerializeObject ( sortQueryResult , Formatting . Indented ) ) ;
150
+
151
+ await Cleanup ( collection ) ;
152
+ }
153
+
154
+ /// <summary>
155
+ /// Get a Database for this id. Delete if it already exists.
156
+ /// </summary>
157
+ /// <param name="id">The id of the Database to create.</param>
158
+ /// <returns>The created Database object</returns>
159
+ private static async Task < Database > GetDatabaseAsync ( string id )
160
+ {
161
+ Database database = client . CreateDatabaseQuery ( ) . Where ( c => c . Id == id ) . ToArray ( ) . FirstOrDefault ( ) ;
162
+ if ( database != null )
163
+ {
164
+ return database ;
165
+ }
166
+
167
+ Console . WriteLine ( "Creating new database..." ) ;
168
+ database = await client . CreateDatabaseAsync ( new Database { Id = id } ) ;
169
+
170
+ return database ;
171
+ }
172
+
173
+ /// <summary>
174
+ /// Get a DocumentCollection by id, or create a new one if one with the id provided doesn't exist.
175
+ /// </summary>
176
+ /// <param name="databaseLink">The database self-link to use.</param>
177
+ /// <param name="id">The id of the DocumentCollection to search for, or create.</param>
178
+ /// <returns>The matched, or created, DocumentCollection object</returns>
179
+ private static async Task < DocumentCollection > GetCollection ( string databaseLink , string id )
180
+ {
181
+ DocumentCollection collection = client . CreateDocumentCollectionQuery ( databaseLink )
182
+ . Where ( c => c . Id == id )
183
+ . AsEnumerable ( )
184
+ . FirstOrDefault ( ) ;
185
+
186
+ if ( collection != null )
187
+ {
188
+ return collection ;
189
+ }
190
+
191
+ Console . WriteLine ( "Creating new collection..." ) ;
192
+
193
+ collection = await client . CreateDocumentCollectionAsync (
194
+ databaseLink ,
195
+ new DocumentCollection
196
+ {
197
+ Id = id ,
198
+ IndexingPolicy = new IndexingPolicy ( new RangeIndex ( DataType . String ) { Precision = - 1 } )
199
+ } ) ;
200
+
201
+ return collection ;
202
+ }
203
+
204
+ /// <summary>
205
+ /// Run a query that returns a single document, and display it
206
+ /// </summary>
207
+ /// <param name="collectionLink">The collection self-link</param>
208
+ /// <param name="query">The query to run</param>
209
+ private static async Task < object > QueryScalar ( string collectionLink , string javascriptQuery )
210
+ {
211
+ string javaScriptFunctionStub = string . Format ( "function() {{ {0}; }}" , javascriptQuery ) ;
212
+ string singleQuerySprocName = "query" ;
213
+
214
+ StoredProcedure currentProcedure = client . CreateStoredProcedureQuery ( collectionLink )
215
+ . Where ( s => s . Id == singleQuerySprocName )
216
+ . AsEnumerable ( )
217
+ . FirstOrDefault ( ) ;
218
+
219
+ if ( currentProcedure != null )
220
+ {
221
+ currentProcedure . Body = javaScriptFunctionStub ;
222
+ await client . ReplaceStoredProcedureAsync ( currentProcedure ) ;
223
+ }
224
+ else
225
+ {
226
+ currentProcedure = await client . CreateStoredProcedureAsync (
227
+ collectionLink ,
228
+ new StoredProcedure
229
+ {
230
+ Id = singleQuerySprocName ,
231
+ Body = javaScriptFunctionStub
232
+ } ) ;
233
+ }
234
+
235
+ StoredProcedureResponse < object > result = await client . ExecuteStoredProcedureAsync < object > ( currentProcedure . SelfLink ) ;
236
+ return result . Response ;
237
+ }
238
+
239
+ /// <summary>
240
+ /// Create a document from JSON string
241
+ /// </summary>
242
+ /// <param name="collectionLink">The collection self-link</param>
243
+ /// <param name="query">The JSON to create</param>
244
+ private static async Task CreateDocumentAsync ( string collectionLink , string json )
245
+ {
246
+ using ( System . IO . MemoryStream ms = new MemoryStream ( System . Text . Encoding . UTF8 . GetBytes ( json ) ) )
247
+ {
248
+ Document documentFromJson = Document . LoadFrom < Document > ( ms ) ;
249
+ await client . CreateDocumentAsync ( collectionLink , documentFromJson ) ;
250
+ }
251
+ }
252
+
253
+ /// <summary>
254
+ /// Cleanup data from previous runs.
255
+ /// </summary>
256
+ /// <param name="collection">The DocumentDB collection.</param>
257
+ /// <returns>The Task for asynchronous execution.</returns>
258
+ private static async Task Cleanup ( DocumentCollection collection )
259
+ {
260
+ Console . WriteLine ( "Cleaning up" ) ;
261
+ foreach ( Document d in await client . ReadDocumentFeedAsync ( collection . SelfLink ) )
262
+ {
263
+ await client . DeleteDocumentAsync ( d . SelfLink ) ;
264
+ }
265
+ }
266
+ }
267
+ }
0 commit comments