/// <summary> /// Initialize a HashPartitionResolver. /// </summary> /// <param name="partitionKeyPropertyName">The property name to be used as the partition Key.</param> /// <param name="client">The DocumentDB client instance to use.</param> /// <param name="database">The database to run the samples on.</param> /// <param name="collectionNames">The names of collections used.</param> /// <returns>The created HashPartitionResolver.</returns> public static async Task <HashPartitionResolver> InitializeHashResolver(string partitionKeyPropertyName, DocumentClient client, Database database, string[] collectionNames) { // Set local to input. string[] CollectionNames = collectionNames; int numCollectionNames = CollectionNames.Length; // Create array of DocumentCollections. DocumentCollection[] collections = new DocumentCollection[numCollectionNames]; // Create string array of Self Links to Collections. string[] selfLinks = new string[numCollectionNames]; //Create some collections to partition data. for (int i = 0; i < numCollectionNames; i++) { collections[i] = await DocumentClientHelper.GetCollectionAsync(client, database, CollectionNames[i]); selfLinks[i] = collections[i].SelfLink; } // Join Self Link Array into a comma separated string. string selfLinkString = String.Join(", ", selfLinks); //Initialize a partition resolver that users hashing, and register with DocumentClient. //Uses User Id for PartitionKeyPropertyName, could also be TenantId, or any other variable. HashPartitionResolver hashResolver = new HashPartitionResolver(partitionKeyPropertyName, new[] { selfLinkString }); client.PartitionResolvers[database.SelfLink] = hashResolver; return(hashResolver); }
/// <summary> /// Get a DocuemntCollection by id, or create a new one if one with the id provided doesn't exist. /// </summary> /// <param name="id">The id of the DocumentCollection to search for, or create.</param> /// <returns>The matched, or created, DocumentCollection object</returns> private static async Task <DocumentCollection> GetOrCreateCollectionAsync(string databaseId, string collectionId) { DocumentCollection collection = client.CreateDocumentCollectionQuery(UriFactory.CreateDatabaseUri(databaseId)) .Where(c => c.Id == collectionId) .ToArray() .SingleOrDefault(); if (collection == null) { DocumentCollection collectionDefinition = new DocumentCollection(); collectionDefinition.Id = collectionId; collectionDefinition.IndexingPolicy = new IndexingPolicy(new RangeIndex(DataType.String) { Precision = -1 }); collectionDefinition.PartitionKey.Paths.Add("/LastName"); collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync( client, databaseId, collectionDefinition, 400); } return(collection); }
/// <summary> /// Get a DocuemntCollection by id, or create a new one if one with the id provided doesn't exist. /// </summary> /// <param name="id">The id of the DocumentCollection to search for, or create.</param> /// <returns>The matched, or created, DocumentCollection object</returns> private static async Task <DocumentCollection> GetOrCreateCollectionAsync(Database db, string id) { DocumentCollection collection = client.CreateDocumentCollectionQuery(db.SelfLink).Where(c => c.Id == id).ToArray().FirstOrDefault(); if (collection == null) { IndexingPolicy optimalQueriesIndexingPolicy = new IndexingPolicy(); optimalQueriesIndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*", Indexes = new System.Collections.ObjectModel.Collection <Index>() { new RangeIndex(DataType.Number) { Precision = -1 }, new RangeIndex(DataType.String) { Precision = -1 } } }); DocumentCollection collectionDefinition = new DocumentCollection { Id = id }; collectionDefinition.IndexingPolicy = optimalQueriesIndexingPolicy; collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, db, collectionDefinition); } return(collection); }
private static async Task ExplicitlyExcludeFromIndex(Database database) { //There may be scenarios where you want to exclude a specific doc from the index even though all other //documents are being indexed automatically. You can use an index directive to control this when you //create a document //Create a document collection with the default indexing policy (Automatically index everything) DocumentCollection collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync( client, database, new DocumentCollection { Id = ConfigurationManager.AppSettings["CollectionId"] }); //Create a document and query on it immediately, should work as this Collection is set to automatically index everyting Document created = await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc1", orderId = "order1" }); //Query for document, should find it bool found = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM root r WHERE r.orderId='order1'").AsEnumerable().Any(); //Now, create a document but this time explictly exclude it from the collection using IndexingDirective created = await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc2", orderId = "order2" }, new RequestOptions { IndexingDirective = IndexingDirective.Exclude }); //Query for document, should not find it found = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM root r WHERE r.orderId='order2'").AsEnumerable().Any(); //Read on document, should still find it Document document = await client.ReadDocumentAsync(created.SelfLink); //Cleanup await client.DeleteDocumentCollectionAsync(collection.SelfLink); }
/// <summary> /// Initialize a HashPartitionResolver that uses a custom function to extract the partition key. /// </summary> /// <param name="partitionKeyExtractor">The partition key extractor function.</param> /// <param name="client">The DocumentDB client instance to use.</param> /// <param name="database">The database to run the samples on.</param> /// <param name="collectionNames">The names of collections used.</param> /// <returns>The created HashPartitionResolver.</returns> public static async Task <HashPartitionResolver> InitializeCustomHashResolver(Func <object, string> partitionKeyExtractor, DocumentClient client, Database database, string[] collectionNames) { // Set local to input. string[] CollectionNames = collectionNames; int numCollectionNames = CollectionNames.Length; // Create array of DocumentCollections. DocumentCollection[] collections = new DocumentCollection[numCollectionNames]; // Create string array of Self Links to Collections. string[] selfLinks = new string[numCollectionNames]; //Create some collections to partition data. for (int i = 0; i < numCollectionNames; i++) { collections[i] = await DocumentClientHelper.GetCollectionAsync(client, database, CollectionNames[i]); selfLinks[i] = collections[i].SelfLink; } // Join Self Link Array into a comma separated string. string selfLinkString = String.Join(", ", selfLinks); var hashResolver = new HashPartitionResolver( partitionKeyExtractor, new[] { selfLinkString }); client.PartitionResolvers[database.SelfLink] = hashResolver; return(hashResolver); }
public async Task <IHttpActionResult> ManagedHashPartitionResolver() { string[] collections = AppSettingsConfig.MainCollection.Split(','); var database = await DocumentClientHelper.GetNewDatabaseAsync(_client, AppSettingsConfig.Db); ManagedHashPartitionResolver managedHashResolver = PartitionInitializers.InitializeManagedHashResolver(u => ((UserProfile)u).UserId, _client, database, 3, null); return(Ok()); }
/// <summary> /// Create a collection if the list is empty, or if the latest one is getting full. /// </summary> private void CreateCollectionIfRequired() { if (this.ShouldCreateCollection()) { string collectionId = string.Format("{0}{1}", this.CollectionIdPrefix, this.CollectionLinks.Count); var createdCollection = DocumentClientHelper.GetCollectionAsync(this.Client, this.Database, collectionId, this.CollectionTemplate).Result; this.CollectionLinks.Add(createdCollection.SelfLink); } }
public async Task <IHttpActionResult> LookupPartitionResolver() { string[] collections = AppSettingsConfig.MainCollection.Split(','); var database = await DocumentClientHelper.GetNewDatabaseAsync(_client, AppSettingsConfig.Db); LookupPartitionResolver <string> lookupResolver = await PartitionInitializers.InitializeLookupPartitionResolver("UserId", _client, database, collections); return(Ok()); }
private static async Task PerformIndexTransformations() { var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); Console.WriteLine("\n7. Perform index transform"); // Create a collection with default indexing policy var collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, databaseId, new DocumentCollection { Id = collectionId }); Console.WriteLine("Collection {0} created with index policy \n{1}", collection.Id, collection.IndexingPolicy); // Insert some documents await client.CreateDocumentAsync(collectionUri, new { id = "dyn1", length = 10, width = 5, height = 15 }); await client.CreateDocumentAsync(collectionUri, new { id = "dyn2", length = 7, width = 15 }); await client.CreateDocumentAsync(collectionUri, new { id = "dyn3", length = 2 }); // Switch to lazy indexing and wait till complete. Console.WriteLine("Changing from Default to Lazy IndexingMode."); // change the collection's indexing policy, // and then do a replace operation on the collection collection.IndexingPolicy.IndexingMode = IndexingMode.Lazy; await client.ReplaceDocumentCollectionAsync(collection); // Check progress and wait for completion - should be instantaneous since we have only a few documents, but larger // collections will take time. await WaitForIndexTransformationToComplete(collection); // Switch to use string & number range indexing with maximum precision. Console.WriteLine("Changing to string & number range indexing with maximum precision (needed for Order By)."); collection.IndexingPolicy = new IndexingPolicy(new RangeIndex(DataType.String) { Precision = -1 }); collection.IndexingPolicy.IndexingMode = IndexingMode.Consistent; // Apply change and wait until it completes await client.ReplaceDocumentCollectionAsync(collection); await WaitForIndexTransformationToComplete(collection); // Now exclude a path from indexing to save on storage space. Console.WriteLine("Changing to exclude some paths from indexing."); collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath() { Path = "/length/*" }); // Apply change, and wait for completion. Once complete, you can run string range and order by queries. await client.ReplaceDocumentCollectionAsync(collection); await WaitForIndexTransformationToComplete(collection); }
public async Task <IHttpActionResult> SpilloverPartitionResolver() { string[] collections = AppSettingsConfig.MainCollection.Split(','); var database = await DocumentClientHelper.GetNewDatabaseAsync(_client, AppSettingsConfig.Db); SpilloverPartitionResolver spilloverResolver = new SpilloverPartitionResolver(_client, database); _client.PartitionResolvers[database.SelfLink] = spilloverResolver; return(Ok()); }
private static async Task DoAsync() { var database = await DocumentClientHelper.GetDatabaseAsync(client, ConfigurationHelper.DatabaseId); var resolver = await InitializeHashResolver(database); await RunImport(database); await WriteCollectionSizes(resolver); }
private static async Task <HashPartitionResolver> InitializeHashResolver(Database database) { var collection1 = await DocumentClientHelper.GetCollectionAsync(client, database, "TweeterStatus.HashBucket0"); var collection2 = await DocumentClientHelper.GetCollectionAsync(client, database, "TweeterStatus.HashBucket1"); HashPartitionResolver hashResolver = new DisplayableHashPartitionResolver(new [] { collection1, collection2 }, PartitionKeyExtractor); client.PartitionResolvers[database.SelfLink] = hashResolver; return(hashResolver); }
/// <summary> /// Initialize a RangePartitionResolver. /// </summary> /// <param name="partitionKeyPropertyName">The property name to be used as the partition Key.</param> /// <param name="client">The DocumentDB client instance to use.</param> /// <param name="database">The database to run the samples on.</param> /// <param name="collectionNames">The names of collections used.</param> /// <returns>The created HashPartitionResolver.</returns> public static async Task <RangePartitionResolver <string> > InitializeRangeResolver(string partitionKeyPropertyName, DocumentClient client, Database database, string[] collectionNames) { // Set local to input. string[] CollectionNames = collectionNames; int numCollectionNames = CollectionNames.Length; // Create array of DocumentCollections. DocumentCollection[] collections = new DocumentCollection[numCollectionNames]; // Create string array of Self Links to Collections. string[] selfLinks = new string[numCollectionNames]; //Create some collections to partition data. for (int i = 0; i < numCollectionNames; i++) { collections[i] = await DocumentClientHelper.GetCollectionAsync(client, database, CollectionNames[i]); selfLinks[i] = collections[i].SelfLink; } // Join Self Link Array into a comma separated string. string selfLinkString = String.Join(", ", selfLinks); // If each collection represents a range, it will have both a start and end point in its range, thus there are 2 rangeNames for each collection. string[] rangeNames = new string[numCollectionNames * 2]; // Keeping track of where in the rangeNames array to add a new start/end of a range. int currentRangePosition = 0; for (int y = 0; y < numCollectionNames; y++) { string[] rangeTemp = collectionNames[y].Split('-'); for (int z = 0; z < rangeTemp.Length; z++) { rangeNames[currentRangePosition] = rangeTemp[z]; currentRangePosition++; } } // Dictionary needed to input into RangePartitionResolver with corresponding range and collection self link. Dictionary <Range <string>, string> rangeCollections = new Dictionary <Range <string>, string>(); // TO DO:: Iterate through the ranges and add then to rangeCollections with the appropriate start/end point, with the corresponding collection self link. //rangeCollections.Add() RangePartitionResolver <string> rangeResolver = new RangePartitionResolver <string>( partitionKeyPropertyName, rangeCollections); client.PartitionResolvers[database.SelfLink] = rangeResolver; return(rangeResolver); }
/// <summary> /// Initialize a HashPartitionResolver that uses a custom function to extract the partition key. /// </summary> /// <param name="database">The database to run the samples on.</param> /// <returns>The created HashPartitionResolver.</returns> private async Task <HashPartitionResolver> InitializeCustomHashResolver(Database database) { DocumentCollection collection1 = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.HashBucket0"); DocumentCollection collection2 = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.HashBucket1"); var hashResolver = new HashPartitionResolver( u => ((UserProfile)u).UserId, new[] { collection1.SelfLink, collection2.SelfLink }); this.client.PartitionResolvers[database.SelfLink] = hashResolver; return(hashResolver); }
/// <summary> /// Initialize a HashPartitionResolver. /// </summary> /// <param name="database">The database to run the samples on.</param> /// <returns>The created HashPartitionResolver.</returns> private async Task <HashPartitionResolver> InitializeHashResolver(Database database) { // Create some collections to partition data. DocumentCollection collection1 = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.HashBucket0"); DocumentCollection collection2 = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.HashBucket1"); // Initialize a partition resolver that users hashing, and register with DocumentClient. HashPartitionResolver hashResolver = new HashPartitionResolver("UserId", new[] { collection1.SelfLink, collection2.SelfLink }); this.client.PartitionResolvers[database.SelfLink] = hashResolver; return(hashResolver); }
/// <summary> /// When a range index is not available (i.e. Only hash or no index found on the path), comparisons queries can still /// can still be performed as scans using AllowScanInQuery request option using the .NET SDK /// /// This method demonstrates how to force a scan when only hash indexes exist on the path /// </summary> private static async Task RangeScanOnHashIndex() { // ************************************************************************************************************* // Warning: This was made an opt-in model by design. // Scanning is an expensive operation and doing this will have a large impact // on RequstUnits charged for an operation and will likely result in queries being throttled sooner. // ************************************************************************************************************* var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); Console.WriteLine("\n5. Force a range scan operation on a hash indexed path"); var collDefinition = new DocumentCollection { Id = collectionId }; collDefinition.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/" }); collDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/length/*" }); var collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, databaseId, collDefinition); Console.WriteLine("Collection {0} created with index policy \n{1}", collection.Id, collection.IndexingPolicy); var doc1 = await client.CreateDocumentAsync(collection.SelfLink, new { id = "dyn1", length = 10, width = 5, height = 15 }); var doc2 = await client.CreateDocumentAsync(collection.SelfLink, new { id = "dyn2", length = 7, width = 15 }); var doc3 = await client.CreateDocumentAsync(collection.SelfLink, new { id = "dyn3", length = 2 }); // Query for length > 5 - fail, this is a range based query on a Hash index only document var found = ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.length > 5"); Console.WriteLine("Range query allowed? {0}", found); // Now add IndexingDirective and repeat query // expect success because now we are explictly allowing scans in a query // using the EnableScanInQuery directive found = ShowQueryIsAllowed(collection, "SELECT * FROM root r WHERE r.length > 5", new FeedOptions { EnableScanInQuery = true }); Console.WriteLine("Range query allowed? {0}", found); //Cleanup await client.DeleteDocumentCollectionAsync(collectionUri); }
/// <summary> /// The default index policy on a DocumentCollection will AUTOMATICALLY index ALL documents added. /// There may be cases where you can want to turn-off automatic indexing and only selectively add only specific documents to the index. /// /// This method demonstrates how to control this by setting the value of IndexingPolicy.Automatic /// </summary> private static async Task UseManualIndexing() { var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); Console.WriteLine("\n2. Use manual (instead of automatic) indexing"); var collectionSpec = new DocumentCollection { Id = collectionId }; collectionSpec.IndexingPolicy.Automatic = false; var collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, databaseId, collectionSpec); Console.WriteLine("Collection {0} created with index policy \n{1}", collection.Id, collection.IndexingPolicy); // Create a dynamic document, with just a single property for simplicity, // then query for document using that property and we should find nothing // BUT, the document is there and doing a ReadDocument by Id will retrieve it Document created = await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc1", orderId = "order1" }); Console.WriteLine("\nDocument created: \n{0}", created); bool found = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM root r WHERE r.orderId = 'order1'").AsEnumerable().Any(); Console.WriteLine("Document found by query: {0}", found); Document doc = await client.ReadDocumentAsync(created.SelfLink); Console.WriteLine("Document read by id: {0}", doc != null); // Now create a document, passing in an IndexingDirective saying we want to specifically index this document // Query for the document again and this time we should find it because we manually included the document in the index created = await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc2", orderId = "order2" }, new RequestOptions { IndexingDirective = IndexingDirective.Include }); Console.WriteLine("\nDocument created: \n{0}", created); found = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM root r WHERE r.orderId = 'order2'").AsEnumerable().Any(); Console.WriteLine("Document found by query: {0}", found); // Cleanup collection await client.DeleteDocumentCollectionAsync(collectionUri); }
private void CreateCollectionIfRequired() { if (this.ShouldCreateCollection()) { try { string collectionId = string.Format("{0}{1}", this.CollectionIdPrefix, NextCollectionNumber); var createdCollection = DocumentClientHelper.GetOrCreateCollectionAsync(this.Client, this.Database.Id, collectionId, this.CollectionTemplate).Result; this.CollectionLinks.Add(createdCollection.SelfLink); } catch { this.CollectionLinks = GetCollections(this.Client, this.Database, this.CollectionIdPrefix, this.CollectionTemplate); } } }
/// <summary> /// Gets or creates the collections for the hash resolver. /// </summary> /// <param name="client">The DocumentDB client instance.</param> /// <param name="database">The database to use.</param> /// <param name="numberOfCollections">The number of collections.</param> /// <param name="collectionIdPrefix">The prefix to use while creating collections.</param> /// <param name="spec">The specification/template to use to create collections.</param> /// <returns>The list of collection self links.</returns> private static List <string> GetCollections( DocumentClient client, Database database, int numberOfCollections, string collectionIdPrefix, DocumentCollectionSpec spec) { var collections = new List <string>(); for (int i = 0; i < numberOfCollections; i++) { string collectionId = string.Format("{0}{1}", collectionIdPrefix, i); var collection = DocumentClientHelper.GetCollectionAsync(client, database, collectionId, spec).Result; collections.Add(collection.SelfLink); } return(collections); }
private static async Task RangeScanOnHashIndex(Database database) { Console.WriteLine("Trying query with EnableScanInQuery option to run a range query against a hash index"); // When a range index is not available (i.e. Only hash or no index found on the path), comparisons queries can still // can still be performed as scans using AllowScanInQuery request option using the .NET SDK // Warning: This was made an opt-in model by design. Scanning is an expensive operation and doing this // will have an impact on your RequstUnits and could result in other queries not being throttled. var collection = new DocumentCollection { Id = ConfigurationManager.AppSettings["CollectionId"] }; collection.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/" }); collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/length/*" }); collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection); var doc1 = await client.CreateDocumentAsync(collection.SelfLink, new { id = "dyn1", length = 10, width = 5, height = 15 }); var doc2 = await client.CreateDocumentAsync(collection.SelfLink, new { id = "dyn2", length = 7, width = 15 }); var doc3 = await client.CreateDocumentAsync(collection.SelfLink, new { id = "dyn3", length = 2 }); // Query for length > 5 - fail, this is a range based query on a Hash index only document ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.length > 5"); // Now add IndexingDirective and repeat query - expect success because now we are explictly allowing scans in a query // using the EnableScanInQuery directive ShowQueryIsAllowed(collection, "SELECT * FROM root r WHERE r.length > 5", new FeedOptions { EnableScanInQuery = true }); //Cleanup await client.DeleteDocumentCollectionAsync(collection.SelfLink); Console.WriteLine("Done with Query scan hints."); }
private static async Task <Permission> CreatePermissionAsync(string resourceLink, string userLink, PermissionMode mode, string resourcePartitionKey = null) { Permission permission = new Permission { Id = Guid.NewGuid().ToString("N"), PermissionMode = mode, ResourceLink = resourceLink }; if (resourcePartitionKey != null) { permission.ResourcePartitionKey = new PartitionKey(resourcePartitionKey); } ResourceResponse <Permission> response = await DocumentClientHelper.ExecuteWithRetries <ResourceResponse <Permission> >( client, () => client.CreatePermissionAsync(userLink, permission)); return(response.Resource); }
/// <summary> /// Initialize a RangePartitionResolver. /// </summary> /// <param name="database">The database to run the samples on.</param> /// <returns>The created RangePartitionResolver.</returns> private async Task <RangePartitionResolver <string> > InitializeRangeResolver(Database database) { // Create some collections to partition data. DocumentCollection collection1 = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.A-M"); DocumentCollection collection2 = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.N-Z"); // Initialize a partition resolver that assigns users (A-M) -> collection1, and (N-Z) -> collection2 // and register with DocumentClient. // Note: \uffff is the largest UTF8 value, so M\ufff includes all strings that start with M. RangePartitionResolver <string> rangeResolver = new RangePartitionResolver <string>( "UserId", new Dictionary <Range <string>, string>() { { new Range <string>("A", "M\uffff"), collection1.SelfLink }, { new Range <string>("N", "Z\uffff"), collection2.SelfLink }, }); this.client.PartitionResolvers[database.SelfLink] = rangeResolver; return(rangeResolver); }
private static async Task UseManualIndexing(Database database) { Console.WriteLine("Trying manual indexing. Documents are indexed only if the create includes a IndexingDirective.Include"); //The default behavior for DocumentDB DocumentCollections is to automatically index every document written to it. //There are cases where you can want to turn-off automatic indexing on the collection //and selectively add only specific documents to the index. var collection = new DocumentCollection { Id = ConfigurationManager.AppSettings["CollectionId"] }; collection.IndexingPolicy.Automatic = false; collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection); // Create a dynamic document, with just a single property for simplicity, // then query for document using that property and we should find nothing Document created = await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc1", orderId = "order1" }); // This should be false as the document won't be in the index bool found = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM root r WHERE r.orderId = 'order1'").AsEnumerable().Any(); // If we do a specific Read on the Document we will find it because it is in the collection Document doc = await client.ReadDocumentAsync(created.SelfLink); // Now create a document, passing in an IndexingDirective saying we want to specifically index this document created = await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc2", orderId = "order2" }, new RequestOptions { IndexingDirective = IndexingDirective.Include }); // Query for the document again and this time we should find it because we manually included the document in the index found = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM root r WHERE r.orderId = 'order2'").AsEnumerable().Any(); // Cleanup collection await client.DeleteDocumentCollectionAsync(collection.SelfLink); Console.WriteLine("Done with manual indexing."); }
/// <summary> /// DocumentDB offers synchronous (consistent) and asynchronous (lazy) index updates. /// By default, the index is updated synchronously on each insert, replace or delete of a document to the collection. /// There are times when you might want to configure certain collections to update their index asynchronously. /// Lazy indexing boosts write performance and is ideal for bulk ingestion scenarios for primarily read-heavy collections /// It is important to note that you might get inconsistent reads whilst the writes are in progress, /// However once the write volume tapers off and the index catches up, then reads continue as normal /// /// This method demonstrates how to switch IndexMode to Lazy /// </summary> private static async Task UseLazyIndexing() { var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); Console.WriteLine("\n3. Use lazy (instead of consistent) indexing"); var collDefinition = new DocumentCollection { Id = ConfigurationManager.AppSettings["CollectionId"] }; collDefinition.IndexingPolicy.IndexingMode = IndexingMode.Lazy; var collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, databaseId, collDefinition); Console.WriteLine("Collection {0} created with index policy \n{1}", collection.Id, collection.IndexingPolicy); //it is very difficult to demonstrate lazy indexing as you only notice the difference under sustained heavy write load //because we're using an S1 collection in this demo we'd likely get throttled long before we were able to replicate sustained high throughput //which would give the index time to catch-up. await client.DeleteDocumentCollectionAsync(collectionUri); }
private static async Task UseLazyIndexing(Database database) { Console.WriteLine("Trying lazy indexing. Queries will be eventually consistent with this config."); // DocumentDB offers synchronous (consistent) and asynchronous (lazy) index updates. // By default, the index is updated synchronously on each insert, replace or delete of a document to the collection. // There are times when you might want to configure certain collections to update their index asynchronously. // Lazy indexing boosts the write performance further and is ideal for bulk ingestion scenarios for primarily read-heavy collections // It is important to note that you might get inconsistent reads whilst the writes are in progress, // However once the write volume tapers off and the index catches up, then the reads continue as normal var collection = new DocumentCollection { Id = ConfigurationManager.AppSettings["CollectionId"] }; collection.IndexingPolicy.IndexingMode = IndexingMode.Lazy; collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection); await client.DeleteDocumentCollectionAsync(collection.SelfLink); Console.WriteLine("Done with lazy indexing."); }
/// <summary> /// Initialize a LookupPartitionResolver. /// </summary> /// <param name="database">The database to run the samples on.</param> /// <returns>The created HashPartitionResolver.</returns> private async Task <LookupPartitionResolver <string> > InitializeLookupPartitionResolver(Database database) { DocumentCollection collectionUS = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.US"); DocumentCollection collectionEU = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.Europe"); DocumentCollection collectionOther = await DocumentClientHelper.GetCollectionAsync(this.client, database, "Collection.Other"); // This implementation takes strings as input. If you'd like to implement a strongly typed LookupPartitionResolver, // take a look at EnumLookupPartitionResolver for an example. var lookupResolver = new LookupPartitionResolver <string>( "PrimaryRegion", new Dictionary <string, string>() { { Region.UnitedStatesEast.ToString(), collectionUS.SelfLink }, { Region.UnitedStatesWest.ToString(), collectionUS.SelfLink }, { Region.Europe.ToString(), collectionEU.SelfLink }, { Region.AsiaPacific.ToString(), collectionOther.SelfLink }, { Region.Other.ToString(), collectionOther.SelfLink }, }); this.client.PartitionResolvers[database.SelfLink] = lookupResolver; return(lookupResolver); }
/// <summary> /// Initialize a LookupPartitionResolver. Default is for US East/West to go to first collection name, Europe to second, and AsiaPacific / Other to third. /// </summary> /// <param name="partitionKeyPropertyName">The property name to be used as the partition Key.</param> /// <param name="client">The DocumentDB client instance to use.</param> /// <param name="database">The database to run the samples on.</param> /// <param name="collectionNames">The names of collections used.</param> /// <returns>The created HashPartitionResolver.</returns> public static async Task <LookupPartitionResolver <string> > InitializeLookupPartitionResolver(string partitionKeyPropertyName, DocumentClient client, Database database, string[] collectionNames) { // Set local to input. string[] CollectionNames = collectionNames; int numCollectionNames = CollectionNames.Length; // Create array of DocumentCollections. DocumentCollection[] collections = new DocumentCollection[numCollectionNames]; // Create string array of Self Links to Collections. string[] selfLinks = new string[numCollectionNames]; //Create some collections to partition data. for (int i = 0; i < numCollectionNames; i++) { collections[i] = await DocumentClientHelper.GetCollectionAsync(client, database, CollectionNames[i]); selfLinks[i] = collections[i].SelfLink; } // This implementation takes strings as input. If you'd like to implement a strongly typed LookupPartitionResolver, // take a look at EnumLookupPartitionResolver for an example. var lookupResolver = new LookupPartitionResolver <string>( partitionKeyPropertyName, new Dictionary <string, string>() { { Region.UnitedStatesEast.ToString(), selfLinks[0] }, { Region.UnitedStatesWest.ToString(), selfLinks[0] }, { Region.Europe.ToString(), selfLinks[1] }, { Region.AsiaPacific.ToString(), selfLinks[2] }, { Region.Other.ToString(), selfLinks[2] }, }); client.PartitionResolvers[database.SelfLink] = lookupResolver; return(lookupResolver); }
/// <summary> /// The default behavior is for DocumentDB to index every attribute in every document automatically. /// There are times when a document contains large amounts of information, in deeply nested structures /// that you know you will never search on. In extreme cases like this, you can exclude paths from the /// index to save on storage cost, improve write performance and also improve read performance because the index is smaller /// /// This method demonstrates how to set IndexingPolicy.ExcludedPaths /// </summary> private static async Task ExcludePathsFromIndex() { string collectionId = string.Format(CultureInfo.InvariantCulture, "{0}-ExcludePathsFromIndex", collectionIdPrefix); var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); Console.WriteLine("\n3. Exclude specified paths from document index"); var collDefinition = new DocumentCollection { Id = collectionId }; collDefinition.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" }); // Special manadatory path of "/*" required to denote include entire tree collDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/metaData/*" }); // exclude metaData node, and anything under it collDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/subDoc/nonSearchable/*" }); // exclude ONLY a part of subDoc collDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/\"excludedNode\"/*" }); // exclude excludedNode node, and anything under it // The effect of the above IndexingPolicy is that only id, foo, and the subDoc/searchable are indexed var collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, databaseId, collDefinition); Console.WriteLine("Collection {0} created with index policy \n{1}", collection.Id, collection.IndexingPolicy); int numDocs = 250; Console.WriteLine("Creating {0} documents", numDocs); for (int docIndex = 0; docIndex < numDocs; docIndex++) { dynamic dyn = new { id = "doc" + docIndex, foo = "bar" + docIndex, metaData = "meta" + docIndex, subDoc = new { searchable = "searchable" + docIndex, nonSearchable = "value" + docIndex }, excludedNode = new { subExcluded = "something" + docIndex, subExcludedNode = new { someProperty = "value" + docIndex } } }; Document created = await client.CreateDocumentAsync(collection.SelfLink, dyn); Console.WriteLine("Creating document with id {0}", created.Id); } // Querying for a document on either metaData or /subDoc/subSubDoc/someProperty will be expensive since they do not utilize the index, // but instead are served from scan automatically. int queryDocId = numDocs / 2; QueryStats queryStats = await GetQueryResult(collection, string.Format(CultureInfo.InvariantCulture, "SELECT * FROM root r WHERE r.metaData='meta{0}'", queryDocId)); Console.WriteLine("Query on metaData returned {0} results", queryStats.Count); Console.WriteLine("Query on metaData consumed {0} RUs", queryStats.RequestCharge); queryStats = await GetQueryResult(collection, string.Format(CultureInfo.InvariantCulture, "SELECT * FROM root r WHERE r.subDoc.nonSearchable='value{0}'", queryDocId)); Console.WriteLine("Query on /subDoc/nonSearchable returned {0} results", queryStats.Count); Console.WriteLine("Query on /subDoc/nonSearchable consumed {0} RUs", queryStats.RequestCharge); queryStats = await GetQueryResult(collection, string.Format(CultureInfo.InvariantCulture, "SELECT * FROM root r WHERE r.excludedNode.subExcludedNode.someProperty='value{0}'", queryDocId)); Console.WriteLine("Query on /excludedNode/subExcludedNode/someProperty returned {0} results", queryStats.Count); Console.WriteLine("Query on /excludedNode/subExcludedNode/someProperty cost {0} RUs", queryStats.RequestCharge); // Querying for a document using food, or even subDoc/searchable > consume less RUs because they were not excluded queryStats = await GetQueryResult(collection, string.Format(CultureInfo.InvariantCulture, "SELECT * FROM root r WHERE r.foo='bar{0}'", queryDocId)); Console.WriteLine("Query on /foo returned {0} results", queryStats.Count); Console.WriteLine("Query on /foo cost {0} RUs", queryStats.RequestCharge); queryStats = await GetQueryResult(collection, string.Format(CultureInfo.InvariantCulture, "SELECT * FROM root r WHERE r.subDoc.searchable='searchable{0}'", queryDocId)); Console.WriteLine("Query on /subDoc/searchable returned {0} results", queryStats.Count); Console.WriteLine("Query on /subDoc/searchable cost {0} RUs", queryStats.RequestCharge); //Cleanup await client.DeleteDocumentCollectionAsync(collectionUri); }
/// <summary> /// Import data into a DocumentDB collection using a "Bulk Import" stored procedure. /// </summary> /// <param name="collection">The collection to run queries against.</param> /// <param name="sourceDirectory">The source directory to read files from.</param> /// <returns></returns> private static async Task ImportData(DocumentCollection collection, string sourceDirectory) { await DocumentClientHelper.RunBulkImport(client, collection, sourceDirectory); }
private static async Task UsingRangeIndexes() { string collectionId = string.Format(CultureInfo.InvariantCulture, "{0}-UsingRangeIndexes", collectionIdPrefix); var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); Console.WriteLine("\n4. Using range indexes"); var collDefinition = new DocumentCollection { Id = collectionId }; // This is how you can specify a range index on strings (and numbers) for all properties. This is the recommended indexing policy for collections. IndexingPolicy indexingPolicy = new IndexingPolicy(new RangeIndex(DataType.String) { Precision = -1 }); // For demo purposes, we are going to exclude all paths in the document, and only // include a range index on strings for the "region". indexingPolicy = new IndexingPolicy(); indexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/region/?", Indexes = new Collection <Index>() { new RangeIndex(DataType.String) { Precision = -1 } } }); indexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/*" }); collDefinition.IndexingPolicy = indexingPolicy; var collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, databaseId, collDefinition); Console.WriteLine("Collection {0} created with index policy \n{1}", collection.Id, collection.IndexingPolicy); await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc1", region = "USA" }); await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc2", region = "UK" }); await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc3", region = "Armenia" }); await client.CreateDocumentAsync(collection.SelfLink, new { id = "doc4", region = "Egypt" }); Console.WriteLine("Documents ordered by region"); foreach (var doc in client.CreateDocumentQuery(collectionUri, "SELECT * FROM orders o ORDER BY o.region")) { Console.WriteLine(doc); } // You can also perform filters against string comparisons like >= 'UK'. Note that you can perform a prefix query, // the equivalent of LIKE 'U%' (is >= 'U' AND < 'U') Console.WriteLine("Documents with region begining with U"); foreach (var doc in client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM orders o WHERE o.region >= 'U'")) { Console.WriteLine(doc); } // Cleanup await client.DeleteDocumentCollectionAsync(collection.SelfLink); }