How to create indexes in MongoDB via .NET - c#

I've programmatically created a new document collection using the MongoDB C# driver.
At this point I want to create and build indexes programmatically. How can I do that?

Starting from v2.0 of the driver there's a new async-only API. The old API should no longer be used as it's a blocking facade over the new API and is deprecated.
The currently recommended way to create an index is by calling and awaiting CreateOneAsync with an IndexKeysDefinition you get by using Builders.IndexKeys:
static async Task CreateIndexAsync()
{
var client = new MongoClient();
var database = client.GetDatabase("HamsterSchool");
var collection = database.GetCollection<Hamster>("Hamsters");
var indexKeysDefinition = Builders<Hamster>.IndexKeys.Ascending(hamster => hamster.Name);
await collection.Indexes.CreateOneAsync(new CreateIndexModel<Hamster>(indexKeysDefinition));
}

you should use CreateIndex as EnsureIndex is marked obsolete for future compatibility with the next versions of MongoDB:
var client = new MongoClient("mongodb://localhost");
var db = client.GetServer().GetDatabase("db");
var collection = db.GetCollection<Hamster>("Hamsters");
collection.CreateIndex(IndexKeys<Hamster>.Ascending(_ => _.Name));

The overload of CreateOneAsync in the currently accepted answer is now marked as obsolete with the message "Use CreateOneAsync with a CreateIndexModel instead." Here's how you do it:
static async Task CreateIndex(string connectionString)
{
var client = new MongoClient(connectionString);
var database = client.GetDatabase("HamsterSchool");
var collection = database.GetCollection<Hamster>("Hamsters");
var indexOptions = new CreateIndexOptions();
var indexKeys = Builders<Hamster>.IndexKeys.Ascending(hamster => hamster.Name);
var indexModel = new CreateIndexModel<Hamster>(indexKeys, indexOptions);
await collection.Indexes.CreateOneAsync(indexModel);
}

Something like this should do:
var server = MongoServer.Create("mongodb://localhost");
var db = server.GetDatabase("myapp");
var users = db.GetCollection<User>("users");
users.EnsureIndex(new IndexKeysBuilder().Ascending("EmailAddress"));
Please see the following bits in the documentation:
http://api.mongodb.org/csharp/current/html/06bcd201-8844-3df5-d170-15f2b423675c.htm

There is an entire area on Indexing under the Definitions and Builders documentation page:
http://mongodb.github.io/mongo-csharp-driver/2.4/reference/driver/definitions/#index-keys
Example:
IndexKeysDefinition<MyModel> keys = "{ Reference: 1 }";
var indexModel = new CreateIndexModel<MyModel>(keys);
await _context.Indexes.CreateOneAsync(indexModel);

the easiest way to create indexes in c# is by using the driver wrapper library MongoDB.Entities. here's an example of creating a text index:
DB.Index<Author>()
.Key(a => a.Name, Type.Text)
.Key(a => a.Surname, Type.Text)
.Create();
and to do a full-text search, you simply do:
DB.SearchText<Author>("search term");
haven't seen anything else that makes it simpler than that.

Related

Programmatically find Partition Key Path of Cosmos Container

var cosmosClient = new CosmosClient(EndpointUrl, AuthorizationKey,
new CosmosClientOptions() {
AllowBulkExecution = true
});
var database = cosmosClient.GetDatabase(SourceDatabase);
var container = database.GetContainer(SourceContainerName);
I'm looking for an approach to programmatically find out
PartitionKeyPath for the container. Is there no API in the SDK to
obtain it from container object?
P.S. Microsoft displays partition key path in Azure portal.
Was wondering how do they display it?
In Azure SDK for .NET, JSON path used for containers partitioning can be found using
ContainerProperties.PartitionKeyPath Property
PartitionKeyPath defaults to "/PartitionKey". Refer: Ms doc
ContainerProperties cproperties = await container.ReadContainerAsync();
Console.WriteLine(cproperties.PartitionKeyPath);
Additionally, you can go ahead and share this feedback so the CosmosDB team can look into this idea.✌
To retrieve the partition key of a container you have to use the GetContainerQueryIterator() method. Depending on what is already available within your code, you could use something like this:
private static async Task<string> GetPartitionKey(Database database, string containerName)
{
var query = new QueryDefinition("select * from c where c.id = #id")
.WithParameter("#id", containerName);
using var iterator = database.GetContainerQueryIterator<ContainerProperties>(query);
while (iterator.HasMoreResults)
{
foreach (var container in await iterator.ReadNextAsync())
{
return container.PartitionKeyPath;
}
}
return null;
}
In version 3.31.2 you can access the PartitionKeyPath as follows
var containerResponse = await cosmosClient.GetDatabase(options.Value.Database)
.GetContainer(collectionId)
.ReadContainerAsync(cancellationToken: cancellationToken);
Console.WriteLine(containerResponse.Resource.PartitionKeyPath)

