I need to use Script Fields in my .net code, in order to run some query logic on Elasticsearch. This doc says that:
Script fields can be accessed on the response using .Fields, similarly to stored fields.
In order to retrieve the Script Fields results, it looks like I need to loop over the results (as suggested here or here). I want to avoid that loop, and somehow get the data within my call to NEST's client.search(), which already populates a list of results. Is that possible?
Here's my call:
public async Task<List<SomeObj>> GetSomeDocsFromEs()
{
var result = await client.SearchAsync<SomeObj>(s => s
.Index(...)
.Query(...)
.Size(...)
.From(...)
.Sort(...)
.ScriptFields(sf => sf
.ScriptField("StoredField1", sc => sc
.Source(some Painless script)
)
.ScriptField("StoredField2", sc => sc
.Source(some Painless script)
)
.ScriptField("StoredField3", sc => sc
.Source(some Painless script)
)
)
.StoredFields(sf => sf
.Fields(
f => f.StoredField1,
f => f.StoredField2,
f => f.StoredField3
)
)
.Source(so => so
.Includes(i => i
.Fields(f => f.SkuEntry.Field1,
f => f.SkuEntry.Field2,
f => f.SkuEntry.Field3
)
)
)
);
return result.Documents.ToList();
}
I want to avoid that loop, and somehow get the data within my call to NEST's client.search(), which already populates a list of results. Is that possible?
Script fields are returned for each hit in the hits array, so there's no way to avoid looping over hits.
result.Documents
in the example maps to the _source
field for each document, which is the original JSON object sent to Elasticsearch and indexed. It's a convenient shorthand for result.Hits.Select(h => h.Source).ToList()
.
Script fields are not part of the _source
document, they are returned in a separate field for each hit. result.Fields
is a convenient shorthand for result.Hits.Select(h => h.Fields).ToList()
.
As an example, given the following query
var searchResponse = client.Search<Project>(s => s
.ScriptFields(sf => sf
.ScriptField("test1", sc => sc
.Source("doc['numberOfCommits'].value * 2")
)
.ScriptField("test2", sc => sc
.Source("doc['numberOfCommits'].value * params.factor")
.Params(p => p
.Add("factor", 2.0)
)
)
)
);
which sends the following request
{
"script_fields": {
"test1": {
"script": {
"source": "doc['numberOfCommits'].value * 2"
}
},
"test2": {
"script": {
"source": "doc['numberOfCommits'].value * params.factor",
"params": {
"factor": 2.0
}
}
}
}
}
The JSON response is
{
"took" : 26,
"timed_out" : false,
"_shards" : {
"total" : 2,
"successful" : 2,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1100,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "project",
"_type" : "_doc",
"_id" : "Konopelski Inc2032",
"_score" : 1.0,
"_routing" : "Konopelski Inc2032",
"fields" : {
"test2" : [
308.0
],
"test1" : [
308
]
}
},
{
"_index" : "project",
"_type" : "_doc",
"_id" : "Feest Group2047",
"_score" : 1.0,
"_routing" : "Feest Group2047",
"fields" : {
"test2" : [
1986.0
],
"test1" : [
1986
]
}
}
]
}
}
To iterate over the script fields of each hit in the response
foreach (var fields in response.Fields)
{
// do something with test1 and test2 values
var test1 = fields.Value<int>("test1");
var test2 = fields.Value<double>("test2");
}