Nest - Point Alias to new index [duplicate] - c#

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.

Related

C# MongoDB update definition from filled model

How i can combine type C# filled model and mongo language for create update request?
i try this but get types error:
var update = new ObjectUpdateDefinition<User>(user) & Builders<User>.Update.Inc(f => f.FindCount, 1);
FullCode:
var user = new Model
{
Email = email,
Language = language,
Password = password,
// +17 fields
};
// how i can convert all fields to set? and join with query Inc (like AllFieldToSet)?
var update = user.AllFieldToSet() & Builders<User>.Update.Inc(f => f.FindCount, 1);
Models.FindOneAndUpdate<Model>(
f => f.Email == email,
update,
new FindOneAndUpdateOptions<Model> {IsUpsert = true});
Firstly I would begin to ask why do you update so many fields at once, this can have a significant effect on performance and scalability, especially when your collection has indexes, replication and/or sharding involved. Whenever you have to update an indexed field it needs to update the index as well.
There are multiple solutions:
ReplaceOne:
Inc only increments the value, this can be done manually in the model: FindCount++
Manually build the update operator: Just manually build using the builder they provided
Write your own extension using reflection Personally I like type safety but you can use this extension that I wrote (you will just have to extend it and build in null checks, etc.)
public static class UpdateBuilderExtensions
{
public static UpdateDefinition<TDocument> SetAll<TDocument, TModel>(this UpdateDefinition<TDocument> updateBuilder, TModel value, params string[] excludeProperties)
{
foreach (var property in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => excludeProperties?.Contains(x.Name) == false))
updateBuilder = Builders<TDocument>.Update.Combine(updateBuilder.Set(property.Name, property.GetValue(value)));
return updateBuilder;
}
}
Usage of it:
You have to exclude properties that have been set already and you have to exclude the BsondId field (if you do not set it manually)
var update = Builders<User>.Update.Inc(x => x.FindCount, 1).SetAll(model, nameof(model.Id), nameof(model.FindCount));
Models.UpdateOne(x => x.Email== "some-email", update, new UpdateOptions
{
IsUpsert = true
});

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 Check Whether an Array (or List) Contains Another an Array (List) Using LINQ

I am trying to get the list of files (file is an entity) which has the selected services (service is another entity). The file can have many services.
I tried the following statement, but it does not give the correct results:
var _serviceTypes = viewModel.SelectedServiceTypes;
// _serviceTypes is an array of integers
var resultsTemp = repository.Files.Where(f => f.Services.Select(s => s.ServiceTypeID).Intersect(_serviceTypes).Any());
What am I missing?
EDIT:
_serviceTypes in an array of integers: {int[2]}
The files can have many services, each of which as one service type id (integer)
For instance, a file has two services in it: ambulance (service type id: 3) and hospitalization (service type id: 5). I want to get all the files which have both the services in it.
Some of the following operations should answer your question:
// requested IDs
var requestedIDs = new List<int>();
// the IDs from one file
var IDsInFile = new List<int>();
if (requestedIDs.Except(IDsInFile).Any())
{
// at least some requested IDs are not in the file
}
else
{
// all requested IDs are in the file
}
if (requestedIDs.Intersect(IDsInFile).Any())
{
// at least some requested IDs are in the file
}
else
{
// not a single requested ID is in the file
}
Since you want every file that contains all of the requested services, the correct query would be
var _serviceTypes = viewModel.SelectedServiceTypes;
// _serviceTypes is an array of integers
var resultsTemp = repository.Files.Where(f => !_serviceTypes.Except(f.Services.Select(s => s.ServiceTypeID)).Any());
You're checking if a single element exists in both lists, which is probably not what you had in mind. Try to check if the intersection is equal to the smaller list (assuming it's _serviceTypes, as that's not evident from your code):
var resultsTemp = repository.Files
.Where(f => f.Services.Select(s => s.ServiceTypeID)
.Intersect(_serviceTypes)
.OrderBy(x => x)
.SequenceEqual(_serviceTypes.OrderBy(x => x));

How to get query names that references the particular iteration path

With selected project name, I had loaded iteration paths. Now I need to get the query names that references the selected iteration path.
Code to load iteration paths passing project name:
private void LoadIterationPaths(string projectName)
{
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(_tfs.Uri);
var wiStore = tfs.GetService<WorkItemStore>();
var projCollections = wiStore.Projects;
var detailsOfTheSelectedProject = projCollections.Cast<Project>().Where(project => !String.IsNullOrEmpty(_selectedTeamProject.Name))
.FirstOrDefault(project => project.Name.Contains(_selectedTeamProject.Name));
var iterationPathsList = GetIterationPaths(detailsOfTheSelectedProject);
foreach (var iterationPath in iterationPathsList.Where(iterationPath => iterationPath.Contains(projectName)))
{
cmbIterationPath.Items.Add(iterationPath);
}
cmbIterationPath.Enabled = cmbIterationPath.Items.Count > 0;
}
Now, I need to get the list of Query names that references the selected iteration Path. Thanks.
Note: I am able to get all the query names in a project but that i don't need.
For that I used the below code
foreach (StoredQuery qi in detailsOfTheSelectedProject.StoredQueries)
{
cmbQueries.Items.Add(qi.Name);
}
Your code should looks like this
string selectedIterationPath = ...
foreach (StoredQuery qi in detailsOfTheSelectedProject.StoredQueries) {
if (qi.QueryText.Contains(selectedIterationPath) {
cmbQueries.Items.Add(qi.Name);
}
}
This is what me and Beytan Kurt suggested in the comments.
Instead of a dumb Contains, you should use a Regular Expression to account for false positives and negatives.

Categories