How I create another index using the elastic search?

I am new to Elasticsearch and NEST etc. using c#. So, far I have learned and managed to write a code to create an index but the problem is how do I create a second table (type). If I create it the same way then it only creates one table and not the second one.
Code:
public static void CreateIndex()
{
ConnectionSettings settings = new ConnectionSettings(new Uri("http://localhost:9200"));
settings.DefaultIndex("store");
ElasticClient client = new ElasticClient(settings);
client.Indices.Delete(Indices.Index("store"));
var indexSettings = client.Indices.Exists("store");
if (!indexSettings.Exists)
{
var response = client.Indices.Create(Indices.Index("store"));
}
}
public static void CreateSeed()
{
int seedValue = 1;
int limitValue = 20000;
IList<stores> List = new List<stores>();
ConnectionSettings settings = new ConnectionSettings(new Uri("http://localhost:9200"));
settings.DefaultIndex("store");
ElasticClient esClient = new ElasticClient(settings);
var item = new store() { ID = seedValue, Title = "item" + seedValue.ToString(), IsPublished = true };
var response = esClient.IndexAsync(item, idx => idx.Index("store"));
}
/// <summary>
///
/// </summary>
public static void CreateMappings()
{
ConnectionSettings settings = new ConnectionSettings(new Uri("http://localhost:9200"));
settings.DefaultIndex("store");
ElasticClient esClient = new ElasticClient(settings);
esClient.Map<stores>(m =>
{
var putMappingDescriptor = m.Index(Indices.Index("store")).AutoMap();
return putMappingDescriptor;
});
}
This create a store index and can be retrieved. However, if I create another table of different name e.g. itemsstore the same way, the older one doesn't exist anywhere.
Why? How do I create a new second table?
Looking at the example given, it looks like this will
delete "store" index
check if the "store" index exists (it won't as it was just deleted)
create a "store" index
set the default index to use as the "store" index (which will be created on indexing the first document, if it doesn't exist)
index store types into a "store" index
create a mapping for the "store" index
In summary, the example looks like it only interacts with a "store" index.
A simple example to create two indices is
var client = new ElasticClient();
var createIndexResponse = await client.Indices.CreateAsync("store");
if (!createIndexResponse.IsValid)
{
// take some action e.g. logging, exception, etc.
// To keep the example simple, just throw an exception
throw new Exception(createIndexResponse.DebugInformation);
}
createIndexResponse = await client.Indices.CreateAsync("itemsstore");
if (!createIndexResponse.IsValid)
{
throw new Exception(createIndexResponse.DebugInformation);
}
The walkthrough on building a Nuget search web application may be useful. The different branches have walkthroughs for different version of the client. For example, the 7.x branch is for NEST 7.x, with 7.x-codecomplete showing the completed example. It'll demonstrate a number of Elasticsearch and search related concepts.

How to pass filtering parameters in PoweBI C# SDK

I'm generating Power BI embed token using C# SDK.
using (var client = new PowerBIClient(new Uri(apiUrl), tokenCredentials))
{
var workspaceId = groupId.ToString();
var report = await client.Reports.GetReportInGroupAsync(workspaceId, reportId);
var generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "view");
var tokenResponsex = await client.Reports.GenerateTokenAsync(workspaceId, reportId, generateTokenRequestParameters);
result.EmbedToken = tokenResponsex;
result.EmbedUrl = report.EmbedUrl;
result.Id = report.Id;
}
I need to pass a parameter for filtering. But couldn't find a straightforward way to do this.
How do I get this done?
You can use RLS in embedded reports implemented with app owns data scenario (a single master account for authentication) by passing EffectiveIdentity information when generating access token for this report with GenerateTokenInGroup.
To implement RLS in the report itself you need either to use USERPRINCIPALNAME() DAX function to filter the data, or define roles and filter the data based on them. If you implement it with roles, after publishing the report go to dataset's security settings and add users to the roles.
To generate the token by providing an effective identity and roles membership, use code like this:
var credentials = new TokenCredentials(accessToken, "Bearer");
using (var client = new PowerBIClient(new Uri("https://api.powerbi.com"), credentials))
{
var datasets = new List<string>() { datasetId }; // Dataset's GUID as a string
var roles = new List<string>();
roles.Add('ROLE1');
roles.Add('ROLE2');
roles.Add('ROLE3');
var effectiveIdentity = new EffectiveIdentity('user#example.com', datasets, roles);
var r = new GenerateTokenRequest("view", effectiveIdentity);
var token = client.Reports.GenerateTokenInGroup(groupId, reportId, r).Token;
}

How to call google.apis.dialogflow.v2 in C#

I am new to Google APIs. I want to know how to call Google Dialogflow API in C# to get intent form the input text. But I can't find any example to call Dialogflow using C#.
Please provide some example to call Dialogflow from C#.
If I understand your question correctly you want to call the DialogFlow API from within a C# application (rather than writing fulfillment endpoint(s) that are called from DialogFlow. If that's the case here's a sample for making that call:
using Google.Cloud.Dialogflow.V2;
...
...
var query = new QueryInput
{
Text = new TextInput
{
Text = "Something you want to ask a DF agent",
LanguageCode = "en-us"
}
};
var sessionId = "SomeUniqueId";
var agent = "MyAgentName";
var creds = GoogleCredential.FromJson("{ json google credentials file)");
var channel = new Grpc.Core.Channel(SessionsClient.DefaultEndpoint.Host,
creds.ToChannelCredentials());
var client = SessionsClient.Create(channel);
var dialogFlow = client.DetectIntent(
new SessionName(agent, sessionId),
query
);
channel.ShutdownAsync();
In an earlier version of the DialogFlowAPI I was running into file locking issues when trying to re-deploy a web api project which the channel.ShutDownAsync() seemed to solve. I think this has been fixed in a recent release.
This is the simplest version of a DF request I've used. There is a more complicated version that passes in an input context in this post:
Making DialogFlow v2 DetectIntent Calls w/ C# (including input context)
(Nitpicking: I assume you know DialogFlow will call your code as specified/registered in the action at DialogFlow? So your code can only respond to DialogFlow, and not call it.)
Short answer/redirect:
Don't use Google.Apis.Dialogflow.v2 (with GoogleCloudDialogflowV2WebhookRequest and GoogleCloudDialogflowV2WebhookResponse) but use Google.Cloud.Dialogflow.v2 (with WebhookRequest and WebhookResponse) - see this eTag-error. I will also mention some other alternatives underneath.
Google.Cloud.Dialogflow.v2
Using Google.Cloud.Dialogflow.v2 NuGet (Edit: FWIW: this code was written for the beta-preview):
[HttpPost]
public dynamic PostWithCloudResponse([FromBody] WebhookRequest dialogflowRequest)
{
var intentName = dialogflowRequest.QueryResult.Intent.DisplayName;
var actualQuestion = dialogflowRequest.QueryResult.QueryText;
var testAnswer = $"Dialogflow Request for intent '{intentName}' and question '{actualQuestion}'";
var dialogflowResponse = new WebhookResponse
{
FulfillmentText = testAnswer,
FulfillmentMessages =
{ new Intent.Types.Message
{ SimpleResponses = new Intent.Types.Message.Types.SimpleResponses
{ SimpleResponses_ =
{ new Intent.Types.Message.Types.SimpleResponse
{
DisplayText = testAnswer,
TextToSpeech = testAnswer,
//Ssml = $"<speak>{testAnswer}</speak>"
}
}
}
}
}
};
var jsonResponse = dialogflowResponse.ToString();
return new ContentResult { Content = jsonResponse, ContentType = "application/json" }; ;
}
Edit: It turns out that the model binding may not bind all properties from the 'ProtoBuf-json' correctly (e.g. WebhookRequest.outputContexts[N].parameters),
so one should probably use the Google.Protobuf.JsonParser (e.g. see this documentation).
This parser may trip over unknown fields, so one probably also wants to ignore that. So now I use this code (I may one day make the generic method more generic and thus useful, by making HttpContext.Request.InputStream a parameter):
public ActionResult PostWithCloudResponse()
{
var dialogflowRequest = ParseProtobufRequest<WebhookRequest>();
...
var jsonResponse = dialogflowResponse.ToString();
return new ContentResult { Content = jsonResponse, ContentType = "application/json" }; ;
}
private T ParseProtobufRequest<T>() where T : Google.Protobuf.IMessage, new()
{
// parse ProtoBuf (not 'normal' json) with unknown fields, else it may not bind ProtoBuf correctly
// https://github.com/googleapis/google-cloud-dotnet/issues/2425 "ask the Protobuf code to parse the result"
string requestBody;
using (var reader = new StreamReader(HttpContext.Request.InputStream))
{
requestBody = reader.ReadToEnd();
}
var parser = new Google.Protobuf.JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true));
var typedRequest = parser.Parse<T>(requestBody);
return typedRequest;
}
BTW: This 'ProtoBuf-json' is also the reason to use WebhookResponse.ToString() which in turn uses Google.Protobuf.JsonFormatter.ToDiagnosticString.
Microsoft's BotBuilder
Microsoft's BotBuilder packages and Visual Studio template.
I havent't used it yet, but expect approximately the same code?
Hand written proprietary code
A simple example of incoming request code (called an NLU-Response by Google) is provided by Madoka Chiyoda (Chomado) at Github. The incoming call is simply parsed to her DialogFlowResponseModel:
public static async Task<HttpResponseMessage> Run([...]HttpRequestMessage req, [...]CloudBlockBlob mp3Out, TraceWriter log)
...
var data = await req.Content.ReadAsAsync<Models.DialogFlowResponseModel>();
Gactions
If you plan to work without DialogFlow later on, please note that the interface for Gactions differs significantly from the interface with DialogFlow.
The json-parameters and return-values have some overlap, but nothing gaining you any programming time (probably loosing some time by starting 'over').
However, starting with DialogFlow may gain you some quick dialog-experience (e.g. question & answer design/prototyping).
And the DialogFlow-API does have a NuGet package, where the Gactions-interface does not have a NuGet-package just yet.

