Why does my Nest method in controller create an index? - c#

i am using net 6.0.1 with asp.net mvc.
var node = new Uri("http://localhost:9200");
var setting = new ConnectionSettings(node);
var client = new ElasticClient(setting);
var news = new News
{
NewsTitle = "TestTitle"
};
client.Index(news, idx => idx.Index("NewsTitle"));
var response = client.Get<News>(1, idx => idx.Index("NewsTitle"));
ElasticSearch is installed and running but when I try to run these lines of code then it does nothing. No index is created.

There's a few things to consider
client.Index(news, idx => idx.Index("NewsTitle")); doesn't check the response of the index document request.
It's a good idea to check the response to see if it succeeded - NEST
has a convenient .IsValid property on all responses that has
semantics for considering if the response is valid
var response = client.Get<News>(1, idx => idx.Index("NewsTitle")); attempts to get a document from the "NewsTitle" index with an id of 1, but the document just indexed does not have an id, so Elasticsearch will generate a random one when it indexes the document.
NEST has a convention where if the POCO to index has an Id property, it will use this as the id of the document. So, if News class is modified to contain an int Id property, and the instance indexed is assigned an Id property value of 1, the get request will return the indexed document.

Related

Elastic search (using NEST) returns 0 results on MatchAll()

I am using following code to index document and test results using NEST in .net core application.
But it is not returning any record.
So either I am doing wrong indexing or I have query problem.
Very new to elastic search. So don't know what is wrong with following code as I am trying to index text of text file and searching it for testing.
private static void Index()
{
var settings = new ConnectionSettings().DefaultIndex("ProjectDocuments");
var client = new ElasticClient(settings);
//First, you need to make the routing required when you are creating your index, like this:
client.CreateIndex("ProjectDocuments", d => d
.Mappings(mapping => mapping
.Map<Document>(map => map
.RoutingField(routing => routing
.Required(true))
.AutoMap())
));
Routing routingFromInt = 1;
Document document = new Document()
{
Id = 1,
Content = "Some Text File Text"
};
IIndexResponse result = client.Index<Document>(document, selector => selector
.Id(1)
.Routing(routingFromInt));
//TODO: Following returns 0. so might be issue with indexing itself.
ISearchResponse<Document> searchResponse = client.Search<Document>(query => query.Query(q => q.MatchAll()).Routing(routingFromInt));
var documents = searchResponse.Documents;
}
Issue was with default index name. Index name with Uppercase is not supported in elastic search.
so "ProjectDocuments" was causing issue. Changed it to "project_documents" and started working.

Bulk collection inside the right Index path in ElasticSearch using NEST in a .NET Core application

