Creating Elasticsearch Index using NEST 5.x - elasticsearch

I am trying to create an index using NEST 5.x pre release version for Elasticsearch 5.x. I have samples from 2.x which shows how index can be created using ElasticClient.CreateIndex method. Below is my sample code.
ESnode = new Uri("http://localhost:9200");
Nodesettings = new ConnectionSettings(ESnode);
Client = new ElasticClient(Nodesettings);
However, when I am typing below, there is NO autocomplete available.
Client.CreateIndex( c => c.
I am able to successfully get the health of the node using below code.
var res = Client.ClusterHealth();
Console.WriteLine("Status:" + res.Status);
I am having a complex document mapping for which I have defined the class structure and intend to use Automap method. Hence I am trying to create the index programatically to avoid manually creating the index.
I tried using some very old version of NEST (1.x) and I am able to get the autocomplete for createIndex. But both v2.4x and 5.x did not provide the autocomplete. Is there a new way to create index? Please let me know.
Thanks

You need to supply a name to the index, in addition to the delegate that provides additional index creation options
var createIndexResponse = client.CreateIndex("index-name", c => c
.Settings(s => s
.NumberOfShards(1)
.NumberOfReplicas(0)
)
.Mappings(m => m
.Map<Conference>(d => d
.AutoMap()
)
)
);

Related

Lowercase Document Type during Nest search of Elasticsearch cluster

I'm searching a v5.5 elasticsearch cluster that's hosted using AWS's managed solution. I'm using a client to send search requests to the cluster but it's not finding any hits. I switched on the cluster lever logging and can see that the problem is that the type being searched is in lowercase (when the document types in the index are uppercase) so it can't match on any documents.
I'm passing a search descriptor object into the Nest client:
GetSearchDescriptor(SearchDescriptor<T> descriptor)
{
descriptor.Index(index)
.Type(documentType)
.Query(q => (q
.Bool(bq => bq
...
}
client.Search<T>(s => GetSearchDescriptor(s))
Where documentType is of type T e.g. Invoice.
When I look at the CloudWatch logs I can see that the request that's hitting the cluster is lowercase invoice (instead of uppercase Invoice). This doesn't match the document type hence doesn't show any results. When I do a kibana search using the exact json in the logs I get the correct results when using uppercase Invoice, but no results when using lowercase invoice.
Any ideas what could be going on here?
NEST's default inference for a type name from a POCO of type T, is to lowercase the typeof(T).Name value. You can easily change this behaviour for T or for all POCOs on ConnectionSettings
For all POCOs
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.DefaultTypeNameInferrer(type => type.Name.ToUpperInvariant());
var client = new ElasticClient(settings);
For only T e.g. Invoice
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.InferMappingFor<Invoice>(m => m
.TypeName("INVOICE")
);
var client = new ElasticClient(settings);
With the latter, you can also use InferMappingFor<T> to specify a default index name for T, and tell the client which property should be used to infer the id for the document.

How can I use X.PagedList with ElasticSearch Nest?

Background
I'm using ElasticSearch as the search engine for a new ASP.Net Core 2.1 website I'm working on. I'm using the Nest API to integrate with it. I want to use the X.PagedList to handle the paging for me.
I've used this in other ASP.Net Core projects and it's worked well querying data in MS SQL Server.
Code
ISearchResponse<Foo> searchResponse =
_elasticSearchClient.Search<Foo>(s => s
.Query(q => q
.Bool(b => b.Filter(distanceFilters))
)
.Source(src => src
.Includes(i => i
.Fields(
f => f.Field1,
f => f.Field2,
f => f.Field3
)
)
)
.From(options.From)
.Size(options.Size)
);
var hitsMD = searchResponse.HitsMetadata;
var results = hitsMD?.Hits.Select(s => new Hit()
{
Index = s.Index,
Id = s.Id,
Score = s.Score,
Job = s.Source
}
).ToPagedList(PageNumber, PageSize);
Issue
When I call .ToPagedList on the search results returned by ElasticSearch, it only shows one page of results.
The issue is that ElasticSearch has its own paging mechanism so it's only returning one page of hits.
I had the idea that because ElasticSearch passes back the total number of hits I could tell the PagedList how many items are in the list by setting the PagedList.TotalItemCount property. However, I can't do this as it's a private set.
I've tried removing the from and size but this returns 10 hits which is ElasticSearch's default size which they obviously put in place for performance reasons.
Question
How can I make use of the X.PagedList package whilst integrating into ElasticSearch using the Nest API?
You've basically got all the pieces here already. All you're missing is StaticPagedList<T>. Since paging is already being handled by Elasticsearch, you need to simply define a static paging setup, i.e.:
var pagedResults = new StaticPagedList<Foo>(results, PageNumber, PageSize, total);

Sharing index mapping configuration in NEST 2.3.3?

Upgrading Elastic & NEST search from 1.6.2 to 2.3.3.
We used be able to share the same PutMappingDescriptor between ElasticClient.CreateIndex() and ElasticClient.Map().
But in 2.3.3, the CreateIndex needs TypeMappingDescriptor and Map requires PutMappingDescriptor.
How do we share the same mapping configuration?
The official Nest developers answered this question in their github, linked here.
Basically, you don't use Func<PutMappingDescriptor<Project>, IPutMappingRequest> but PutMappingDescriptor<Project> directly. by newing up a PutMappingDescriptor<Project> and build up your fluent mapping from there.
Creating index expects ITypeMapping while updating index expects IPutMappingRequest which implements ITypeMapping. So you can satisfy both by using PutMappingDescriptor.
To create an index, use:
```
client.CreateIndex("projects", c => c
.Mappings(ms => ms
.Map(m => GetMapping())
)
);
```
where you ignore m passed in in the lambda and use the one you created. The reason why you can do that can be found in NEST's source code where it creates an empty TypeMappingDescriptor for your to further build upon:
public MappingsDescriptor Map<T>(Func<TypeMappingDescriptor<T>, ITypeMapping> selector) where T : class =>
Assign(typeof (T), selector?.Invoke(new TypeMappingDescriptor<T>()));
To update mapping, do:
client.Map(GetMapping());

Elasticsearch Nest not honoring index = not_indexed on POCO field?

i just found out through the documentation that we should be telling it to use the mapping attributes and manually creating the index before we index.
however, the documentation is not consistent with the newest version of the code. (pre release).
http://nest.azurewebsites.net/nest/indices/put-mapping.html
var response = this.ConnectedClient.Map<ElasticSearchProject>();
the call above in the new code takes 1 argument in the Map() method. the documentation is not requiring any arguments.
what should be contained in that method? it seems like there are many options, but i'm unclear on which ones to use.
Have a look at the Create Indices documentation. I think something like this will work for what you are trying to accomplish. Plus it will create the index and apply the mapping all in one call to your Elasticsearch instance.
client.CreateIndex("myindexname", c => c
.NumberOfReplicas(0)
.NumberOfShards(1)
.Settings(s=>s
.Add("merge.policy.merge_factor","10")
.Add("search.slowlog.threshold.fetch.warn", "1s")
)
.AddMapping<ElasticSearchProject>(m => m.MapFromAttributes())
.AddMapping<Person>(m => m.MapFromAttributes())
);
The .AddMapping<ElasticSearchProject>(m => m.MapFromAttributes()) line tells NEST to grab all of the Attribute settings on the via ElasticType and ElasticProperty on the ElasticSearchProject class and use those to create the mapping.

FOSElasticaBundle order query

I am integrating FOSElasticaBundle in my Symfony 2.3 project and I need to sort the results by their price property.
Here is my code:
$finder = $this->container->get('fos_elastica.finder.website.product');
$fieldTerms = new \Elastica\Query\Terms();
$fieldTerms->setTerms('taxon_ids', $taxon_ids_array);
$boolQuery->addMust($fieldTerms);
$resultSet = $finder->find($boolQuery);
How I can do this?
Thanks
Try create a \Elastica\Query object which also contains the sorting information, then send this to the finder:
$finder = $this->container->get('fos_elastica.finder.website.product');
$fieldTerms = new \Elastica\Query\Terms();
$fieldTerms->setTerms('taxon_ids', $taxon_ids_array);
$boolQuery->addMust($fieldTerms);
$finalQuery = new \Elastica\Query($boolQuery);
$finalQuery->setSort(array('price' => array('order' => 'asc')));
$resultSet = $finder->find($finalQuery);
Have a look at the elasticsearch docs on the sort parameter to see how to use it properly.
NOTE: \Elastica\Query is quite different to \Elastica\Query\AbstractQuery, the first encapsulates everything you could send to the _search API endpoint (facets, sorting, explain, etc...) The AbstractQuery represents a base type for each of the individual query types (range, fuzzy, terms, etc...).

Resources