/// <summary> /// Create a post trigger that updates metadata: for each inserted doc it will look at doc.size /// and update aggregate properties: { minSize, maxSize, totalSize } in the metadata doc. /// In the end print to show the aggregate values of min, max, total for all docs. /// </summary> private static async Task RunPostTrigger(string colSelfLink) { Random rnd = new Random(); // 1. Create a trigger. string triggerPath = @"js\UpdateMetadata.js"; string triggerId = Path.GetFileNameWithoutExtension(triggerPath); string triggerBody = File.ReadAllText(triggerPath); Trigger trigger = new Trigger { Id = Path.GetFileName(triggerId), Body = triggerBody, TriggerOperation = TriggerOperation.Create, TriggerType = TriggerType.Post }; await TryDeleteStoredProcedure(colSelfLink, trigger.Id); await client.CreateTriggerAsync(colSelfLink, trigger); // 2. Create the metadata document. var metaDoc = new { id = "meta", isMetadata = true, minSize = 0, maxSize = 0, totalSize = 0 }; await client.CreateDocumentAsync(colSelfLink, metaDoc); // 3. Import a number of docs with trigger. Use client API this time, we already have sample fot using script. var requestOptions = new RequestOptions { PostTriggerInclude = new List<string> { triggerId } }; await client.CreateDocumentAsync(colSelfLink, new { size = rnd.Next(1000), }, requestOptions); await client.CreateDocumentAsync(colSelfLink, new { size = rnd.Next(1000), }, requestOptions); await client.CreateDocumentAsync(colSelfLink, new { size = rnd.Next(1000), }, requestOptions); await client.CreateDocumentAsync(colSelfLink, new { size = rnd.Next(1000), }, requestOptions); await client.CreateDocumentAsync(colSelfLink, new { size = rnd.Next(1000), }, requestOptions); // 4. Print aggregate info from the metadata document. metaDoc = client.CreateDocumentQuery<dynamic>(colSelfLink, "SELECT * FROM root r WHERE r.isMetadata = true").AsEnumerable().First(); Console.WriteLine("Document statistics: min size: {0}, max size: {1}, total size: {2}", metaDoc.minSize, metaDoc.maxSize, metaDoc.totalSize); }
/// <summary> /// Runs a simple script which just does a server side query /// </summary> private static async Task RunSimpleScript(string colSelfLink) { // 1. Create stored procedure for script. string scriptFileName = @"js\SimpleScript.js"; string scriptId = Path.GetFileNameWithoutExtension(scriptFileName); var sproc = new StoredProcedure { Id = scriptId, Body = File.ReadAllText(scriptFileName) }; await TryDeleteStoredProcedure(colSelfLink, sproc.Id); sproc = await client.CreateStoredProcedureAsync(colSelfLink, sproc); // 2. Create a document. var doc = new { Name = "Estel", Headquarters = "Russia", Locations = new [] { new { Country = "Russia", City = "Novosibirsk" } }, Income = 50000 }; Document created = await client.CreateDocumentAsync(colSelfLink, doc); // 3. Run the script. Pass "Hello, " as parameter. // The script will take the 1st document and echo: Hello, <document as json>. var response = await client.ExecuteStoredProcedureAsync<string>(sproc.SelfLink, "Hello, "); Console.WriteLine("Result from script: {0}\r\n", response.Response); await client.DeleteDocumentAsync(created.SelfLink); }
private static async Task CreateToDoIndexAsync() { if (!await ResourceExistsAsync("/indexes/todo")) { var index = new { name = "todo", fields = new[] { new { name = "id", type = "Edm.String", key = true, facetable = false, filterable = false, searchable = false, sortable = false }, new { name = "title", type = "Edm.String", key = false, facetable = false, filterable = false, searchable = true, sortable = false }, new { name = "description", type = "Edm.String", key = false, facetable = false, filterable = false, searchable = true, sortable = false }, new { name = "dueDate", type = "Edm.DateTimeOffset", key = false, facetable = true, filterable = true, searchable = false, sortable = true }, new { name = "isComplete", type = "Edm.String", key = false, facetable = false, filterable = false, searchable = false, sortable = false }, new { name = "tags", type = "Collection(Edm.String)", key = false, facetable = true, filterable = true, searchable = true, sortable = false } }, suggesters = new[] { new { name = "sg", searchMode = "analyzingInfixMatching", sourceFields = new[] { "title", "tags" } } }, corsOptions = new { allowedOrigins = new[] { "*" } // use a specific domain when allowing Javascript hit your search index directly, "*" is used for demo purposes } }; await SendAsync(HttpMethod.Post, "/indexes", JsonConvert.SerializeObject(index)); } }
private static async Task ExcludePathsFromIndex(string databaseLink) { bool found; 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 IndexingPath {Path = "/" }); collection.IndexingPolicy.ExcludedPaths.Add("/\"metaData\"/*"); collection.IndexingPolicy.ExcludedPaths.Add("/\"subDoc\"/\"subSubDoc\"/\"someProperty\"/*"); collection = await client.CreateDocumentCollectionAsync(databaseLink, 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 try { client.CreateDocumentQuery(collection.SelfLink, String.Format("SELECT * FROM root r WHERE r.metaData='{0}'", "meta")).AsEnumerable().Any(); } catch (Exception e) { var baseEx = (DocumentClientException) e.GetBaseException(); if (baseEx.StatusCode != HttpStatusCode.BadRequest) { throw; } } try { found = client.CreateDocumentQuery(collection.SelfLink, String.Format("SELECT * FROM root r WHERE r.subDoc.subSubDoc.someProperty='{0}'", "value")).AsEnumerable().Any(); } catch (Exception e) { var baseEx = (DocumentClientException)e.GetBaseException(); if (baseEx.StatusCode != HttpStatusCode.BadRequest) { throw; } } //Querying for a document using id, or even subDoc/searchable > succeed because they were not excluded found = client.CreateDocumentQuery(collection.SelfLink, String.Format("SELECT * FROM root r WHERE r.id='{0}'", "doc1")).AsEnumerable().Any(); if (!found) throw new ApplicationException("Should've found the document"); found = client.CreateDocumentQuery(collection.SelfLink, String.Format("SELECT * FROM root r WHERE r.subDoc.searchable='{0}'", "searchable")).AsEnumerable().Any(); if (!found) throw new ApplicationException("Should've found the document"); //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 IndexingPath { Path = "/" }); collection.IndexingPolicy.ExcludedPaths.Add("/\"subDoc\"/*"); collection = await client.CreateDocumentCollectionAsync(databaseLink, collection); //Query for /subDoc/searchable > fail because we have excluded the whole subDoc, and all its children. try { client.CreateDocumentQuery(collection.SelfLink, String.Format("SELECT * FROM root r WHERE r.subDoc.searchable='{0}'", "searchable")).AsEnumerable().Any(); } catch (Exception e) { var baseEx = (DocumentClientException)e.GetBaseException(); if (baseEx.StatusCode != HttpStatusCode.BadRequest) { throw; } } //Cleanup await client.DeleteDocumentCollectionAsync(collection.SelfLink); }
private static async Task CreateDocumentDBIndexerAsync() { string account = new Uri(ConfigurationManager.AppSettings["docdb-endpoint"]).Host.Split('.')[0]; string authKey = ConfigurationManager.AppSettings["docdb-authKey"]; string database = ConfigurationManager.AppSettings["docdb-database"]; string collection = ConfigurationManager.AppSettings["docdb-collection"]; // create data source if (!await ResourceExistsAsync("/datasources/tododocdb")) { var dataSource = new { name = "tododocdb", type = "documentdb", credentials = new { connectionString = String.Format("AccountName={0};AuthKey={1};DatabaseId={2}", account, authKey, database) }, container = new { name = collection }, dataChangeDetectionPolicy = new Dictionary<string, object> { { "@odata.type", "#Microsoft.Azure.Search.HighWaterMarkChangeDetectionPolicy" }, { "highWaterMarkColumnName", "_ts" }, // DocumentDB has a built-in timestamp property called "_ts" } }; await SendAsync(HttpMethod.Post, "/datasources", JsonConvert.SerializeObject(dataSource)); } // create indexer and schedule it if (!await ResourceExistsAsync("/indexers/todoixr")) { var indexer = new { name = "todoixr", dataSourceName = "tododocdb", schedule = new { interval = "PT5M" }, // every 5 minutes targetIndexName = "todo" }; await SendAsync(HttpMethod.Post, "/indexers", JsonConvert.SerializeObject(indexer)); } }