ElasticSearch with Nest: Partial search using multiple words using Query<>.Wildcard - elasticsearch

I have been pulling my hair out trying to configure and partial search ElasticSearch indexed data using Nest library version 5.3.1 (same version applies to its one of its dependencies; Elasticsearch.Net).
As per suggestions found online I used data attributes to specify analyzer type on some of the indexed properties as shown below:
public class Article
{
public int Id { get; set; }
[Completion(Analyzer = "standard", PreservePositionIncrements = true, PreserveSeparators = true)]
public string Title { get; set; }
public string Url { get; set; }
}
I have at least one record in the search index for type "Article" having title starting with "The greatest ....". Whenever I perform a partial search for a keyword "greatest" using code below, it works just fine returning matching search results.
MultiTermQueryRewrite multiqueryRewrite = null;
var searchQuery = Query<Article>.Wildcard(f => f.Title, "*greatest*", rewrite: multiqueryRewrite);
var client = ElasticsearchClient.GetClient<Article>();
return client.Search<Article>(s => s.Query(searchQuery));
But... if I try searching for "the greatest" keywords with any variation listed below, I don't get any results back.
var searchQuery = Query<Article>.Wildcard(f => f.Title, "*the greatest*", rewrite: multiqueryRewrite);
or
var searchQuery = Query<Article>.Wildcard(f => f.Title, "*the*greatest*", rewrite: multiqueryRewrite);
or even
var searchQuery = Query<Article>.Wildcard(f => f.Title, "*the?greatest*", rewrite: multiqueryRewrite);
I am new to the ElasticSearch product, so any help would be greatly appreciated.
Thanks in advance for your help.

As per documentation
Wild card Matches documents that have fields matching a wildcard expression (not analyzed).
Since title field is Analyzed, it gets tokenized before getting indexed. Some text say The Greatest will get tokenized and then converted into lower case (Behaviour Of Standard Analyzer). So it will be stored in reverse index as two tokens the and greatest.
When you search for *greatest*. It is searched as there is a token corresponding to that.
But when you search for * the greatest * , it is not found as there is no token which contains this text.
You can use Query String
var searchQuery = Query<Article>.QueryString(c => c
.Query("*the greatest*")
.DefaultField(p=>p.Title))
Hope this helps!!

The standard analyzer applied to the Title field produces lower case terms of your "The Greatest" Title in the following format [the, greatest]. You could consider using the Keyword Analyzer but please note you will have to deal with word casing.

Related

ElasticSearch / NEST 6 - Serialization of enums as strings in terms query

I've been trying to update to ES6 and NEST 6 and running into issues with NEST serializing of search requests - specifically serializing Terms queries where the underlying C# type is an enum.
I've got a Status enum mapped in my index as a Keyword, and correctly being stored in its string representation by using NEST.JsonNetSerializer and setting the contract json converter as per Elasticsearch / NEST 6 - storing enums as string
The issue comes when trying to search based on this Status enum. When I try to use a Terms query to specify multiple values, these values are being serialized as integers in the request and causing the search to find no results due to the type mismatch.
Interestingly the enum is serialized correctly as a string in a Term query, so I'm theorizing that the StringEnumConverter is being ignored in a scenario where it's having to serialize a collection of enums rather than a single enum.
Lets show it a little more clearly in code. Here's the enum and the (simplified) model used to define the index:
public enum CampaignStatus
{
Active = 0,
Sold = 1,
Withdrawn = 2
}
public class SalesCampaignSearchModel
{
[Keyword]
public Guid Id { get; set; }
[Keyword(DocValues = true)]
public CampaignStatus CampaignStatus { get; set; }
}
Here's a snippet of constructing the settings for the ElasticClient:
var pool = new SingleNodeConnectionPool(new Uri(nodeUri));
var connectionSettings = new ConnectionSettings(pool, (builtin, serializerSettings) =>
new JsonNetSerializer(builtin,
serializerSettings,
contractJsonConverters: new JsonConverter[]{new StringEnumConverter()}
)
)
.EnableHttpCompression();
Here's the Term query that correctly returns results:
var singleTermFilterQuery = new SearchDescriptor<SalesCampaignSearchModel>()
.Query(x => x.Term(y => y.Field(z => z.CampaignStatus).Value(CampaignStatus.Active)));
Generating the request:
{
"query": {
"term": {
"campaignStatus": {
"value": "Active"
}
}
}
}
Here's the Terms query that does not return results:
var termsFilterQuery = new SearchDescriptor<SalesCampaignSearchModel>()
.Query(x => x.Terms(y => y.Field(z => z.CampaignStatus).Terms(CampaignStatus.Active, CampaignStatus.Sold)));
Generating the request:
{
"query": {
"terms": {
"campaignStatus": [
0,
1
]
}
}
}
So far I've had a pretty good poke around at the options being presented by the JsonNetSerializer, tried a bunch of the available attributes (NEST.StringEnumAttribute, [JsonConverter(typeof(StringEnumConverter))] rather than using the global one on the client, having an explicit filter object with ItemConverterType set on the collection of CampaignStatuses, etc.) and the only thing that has had any success was a very brute-force .ToString() every time I need to query on an enum.
These are toy examples from a reasonably extensive codebase that I'm trying to migrate across to NEST 6, so what I'm wanting is to be able to specify global configuration somewhere rather than multiple developer teams needing to be mindful of this kind of eccentricity.
So yeah... I've been looking at this for a couple of days now. Good chances there's something silly I've missed. Otherwise I'm wondering if I need to be providing some JsonConverter with a contract that would match to an arbitrary collection of enums, and whether NEST and their tweaked Json.NET serializer should just be doing that kind of recursive resolution out of the box already.
Any help would be greatly appreciated, as I'm going a bit crazy with this one.

