/// <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);
        }
Example #4
0
        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());
        }
Example #9
0
        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);
        }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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);
        }
Example #15
0
        /// <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);
        }
Example #16
0
        /// <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);
        }
Example #17
0
        /// <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);
         }
     }
 }
Example #19
0
        /// <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);
        }
Example #20
0
        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.");
        }
Example #21
0
        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);
        }
Example #22
0
        /// <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);
        }
Example #23
0
        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.");
        }
Example #24
0
        /// <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);
        }
Example #25
0
        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.");
        }
Example #26
0
        /// <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);
        }
Example #27
0
        /// <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);
        }
Example #28
0
        /// <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);
        }
Example #29
0
 /// <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);
 }
Example #30
0
        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);
        }