/// <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);
        }
        /// <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);
        }
        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);
        }
Exemple #4
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);
        }
Exemple #5
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);
        }
Exemple #6
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 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 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.");
        }
Exemple #9
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);
        }
        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.");
        }
Exemple #11
0
        private static async Task UseRangeIndexesOnStrings()
        {
            var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId);

            Console.WriteLine("\n6. Use range indexes on strings");

            var collDefinition = new DocumentCollection {
                Id = collectionId
            };

            // We COULD create a Range index for ALL paths for Numbers AND Strings.
            // This indexing policy gives you the most flexibility as it allows you to perform range queries and order by on strings and number on the whole document.
            // This might have a higher index storage overhead if you have long strings or a large number of unique strings.
            // You can be selective of which paths need a Range index through IncludedPath configuration
            IndexingPolicy indexingPolicy = new IndexingPolicy();

            indexingPolicy.IncludedPaths.Add(new IncludedPath
            {
                Path    = "/*",
                Indexes = new Collection <Index>()
                {
                    new RangeIndex(DataType.Number)
                    {
                        Precision = -1
                    },
                    new RangeIndex(DataType.String)
                    {
                        Precision = -1
                    }
                }
            });

            // So instead, for demo purposes, we are going to use the default (range on numbers, hash on strings) for the whole document (/* )
            // and just include a range index on strings for the "region".
            indexingPolicy = new IndexingPolicy();
            indexingPolicy.IncludedPaths.Add(new IncludedPath {
                Path = "/*"
            });
            indexingPolicy.IncludedPaths.Add(new IncludedPath
            {
                Path    = "/region/?",
                Indexes = new Collection <Index>()
                {
                    new RangeIndex(DataType.String)
                    {
                        Precision = -1
                    }
                }
            });

            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" });

            // Now ordering against region is allowed. You can run the following query
            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);
        }
Exemple #12
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);
        }
Exemple #13
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);
        }