I am trying to bulk a collection of elements inside an index of ElasticSearch using NEST inside a .NET Core application.
Currently what I have is working, and the elements are saved, but Is not saved where I try to do
My client creation:
protected ElasticClient GetClient()
{
var node = new Uri("http://localhost:9200/");
var settings = new ConnectionSettings(node)
.DefaultIndex("TestIndex")
.PrettyJson(true);
return new ElasticClient(settings);
}
Here is how I create the descriptor for bulk all the data
protected BulkDescriptor GenerateBulkDescriptor<T>(IEnumerable<T> elements, string indexName) where T: class, IIndexable
{
var bulkIndexer = new BulkDescriptor();
foreach (var element in elements)
bulkIndexer.Index<T>(i => i
.Document(element)
.Id(element.Id)
.Index(indexName));
return bulkIndexer;
}
Finally, once I have this, here is how I index the data
var descriptor = GenerateBulkDescriptor(indexedElements, "indexed_elements");
var response = GetClient().Bulk(descriptor);
But, If I see how It's stored in the Elastic index using this, that is what I have:
How can I know if is created under TestIndex index? Because as far as I can see, there is just one index created
Thank you a lot in advance
When defining the index operations on the BulkDescriptor, you are explicitly setting the index to use for each operation
foreach (var element in elements)
bulkIndexer.Index<T>(i => i
.Document(element)
.Id(element.Id)
.Index(indexName));
where indexName is "indexed_elements". This is why all documents are indexed into this index and you do not see any in "TestIndex".
The Bulk API allows multiple operations to be defined, which may include indexing documents into different indices. When the index is specified directly on an operation, that will be the index used. If all index operations on a Bulk API call are to take place against the same index, you can omit the index on each operation and instead, specify the index to use on the Bulk API call directly
var defaultIndex = "default_index";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex);
var client = new ElasticClient(settings);
var people = new []
{
new Person { Id = 1, Name = "Paul" },
new Person { Id = 2, Name = "John" },
new Person { Id = 3, Name = "George" },
new Person { Id = 4, Name = "Ringo" },
};
var bulkResponse = client.Bulk(b => b
.Index("people")
.IndexMany(people)
);
which sends the following request
POST http://localhost:9200/people/_bulk
{"index":{"_id":"1","_type":"person"}}
{"id":1,"name":"Paul"}
{"index":{"_id":"2","_type":"person"}}
{"id":2,"name":"John"}
{"index":{"_id":"3","_type":"person"}}
{"id":3,"name":"George"}
{"index":{"_id":"4","_type":"person"}}
{"id":4,"name":"Ringo"}
Note that the URI is /people/bulk and that each JSON object representing an operation does not contain an "_index".
If you omit the .Index() on Bulk API call, it will use the DefaultIndex configured on ConnectionSettings:
var bulkResponse = client.Bulk(b => b
.IndexMany(people)
);
which yields
POST http://localhost:9200/_bulk
{"index":{"_id":"1","_index":"default_index","_type":"person"}}
{"id":1,"name":"Paul"}
{"index":{"_id":"2","_index":"default_index","_type":"person"}}
{"id":2,"name":"John"}
{"index":{"_id":"3","_index":"default_index","_type":"person"}}
{"id":3,"name":"George"}
{"index":{"_id":"4","_index":"default_index","_type":"person"}}
{"id":4,"name":"Ringo"}
You can also specify a default index to use for a given POCO type on ConnectionSettings with DefaultMappingFor<T>(), where T is your POCO type.
After som tests and attemps, I have found a solution.
First of all, it was a problem with the index configured, once I set it in lower case, the index was working fine indexing data inside.
Then, I had the problem of index data in a specific "path" inside the same index, finalyy I found the Type solution from NEST, taking also advantage of the DefaultMappingFor suggested by Russ in the previous answer.
Client definition:
var node = new Uri(_elasticSearchConfiguration.Node);
var settings = new ConnectionSettings(node)
.DefaultMappingFor<IndexedElement>(m => m
.IndexName(_elasticSearchConfiguration.Index)
.TypeName(nameof(IndexedElement).ToLower()))
.PrettyJson(true)
.DisableDirectStreaming();
var client = new ElasticClient(settings);
Then, the BulkDescriptior creation:
var bulkIndexer = new BulkDescriptor();
foreach (var element in elements)
bulkIndexer.Index<IndexedElement>(i => i
.Document(element)
.Type(nameof(IndexedElement).ToLower()))
.Id(element.Id)
);
And finally, data bulk:
client.Bulk(bulkIndexer);
Now, If I perform a call to the index, I can see this
{
"testindex": {
"aliases": {},
"mappings": {
"indexedelement": {
[...]
}
Thank you Russ for your help and for who have had a look to the post.
UPDATE
Finally, it seems that the unique problem was regarding the default index, that it must be lowercase, so, specify the type with the name of the POCO itself is not neccesary, like #RussCam has truly detected in comments above. After changing thedefault index to lowercase, all the different possibilities worked fine.
Thank you all again

How to delete object from Cosmos DB without knowing the partition key value?

If I don't have the partition key value, how do I delete a document?
I have the id of the document and the property name of the partition key (in my case: type).
I tried:
var docLink = UriFactory.CreateDocumentUri(databaseName, collectionName, documentId);
var resp = client.DeleteDocumentAsync(docLink).Result;
Got error: PartitionKey value must be supplied for this operation.
Example document:
{
"id": "AB12CD",
"type": "Company",
"Address": "123 Test Street"
}
I tried to get the partition key value for the specific document id
Using C# code...
I tried to read the document by its id and then pull the type value, but I got error: InvalidOperationException: PartitionKey value must be supplied for this operation. for using the following code.
var response = client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseName, collectionName, id)).Result;
I also tried a Cross Partition Query by trying:
FeedOptions queryOptions = new FeedOptions { MaxItemCount = 10 };
queryOptions.EnableCrossPartitionQuery = true;
var queryString = "SELECT * FROM c WHERE c.id= '" + id + "'";
var QueryInSql = client.CreateDocumentQuery<JObject>(
documentCollectionUri,,
queryOptions).AsDocumentQuery();
var result = QueryInSql.ExecuteNextAsync<JObject>().Result;
res = result.ToList<JObject>(); //if result has nothing, will be empty list
^ returns an empty list. If I check on the Azure Portal, I can see the document with that specific id does exist in the db.
Just refer to this doc, you will find the answer.
In C# code, please use Undefined.Value and everything will be fine.
client.DeleteDocumentAsync(
UriFactory.CreateDocumentUri(DbName, CollectionName, id),
new RequestOptions() { PartitionKey = new PartitionKey(Undefined.Value) });
Hope it helps you.
Update Answer:
Maybe I misunderstanding your requirement yesterday, let me make a new clarification here.
Firstly, you collection is partitioned by partition field: type.
1.If you do not know the value of partition key and the document does have a partition field ,please use EnableCrossPartitionQuery property.
FeedOptions queryOptions = new FeedOptions
{
MaxItemCount = 10,
EnableCrossPartitionQuery = true
};
var id = '1';
var queryString = "SELECT * FROM c WHERE c.id= '" + id + "'";
var QueryInSql = client.CreateDocumentQuery<JObject>(
uri,
queryString,
queryOptions).AsDocumentQuery();
var result = QueryInSql.ExecuteNextAsync<JObject>().Result;
var res = result.ToList<JObject>(); //if result has nothing, will be empty list
Console.WriteLine("\nRead {0}", res[0]);
2.If you know the value of partition key and the document does have a partition field,please pass the partition key property in FeedOptions.
3.If you know the document does not have partition field in a partitioned collection,please use Undefined.Value.
In case somebody is coming here to delete_item using python sdk. If the item doesn't have a partition key defined
Pass empty dictionary to the partition_key param to delete the item.
cosmosContainerClient.delete_item(item['id'], partition_key={})

Nest - Point Alias to new index [duplicate]

I'm using C# .NET application with NEST to create an index.
I've created an elasticsearch index that customers can query called index_1. I then build another version of the index using a different instance of the application and call it index_1_temp.
What is the safest way for me to rename index_1_temp to index_1 then delete the original index_1?
I know ES has aliases but I'm not sure how to use them for this task
EDIT: The original index does not have an Alias associated with it.
I would recommend always using aliases in scenarios where you may create incrementally differing versions of an index, as may be the case when refining the model of signals within your search strategy.
You can add an alias at the point of creating an index
var client = new ElasticClient(connectionSettings);
var indices = new[] { "index-v1", "index-v2" };
var alias = "index-alias";
// delete index-v1 and index-v2 if they exist, to
// allow this example to be repeatable
foreach (var index in indices)
{
if (client.IndexExists(index).Exists)
{
client.DeleteIndex(index);
}
}
var createIndexResponse = client.CreateIndex(indices[0], c => c
.Aliases(a => a
.Alias(alias)
)
);
Then when you create a new index, you can remove the alias from current indices and add it to the new index. This alias swap operation is atomic
createIndexResponse = client.CreateIndex(indices[1]);
// wait for index-v2 to be operable
var clusterHealthResponse = client.ClusterHealth(c => c
.WaitForStatus(WaitForStatus.Yellow)
.Index(indices[1]));
// swap the alias
var bulkAliasResponse = client.Alias(ba => ba
.Add(add => add.Alias(alias).Index(indices[1]))
.Remove(remove => remove.Alias(alias).Index("*"))
);
// verify that the alias only exists on index-v2
var aliasResponse = client.GetAlias(a => a.Name(alias));
The output of the last response is
{
"index-v2" : {
"aliases" : {
"index-alias" : { }
}
}
}
When searching, the consumers would always use the alias. Since the alias points to a single index only, you can also use it to index new documents and update existing documents.

Getting the profileid for a userid

In our company we created a custom Issues app. Additionally to using this app in the web interface, we also want to be able to change the state of an issue (new, acknowledged, test, resolved, ...) automatically via git commit hooks. The basics are working fine (ie change state, add notes, ...), but we also want to change the responsibility for the current item to a specific user. In that special case, it's the creator if this item.
My first try was the following:
var appid = 1234; var itemid = 1;
var item = podio.ItemService.GetItemByAppItemId(appid, itemid);
var update = new Item {ItemId = item.ItemId};
var creator = item.CreatedBy.Id;
var resp = update.Field<ContactItemField>("responsibility");
resp.ContactIds = new List<int>{creator.Value};
//change some other fields as well
podio.ItemService.UpdateItem(update);
This throws an "Object not found" exception, because in the resp.ContactIds one must not set the UserId but the ProfileId.
I then tried to get the ProfileId of the item-creator via
podio.ContactService.GetUserContactField(creator.Value, "profile_id");
but this also throws an exception "(Authentication as app is not allowed for this method").
So how can I get an appropriate profile id for the user when I use authentication as app?
OK, I found a workaround for it, not sure, if this is possible for other scenarios, but it works for the current case.
Instead of using the C# interface for setting the ContactIds for the ContactItemField, I set the json values directly.
var appid = 1234; var itemid = 1;
var item = podio.ItemService.GetItemByAppItemId(appid, itemid);
var update = new Item {ItemId = item.ItemId};
var creator = item.CreatedBy.Id;
var resp = update.Field<ContactItemField>("responsibility");
resp.ContactIds = new List<int>(); // set to an empty list, so that resp.Values is initialized to an empty JArray
var u = new JObject { {"value", new JObject { {"type" , "user" }, {"id", creator } } } };
responsibleField.Values.Add(u); //add the new user to the Values of the field
//change some other fields as well
podio.ItemService.UpdateItem(update);
And if I set the value with type user I can use the known userid and the API on the server takes care of the lookup.

Categories