run a primitive mycouch query

I'm new to couchdb and mycouch. I'm trying to implement a very simple query, I just want to get the results of a view and save it into my DTO class.
My couchdb query works, when I query it manually via HTTP:
http://localhost:5984/mydb/_design/tshirts/_view/getAllTshirts
However, when I try running it from my app using mycouch, I can't get to run it. My current query:
using MyCouch.Requests;
using MyCouch.Responses;
// (...)
using (var client = new Client("http://localhost:5984/samples")) {
var query = new QueryViewRequest("getAllTshirts");
ViewQueryResponse<TShirt[]> result = await client.Views.QueryAsync<TShirt[]>(query);
Console.WriteLine (result);
}
For some reason, it won't find the Client class. I found an example where Client is used on github, as you can see, I'm using all the MyCouch related namespaces as in the example.
I also tried using MyCouchStore instead:
using (var store = new MyCouchStore("http://localhost:5984/", "samples")) {
var query = new QueryViewRequest("getAllTshirts");
ViewQueryResponse<TShirt[]> result = await store.Views.QueryAsync<TShirt[]>(query);
Console.WriteLine (result);
}
However, the store doesn't contain any property named Views.
Any ideas how to query my view using MyCouch?
This is what I do, with the MyCouchStore
using (var store = new MyCouchStore("http://user:password#localhost:5984", "samples")) {
var query = new Query("tshirts", "getAllTshirts");
var rows = store.QueryAsync<TShirt>(query).Result;
}
Apparantely, the documentation was not up to date. The constructor requires now 2 arguments, the second being an optional bootstrapper. This worked for me:
var client = new Client("http://localhost:5984/samples", null)

Categories