Lite DB not finding inner object query

I have two objects.
[DataContract]
public class Record
{
[DataMember]
public string Id { get; set; }
}
And this class:
public class BatteryStatus : Record
{
[DataMember]
public DateTime RetrieveTime { get; set; }
}
I'm using Lite DB as a local NoSQL option to query and save the data. I'm needing to find and delete the values based after some time. Here's my code doing so:
var col = db.GetCollection<BatteryStatus>(CollectionName);
var test = col.FindAll()
.Where(x => x.Id == status.Id).ToList();
var result = col.Find(Query.EQ("Id", status.Id.ToString())).ToList();
Test returns with the with the object, but the result value doesn't. Lite DB only uses the Query or the BSONId as a way to delete an object. I don't have a BSON id attached to it (it's a referenced definition so I can't change it).
How can I use the "Query" function in order to get a nested value so I can delete it?
Classes has properties, BSON documents has fields. By default, LiteDB convert all property names to same name in BSON document except _id field which is document identifier.
If you want query using Linq, you will use properties expressions. If you are using Query object class, you must use field name.
var result = col.FindById(123);
// or
var result = col.FindOne(x => x.Id == 123);
// or
var result = col.FindOne(Query.EQ("_id", 123));
Find using _id always returns 1 (or zero) document.
I figured out the problem with LiteDB, since I was using the property name of "Id", the BSON interpreted that as the "_id" of the JSON object, and merging their two values. I solve the issue by renaming the "Id" property to something else.

Include_In_Parent option for ElasticSearch and NEST library

I am using ElasticSearch and the NEST .Net library for implementing the Search functionality needed in our app. In my model, I have a type that contains Nested objects as per below.
[ElasticType(Name = "x")]
public class X
{
[ElasticProperty(IncludeInAll = false, Index = FieldIndexOption.NotAnalyzed)]
public string Id { get; set; }
[ElasticProperty(Type = FieldType.Nested)]
public List<Y> Ys { get; set; }
}
Any queries executed against X are actually executed against the List of Ys. I would like to highlight the hits in the nested objects and based on https://github.com/elasticsearch/elasticsearch/issues/5245 .
However, in order to use the proposed workaround, the include_in_parent option should be true for the nested object.
How can this option be enabled using the NEST library? Is there any ElasticProperty property (I haven’t found any obvious one) or some other way to do so?
Thank you
Apparently this can be done only by using fluent syntax. For the above case the code would be:
.AddMapping<X>(m => m
.Properties(p => p
.NestedObject<Y>(n => n
.Name("ys")
.IncludeInParent())

Search Query in RavenDB

I would like to know the Search Query for the below condition. I have created an index called MeetingEventIndex as below:
public class MeetingEventIndex : AbstractIndexCreationTask<mngtMeetingEvent>
{
public MeetingEventIndex ()
{
Map = docs => from d in docs select new {d.meetingroomid, d.text, d.details};
Index(x=>x.meetingroomid, FieldIndexing.Analyzed);
Index(x=>x.text, FieldIndexing.Analyzed);
Index(x=>x.details, FieldIndexing.Analyzed);
}
}
I am trying to create a search query as below "Search the term in text or details field and meetingroomid==123"
docsession.Query<mngtMeetingEvent, MeetingEventIndex>()
.Search(x=>x.text , search)
.Search(x=>x.details, search. options: SearchOptions.Or)
.Search(x=>x.meetingroomid, "123", option.SearchOptions.And)
.ToList()
But this is not returning any result.
Basically I am looking for ((searchterm in text field || searchterm in details field ) and mrcode in meetingroomid field).
Please help.
Your query is probably more easily expressed as LuceneQuery, instead:
docsession.Advanced..LuceneQuery<mngtMeetingEvent, MeetingEventIndex>()
.OpenSubClause()
.Search("text", search)
.OrElse()
.Search("details", search)
.CloseSubClause()
.AndAlso()
.WhereEquals("meetingroomid", "123")
.ToList();

Elastic Search NEST - Search returns no records when we mention Type name

Tried Code:-
public class Company
{
public long Number { get; set; }
public string Name{ get; set; }
}
My problem is : If I mention Type name within Search Tag I couldn't get any result.
1) Shows Result
var rowsList= client.Search(s => s.MatchAll()).Documents.ToList();
2) Returns 0 rows
var newr = client.Search<Company>(s => s.MatchAll()).Documents.ToList();
The typed search defaults to
/[inferred_index]/[inferred_typename]/_search
If you want to search through all indices and types you will have to explicit about it
client.Search<Company>(s=>s
.AllIndices()
.AllTypes()
.MatchAll()
)
this will do the search on /_search
You can control how nest infers indices and type names like so:
var settings = new ConnectionSettings(uri)
.SetDefaultIndex("my-default-index")
.MapDefaultTypeNames(d=>d
.Add(typeof(Company), "company-type")
)
MapDefaultTypeIndices(d=>d
.Add(typeof(Company), "company-index")
)
SetDefaultTypeNameInferrer(s=>s.ToLowerInvariant())
Now if you search for <Company> it will default to /company-index/company-type/_search
if you do a search for <Person> it will query on /my-default-index/person/_search.

Resources