I have these data in my elasticsearch with this structure.
How do I search firstName, middleName and surname from this array? Note: The NameDetails
array length is dynamic. Person A could have just one element of NameDetails
. Person B could have 3 elements of NameDetails
.
At the moment, I could only search by gender
. I am using NEST nuget C#. This is my query.
var response = await _elasticClient.SearchAsync<Person>(s => s
.Query(q => q
.Bool(b => b.Must(
mu => mu
.Match(m => m
.Field(f => f.Gender)
.Query(Gender)
)
)
)
)
);
In NEST, I tried with this code but return no result.
var response = _elasticClient.Search <Model.Entities.Split.Person.Person> (s => s
.Index("person")
.Query(q => q
.Match(m => m
.Field(f => f.NameDetails.Name[0].NameValue.FirstName)
.Query("Fawsu")
)
)
);
But if I directly run the DSL query at ElasticSearch with query below, it returns result.
GET /person/_search
{
"query": {
"match": {
"nameDetails.nameValue.firstName": {
"query": "Fawsu"
}
}
}
}
}
or
GET /person/_search
{
"query": {
"bool": {
"must": [
{
"fuzzy": {
"nameDetails.nameValue.surname": {
"value": "Pibba",
"fuzziness": "AUTO"
}
}
},
{
"fuzzy": {
"nameDetails.nameValue.firstName": {
"value": "Fawsu",
"fuzziness": "AUTO"
}
}
}
]
}
}
}
It's useful to see how this is mapped to a POCO
public class Person
{
public string Gender { get; set; }
public string ActiveStatus { get; set; }
public string Deceased { get; set; }
public List<Name> NameDetails { get; set; }
}
public class Name
{
public List<NameValue> NameValue { get; set; }
public string NameType { get; set; }
}
public class NameValue
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
(You might map some of these fields as types other than string, I'll leave that as an exercise for the reader).
To search on first name
var client = new ElasticClient();
var response = client.Search<Person>(s => s
.Index("people")
.Query(q => q
.Match(m => m
.Field(f => f.NameDetails[0].NameValue[0].FirstName)
.Query("Fawsu")
)
)
);
which yields the query
POST http://localhost:9200/people/_search?pretty=true&typed_keys=true
{
"query": {
"match": {
"nameDetails.nameValue.firstName": {
"query": "Fawsu"
}
}
}
}
The expression in .Field(f => f...)
is an expression to build the path to "nameDetails.nameValue.firstName"
, which will look for a match in any first name of name value and name details. The fact it uses indexers does not mean that it is targeting the first name value of the first name details, but are simply a way to traverse the object graph to build the expression.
To build a composite query to target multiple values of the same name value, both Name
and NameValue
would need to be mapped as nested
data types, and then nested queries would be used.
For a single field
var response = client.Search<Person>(s => s
.Index("people")
.Query(q => q
.Nested(n => n
.Path(f => f.NameDetails)
.Query(nq => nq
.Nested(nn => nn
.Path(f => f.NameDetails[0].NameValue)
.Query(nnq => nnq
.Match(m => m
.Field(f => f.NameDetails[0].NameValue[0].FirstName)
.Query("Fawsu")
)
)
)
)
)
)
);
For multiple fields
var response = client.Search<Person>(s => s
.Index("people")
.Query(q => q
.Nested(n => n
.Path(f => f.NameDetails)
.Query(nq => nq
.Nested(nn => nn
.Path(f => f.NameDetails[0].NameValue)
.Query(nnq => nnq
.Bool(b => b
.Must(m => m
.Match(m => m
.Field(f => f.NameDetails[0].NameValue[0].FirstName)
.Query("Fawsu")
), m => m
.Match(m => m
.Field(f => f.NameDetails[0].NameValue[0].LastName)
.Query("Pibba")
)
)
)
)
)
)
)
)
);
the latter of which results in the query
POST http://localhost:9200/people/_search?pretty=true&typed_keys=true
{
"query": {
"nested": {
"path": "nameDetails",
"query": {
"nested": {
"path": "nameDetails.nameValue",
"query": {
"bool": {
"must": [
{
"match": {
"nameDetails.nameValue.firstName": {
"query": "Fawsu"
}
}
},
{
"match": {
"nameDetails.nameValue.lastName": {
"query": "Pibba"
}
}
}
]
}
}
}
}
}
}
}