ElasticSearch-NEST Query String Phrase - elasticsearch

I have a problem with ElasticSearch query phrases.
My index document is;
var person = new Person
{
Id = "4",
Firstname = "ali ahmet",
Lastname = "yazıcı"
};
var index = client.Index(person, x => x.Index("personindex"));
My search phrase is;
var result = client.Search<Person>(s => s
.From(0)
.Size(10)
.Query(q => q
.SimpleQueryString(qs => qs
.OnFields(new[]{"firstname","lastname"})
.Query("\"ali ah*\"")
)
)
);
Result document is empty. But when i change my phrase to
.Query("\"ali ahmet\"")
result is coming. Why return empty result from
.Query("\"ali ah*\"")
this phrase.
EDIT
Person Class
public class Person
{
public string Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
Index mapping
var response = client.CreateIndex("personindex", c => c
.AddMapping<Person>(m => m.MapFromAttributes())

From documentation for simple query string:
" wraps a number of tokens to signify a phrase for searching
When you are searching for .Query("\"ali ah*\"") actually it looks for phrase ali ah*, but * is not treated as wildcard character.
Chnage your NEST query to:
var result = client.Search<Person>(s => s
.Explain()
.From(0)
.Size(10)
.Query(q => q
.QueryString(qs => qs
.OnFields(new[] {"firstname", "lastname"})
.Query("ali ah*")
)
));
Hope it helps.

var result = client.Search<Person>(s => s
.Explain()
.From(0)
.Size(10)
.Query(q => q
.Match(qs => qs
.OnFields(new[] {"firstname", "lastname"})
.Query("ali ah*")
.MinimumShouldMatch(100)
)
));

Related

Searching ElasticSearch with dynamic field names

I have a method to search in C# as follows:
public void Search(string data)
{
var searchResponse = client.Search<Products>(s => s
.From(0)
.Size(100)
.Query(q => q
.Match(m => m
.Field( f => f.ProductName)
.Query(data))));
int cnt = searchResponse.Documents.Count;
}
This returned 5 documents, which is valid.
But, I wanted to modify the above method as follows, so that I can pass the field to be searched dynamically.
public void Search(string data,string fieldName)
{
var searchResponse = client.Search<Products>(s => s
.From(0)
.Size(100)
.Query(q => q
.Match(m => m
.Field(fieldName)
.Query(data))));
int cnt = searchResponse.Documents.Count;
}
The above code does return any error, but return 0 documents.
Here I am passing ProductName to fieldName parameter. I know this will not work. I just want tell my intention here.
Field Infering
You can pass below to your query
var fieldString = new Field("fieldName");
var fieldString = new Field("fieldName");
var searchResponse = _elasticClient.Search<AllOpportunitySearchResult>(s => s
.From(0)
.Size(100)
.Query(q => q
.Match(m => m
.Field(fieldString)
.Query(data)
)
)
);

Not understanding the behavior of _id on ES

I have documents in a Mongo database, they use the _id field as an index.
I use Monstache to sync ES with Mongo's op log, so the documents in ES have the same _id field.
When searching a specific document, Kibana shows:
Tags:
tag1 testtag CreatedOn:
October 26th 2018, 14:25:57.053
_id:
FRaqDPIzWcVI2dl-oA9uUFHLVFQk8qIqqhySWSkM7Ds
_type:
testobject
_index:
test.object
_score:
0
but then a query with Nest, returns this in the Documents array:
_id = 0d5aa177-3066-4c6a-aaf5-9b887ae7297f
and when I look in the Hits array, I see:
Id = FRaqDPIzWcVI2dl-oA9uUFHLVFQk8qIqqhySWSkM7Ds
So in Documents, the _id is now an unrelated guid, but in Hits the _id is called Id and it has the right value.
Why is that, and is there a way to get the proper value for _id in Documents?
Edit: more info
This is the object; since it's shared by MongoDB and ES, it has attributes for both.
[Nest.ElasticsearchType, BsonIgnoreExtraElements]
public class TestObject
{
public string _id { get; set; }
public string OwnerId { get; set; }
public Flags Flags { get; set; }
[Nest.Text, BsonIgnoreIfDefault] public string Title { get; set; }
[Nest.Text] public string Tags { get; set; }
[Nest.Ignore] public string Hash { get; set; }
[Nest.Ignore, BsonIgnoreIfDefault] public string Link { get; set; }
}
This is the code creating the index:
private static void InitializeElasticSearch(string ConnectionString)
{
var Settings = new ConnectionSettings(new Uri(ConnectionString))
.DefaultIndex(_IndexName)
.DefaultFieldNameInferrer(_ => _)
.DefaultMappingFor<TestObject>(_ => _.Ignore(I => I._id));
_ElasticClient = new ElasticClient(Settings);
if (!_ElasticClient.IndexExists(_IndexName).Exists)
{
// create the index
var CreateIndexResponse = _ElasticClient.CreateIndex(_IndexName, C => C
.Settings(S => S
.Analysis(A => A
.CharFilters(Cf => Cf
.Mapping("expressions", E => E
.Mappings(TextLists.Expressions)
)
)
.TokenFilters(Tf => Tf
.Synonym("synonyms", Sy => Sy
.Synonyms(TextLists.Synonyms)
.Tokenizer("whitespace")
)
)
.Analyzers(An => An
.Custom("index", Ca => Ca
.CharFilters("expressions")
.Tokenizer("standard")
.Filters("standard", "synonyms", "stop")
)
)
)
)
.Mappings(M => M
.Map<TestObject>(Mm => Mm
.AutoMap()
.Properties(P => P
.Text(T => T
.Name(N => N.Title)
.Analyzer("index")
)
.Text(T => T
.Name(N => N.Tags)
.Analyzer("index")
)
)
)
)
);
Then, the query code:
var R = await _ElasticClient.SearchAsync<TestObject>(Sr => Sr
.Query(Q =>
{
// do we query 'all' ?
if (Terms == "*") return Q.MatchAll();
// or do we have a general query
return Q
.MultiMatch(Fu => Fu
.Fields(F => F
.Field(Ff => Ff.Tags)
.Field(Ff => Ff.Title)
)
.Query(Terms)
.Fuzziness(Fuzziness.EditDistance(2))
);
})
.Take(_MaxObjectReturned)
);

Does Elasticsearch Nest support Update By Query

I want to use the UpdateByQuery method on the high level client but can't find any documentation for Nest. They have great documentation if I wanted to make a CURL request but nothing for NEST. https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
If anyone has and example of them using it or can share documentation they have found that would be awesome!
Update By Query API is supported in NEST. Here's an example adapted from the integration tests. NEST Documentation for Index and Update APIs is planned :)
private static void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultMappingFor<Test>(m => m
.IndexName("tests")
.TypeName("test")
);
var client = new ElasticClient(settings);
var index = IndexName.From<Test>();
if (client.IndexExists(index).Exists)
client.DeleteIndex(index);
client.CreateIndex(index, c => c
.Mappings(m => m
.Map<Test>(map => map
.Properties(props => props
.Text(s => s.Name(p => p.Text))
.Keyword(s => s.Name(p => p.Flag))
)
)
)
);
client.Bulk(b => b
.IndexMany(new[] {
new Test { Text = "words words", Flag = "bar" },
new Test { Text = "words words", Flag = "foo" }
})
.Refresh(Refresh.WaitFor)
);
client.Count<Test>(s => s
.Query(q => q
.Match(m => m
.Field(p => p.Flag)
.Query("foo")
)
)
);
client.UpdateByQuery<Test>(u => u
.Query(q => q
.Term(f => f.Flag, "bar")
)
.Script("ctx._source.flag = 'foo'")
.Conflicts(Conflicts.Proceed)
.Refresh(true)
);
client.Count<Test>(s => s
.Query(q => q
.Match(m => m
.Field(p => p.Flag)
.Query("foo")
)
)
);
}
public class Test
{
public string Text { get; set; }
public string Flag { get; set; }
}
Observe that the count from the first Count API call is 1, and on the second Count API call after the Update By Query API call, it's 2.

Combining queries using bool in Nest Elasticsearch

I need to get the documents from ES using NEST client with multiple And/OR conditions on two fields.
My query is as:
SELECT * FROM Document WHERE (Year!=2012 && Year!=2013 ) AND (Format=".pdf" || Format=".prt" || Format=".jpeg")
below is my code:
var qc = new List<QueryContainer>();
foreach (var year in years)// years is the list of years that must not be included
{
qc.Add(Query<Document>.Match(m => m.OnField(p => p.Year).Query(year)));
}
var qF = new List<QueryContainer>();
foreach (var format in txtDocs)// txtDocs is the list of formats that should be included if available
{
qF.Add(Query<Document>.Match(m => m.OnField(p => p.Format).Query(format)));
}
var searchResults = client.Search<Document>(s => s.Index(defaultIndex).From(0).Size(50).
Query(
f => f.Bool(
b => b
.MustNot(qc.ToArray()).Should(qF.ToArray()))));
When I try this code it works for the years that must not appear in the results but for the formats that should be selected by user, it doesn't show those selected formats although they are available.
I also used "must" instead of "should", but then it does not retrieve anything at all.
Has anyone had such a similar problem?
public class Test
{
public int Year { get; set; }
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed)]
public string Format { get; set; }
}
var searchResponse = client.Search<Test>(s => s.Query(q => q
.Bool(b => b
.MustNot(
m => m.Term(t => t.OnField(f => f.Year).Value(2012)),
m => m.Term(t => t.OnField(f => f.Year).Value(2013))
)
.Should(
should => should.Term(t => t.OnField(f => f.Format).Value(".pdf")),
should => should.Term(t => t.OnField(f => f.Format).Value(".prt")),
should => should.Term(t => t.OnField(f => f.Format).Value(".jpeg"))
)
)));
Hope it helps.
Here is the code for making a dynamic query:
QueryContainer qYear=null;
foreach (var year in years)
{
qYear |= new TermQuery() { Field = "year", Value = year };
}
QueryContainer qDoc=null;
foreach (var format in txtDocs)
{
qDoc|=new TermQuery() {Field="format", Value= format};
}
var searchResults = client.Search<Document>(s => s.Index(defaultIndex).From(0).Size(50).
Query(q => q.Bool(b => b.Should(qDoc).MustNot(qYear))));

Elasticsearch NEST client creating multi-field fields with completion

I am trying to create some completion suggesters on some of my fields. My document class looks like this:
[ElasticType(Name = "rawfiles", IdProperty = "guid")]
public class RAW
{
[ElasticProperty(OmitNorms = true, Index = FieldIndexOption.NotAnalyzed, Type = FieldType.String, Store = true)]
public string guid { get; set; }
[ElasticProperty(OmitNorms = true, Index = FieldIndexOption.Analyzed, Type = FieldType.String, Store = true, IndexAnalyzer = "def_analyzer", SearchAnalyzer = "def_analyzer_search", AddSortField = true)]
public string filename { get; set; }
[ElasticProperty(OmitNorms = true, Index = FieldIndexOption.Analyzed, Type = FieldType.String, Store = true, IndexAnalyzer = "def_analyzer", SearchAnalyzer = "def_analyzer_search")]
public List<string> tags { get { return new List<string>(); } }
}
And here is how I am trying to create the completion fields
public bool CreateMapping(ElasticClient client, string indexName)
{
IIndicesResponse result = null;
try
{
result = client.Map<RAW>(
c => c.Index(indexName)
.MapFromAttributes()
.AllField(f => f.Enabled(false))
.SourceField(s => s.Enabled())
.Properties(p => p
.Completion(s => s.Name(n => n.tags.Suffix("comp"))
.IndexAnalyzer("standard")
.SearchAnalyzer("standard")
.MaxInputLength(20)
.Payloads()
.PreservePositionIncrements()
.PreserveSeparators())
.Completion(s2 => s2.Name(n=>n.filename.Suffix("comp"))
.IndexAnalyzer("standard")
.SearchAnalyzer("standard")
.MaxInputLength(20)
.Payloads()
.PreservePositionIncrements()
.PreserveSeparators())
)
);
}
catch (Exception)
{
}
return result != null && result.Acknowledged;
}
My problem is that this is only creating a single completion field named "comp". I was under the impression that this will create two completion fields, one named filename.comp and the other named tags.comp.
I then tried the answer on this SO question but this complicated the matter even worse as now my two fields were mapped as a completion field only.
Just to be clear, I want to create a multi-field (field) that has a data, sort and completion fileds. Much like the one in this example
This is how you can reproduce auto-complete example from attached by you article.
My simple class(we are going to implement auto-complete on Name property)
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
}
To create multi field mapping in NEST we have to define mapping in such manner:
var indicesOperationResponse = client.CreateIndex(descriptor => descriptor
.Index(indexName)
.AddMapping<Document>(m => m
.Properties(p => p.MultiField(mf => mf
.Name(n => n.Name)
.Fields(f => f
.String(s => s.Name(n => n.Name).Index(FieldIndexOption.Analyzed))
.String(s => s.Name(n => n.Name.Suffix("sortable")).Index(FieldIndexOption.NotAnalyzed))
.String(s => s.Name(n => n.Name.Suffix("autocomplete")).IndexAnalyzer("shingle_analyzer"))))))
.Analysis(a => a
.Analyzers(b => b.Add("shingle_analyzer", new CustomAnalyzer
{
Tokenizer = "standard",
Filter = new List<string> {"lowercase", "shingle_filter"}
}))
.TokenFilters(b => b.Add("shingle_filter", new ShingleTokenFilter
{
MinShingleSize = 2,
MaxShingleSize = 5
}))));
Let's index some documents:
client.Index(new Document {Id = 1, Name = "Tremors"});
client.Index(new Document { Id = 2, Name = "Tremors 2: Aftershocks" });
client.Index(new Document { Id = 3, Name = "Tremors 3: Back to Perfection" });
client.Index(new Document { Id = 4, Name = "Tremors 4: The Legend Begins" });
client.Index(new Document { Id = 5, Name = "True Blood" });
client.Index(new Document { Id = 6, Name = "Tron" });
client.Index(new Document { Id = 7, Name = "True Grit" });
client.Index(new Document { Id = 8, Name = "Land Before Time" });
client.Index(new Document { Id = 9, Name = "The Shining" });
client.Index(new Document { Id = 10, Name = "Good Burger" });
client.Refresh();
Now, we are ready to write prefix query :)
var searchResponse = client.Search<Document>(s => s
.Query(q => q
.Prefix("name.autocomplete", "tr"))
.SortAscending(sort => sort.Name.Suffix("sortable")));
This query will get us
Tremors 2: Aftershocks
Tremors 3: Back to Perfection
Tremors 4: The Legend Begins
Tron
True Blood
True Grit
Hope this will help you.
Recently, guys from NEST prepared great tutorial about NEST and elasticsearch. There is a part about suggestions, it should be really useful for you.

Resources