Exemple #14
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()
        {
            var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId);

            Console.WriteLine("\n4. Exclude specified paths from document index");

            dynamic dyn = new
            {
                id           = "doc1",
                foo          = "bar",
                metaData     = "meta",
                subDoc       = new { searchable = "searchable", nonSearchable = "value" },
                excludedNode = new { subExcluded = "something", subExcludedNode = new { someProperty = "value" } }
            };

            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);

            Document created = await client.CreateDocumentAsync(collection.SelfLink, dyn);

            Console.WriteLine("\nDocument created: \n{0}", created);

            // Querying for a document on either metaData or /subDoc/subSubDoc/someProperty > fail because they were excluded
            var found = ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.metaData='meta'");

            Console.WriteLine("Query on metaData returned results? {0}", found);

            found = ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.subDoc.nonSearchable='value'");
            Console.WriteLine("Query on /subDoc/nonSearchable/ returned results? {0}", found);

            found = ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.excludedNode.subExcludedNode.someProperty='value'");
            Console.WriteLine("Query on /excludedNode/subExcludedNode/someProperty/ returned results? {0}", found);

            // Querying for a document using food, or even subDoc/searchable > succeed because they were not excluded
            found = ShowQueryIsAllowed(collection, "SELECT * FROM root r WHERE r.foo='bar'");
            Console.WriteLine("Query on foo returned results? {0}", found);

            found = ShowQueryIsAllowed(collection, "SELECT * FROM root r WHERE r.subDoc.searchable='searchable'");
            Console.WriteLine("Query on /subDoc/searchable/ returned results? {0}", found);

            //Cleanup
            await client.DeleteDocumentCollectionAsync(collectionUri);
        }
        private static async Task PerformIndexTransformations(Database database)
        {
            Console.WriteLine("Performing indexing transformations on an existing collection ...");

            // Create a collection with default indexing policy
            var collection = new DocumentCollection {
                Id = ConfigurationManager.AppSettings["CollectionId"]
            };

            collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection);

            // Insert some documents
            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 });

            // Switch to lazy indexing and wait till complete.
            Console.WriteLine("Changing from Default to Lazy IndexingMode.");

            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 range indexing with maximum precision.
            Console.WriteLine("Changing to string range indexing with maximum precision for Order By.");

            collection.IndexingPolicy.IndexingMode  = IndexingMode.Consistent;
            collection.IndexingPolicy.IncludedPaths = new Collection <IncludedPath>()
            {
                new IncludedPath()
                {
                    Path    = "/*",
                    Indexes = new Collection <Index>()
                    {
                        new RangeIndex(DataType.Number)
                        {
                            Precision = -1
                        },
                        new RangeIndex(DataType.String)
                        {
                            Precision = -1
                        }
                    }
                }
            };

            // Apply change
            await client.ReplaceDocumentCollectionAsync(collection);

            // Wait for completion. Once complete, you can run string range and order by queries.
            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 = "/excluded/*"
            });

            // Apply change
            await client.ReplaceDocumentCollectionAsync(collection);

            // Wait for completion. Once complete, you can run string range and order by queries.
            await WaitForIndexTransformationToComplete(collection);

            Console.WriteLine("Done with indexing policy transformations.");
        }
        private static async Task ExcludePathsFromIndex(Database database)
        {
            Console.WriteLine("Trying exclusions of paths from indexing to save storage space and improve write throughput.");

            dynamic dyn = new
            {
                id       = "doc1",
                metaData = "meta",
                subDoc   = new { searchable = "searchable", subSubDoc = new { someProperty = "value" } }
            };

            // The default behavior is for DocumentDB to index every attribute in every document.
            // 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 because there is less work that needs to
            // happen on writing and also improve read performance because the index is smaller

            var collection = new DocumentCollection {
                Id = ConfigurationManager.AppSettings["CollectionId"]
            };

            // Special manadatory path of "/*" required to denote include entire tree
            collection.IndexingPolicy.IncludedPaths.Add(new IncludedPath {
                Path = "/*"
            });

            collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath {
                Path = "/metaData/*"
            });
            collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath {
                Path = "/subDoc/subSubDoc/someProperty/*"
            });

            collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection);

            var created = await client.CreateDocumentAsync(collection.SelfLink, dyn);

            // Querying for a document on either metaData or /subDoc/subSubDoc/someProperty > fail because they were excluded
            ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.metaData='meta'");

            ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.subDoc.subSubDoc.someProperty='value'");

            // Querying for a document using id, or even subDoc/searchable > succeed because they were not excluded
            ShowQueryReturnsResults(collection, "SELECT * FROM root r WHERE r.id='doc1'");

            ShowQueryReturnsResults(collection, "SELECT * FROM root r WHERE r.subDoc.searchable='searchable'");

            // Cleanup collection
            await client.DeleteDocumentCollectionAsync(collection.SelfLink);

            // To exclude subDoc and anything under it add an ExcludePath of "/\"subDoc\"/*"
            collection = new DocumentCollection {
                Id = ConfigurationManager.AppSettings["CollectionId"]
            };

            // Special manadatory path of "/*" required to denote include entire tree
            collection.IndexingPolicy.IncludedPaths.Add(new IncludedPath {
                Path = "/*"
            });
            collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath {
                Path = "/subDoc/*"
            });

            collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection);

            // Query for /subDoc/searchable > fail because we have excluded the whole subDoc, and all its children.
            ShowQueryIsNotAllowed(collection, "SELECT * FROM root r WHERE r.subDoc.searchable='searchable'");

            //Cleanup
            await client.DeleteDocumentCollectionAsync(collection.SelfLink);

            Console.WriteLine("Done with Exclude paths.");
        }
        private static async Task UseRangeIndexesOnStrings(Database database)
        {
            Console.WriteLine("Trying Range index on strings. This enables Order By and range queries on strings.");

            var collection = new DocumentCollection {
                Id = ConfigurationManager.AppSettings["CollectionId"]
            };

            // Overide to Range, Max (-1) for Strings. This allows you to perform string range queries and string order by queries.
            // Note that this might have a higher index storage overhead however if you have long strings or a large number of unique
            // strings. You can be selective of which paths need a Range index through IncludedPath configuration,
            collection.IndexingPolicy.IncludedPaths.Add(new IncludedPath
            {
                Path    = "/*",
                Indexes = new Collection <Index>()
                {
                    new RangeIndex(DataType.Number)
                    {
                        Precision = -1
                    },
                    new RangeIndex(DataType.String)
                    {
                        Precision = -1
                    }
                }
            });

            // Alternatively, you can use the default for /* and just range for the "region".
            // Not creating collection with this in the sample, but this can be used instead.
            IndexingPolicy alternateIndexingPolicy = new IndexingPolicy();

            alternateIndexingPolicy.IncludedPaths.Add(new IncludedPath {
                Path = "/*"
            });
            alternateIndexingPolicy.IncludedPaths.Add(new IncludedPath
            {
                Path    = "/region/?",
                Indexes = new Collection <Index>()
                {
                    new RangeIndex(DataType.Number)
                    {
                        Precision = -1
                    }
                }
            });

            collection = await DocumentClientHelper.CreateDocumentCollectionWithRetriesAsync(client, database, collection);

            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" });

            // Now ordering against region is allowed. You can run the following query
            foreach (var doc in client.CreateDocumentQuery(
                         collection.SelfLink,
                         "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
            // i.e., the equivalent of LIKE 'U%' is >='U' AND < 'U\uffff'
            foreach (var doc in client.CreateDocumentQuery(
                         collection.SelfLink,
                         "SELECT * FROM orders o WHERE o.region >= 'UK'"))
            {
                Console.WriteLine(doc);
            }

            // Cleanup
            await client.DeleteDocumentCollectionAsync(collection.SelfLink);

            Console.WriteLine("Done with Range indexing on strings.");
        }