Skip to content

Commit

Permalink
feature: add script_fields support for inner_hits
Browse files Browse the repository at this point in the history
  • Loading branch information
GokselKUCUKSAHIN committed Feb 16, 2025
1 parent 02c834c commit 1365e02
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
77 changes: 77 additions & 0 deletions es/inner_hits.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package es

type innerHitsType Object

type scriptFieldType Object

type scriptFieldsType GenericObject[scriptFieldType]

type fieldAndFormatType Object

type fieldCollapseType Object
Expand Down Expand Up @@ -187,6 +191,34 @@ func (ih innerHitsType) Name(name string) innerHitsType {
return ih
}

// ScriptField adds a script field to the inner_hits configuration.
//
// This method allows you to define a dynamically computed field within the inner_hits section
// of a query. The field is generated using the specified script.
//
// Example usage:
//
// script := es.ScriptSource("doc['price'].value * params.factor", es.ScriptLanguage.Painless)
// ih := es.InnerHits().ScriptField("discounted_price", es.ScriptField(script))
// // ih now contains a script field named "discounted_price" that calculates values dynamically.
//
// Parameters:
// - name: A string representing the name of the script field.
// - scriptField: An es.scriptFieldType object defining the script to be used.
//
// Returns:
//
// The updated es.innerHitsType object with the specified script field added.
func (ih innerHitsType) ScriptField(name string, scriptField scriptFieldType) innerHitsType {
scriptFields, ok := ih["script_fields"].(scriptFieldsType)
if !ok {
scriptFields = scriptFieldsType{}
}
scriptFields[name] = scriptField
ih["script_fields"] = scriptFields
return ih
}

// SeqNoPrimaryTerm sets the "seq_no_primary_term" field for the inner hits configuration.
//
// This method specifies whether to include the sequence number and primary term of
Expand Down Expand Up @@ -417,6 +449,51 @@ func (ih innerHitsType) Version(version bool) innerHitsType {
return ih
}

// ScriptField creates a new es.scriptFieldType object with the specified script.
//
// This function initializes an es.scriptFieldType object, which is used to define a script-based field
// in an Elasticsearch query. The provided script determines the field's value dynamically at query time.
//
// Example usage:
//
// script := es.ScriptSource("doc['price'].value * params.factor", es.ScriptLanguage.Painless)
// sf := es.ScriptField(script)
// // sf now contains an es.scriptFieldType object with the specified script.
//
// Parameters:
// - script: An es.scriptType object representing the script to be executed.
//
// Returns:
//
// An es.scriptFieldType object containing the specified script.
func ScriptField(script scriptType) scriptFieldType {
return scriptFieldType{
"script": script,
}
}

// IgnoreFailure sets the "ignore_failure" field in the script field configuration.
//
// This method allows the script field to ignore failures during execution. If set to `true`,
// Elasticsearch will continue processing the query even if the script encounters an error.
//
// Example usage:
//
// script := es.ScriptSource("doc['price'].value * params.factor", es.ScriptLanguage.Painless)
// sf := es.ScriptField(script).IgnoreFailure(true)
// // sf now has an "ignore_failure" field set to true.
//
// Parameters:
// - ignoreFailure: A boolean indicating whether script execution failures should be ignored.
//
// Returns:
//
// The updated es.scriptFieldType object with the "ignore_failure" field set.
func (sf scriptFieldType) IgnoreFailure(ignoreFailure bool) scriptFieldType {
sf["ignore_failure"] = ignoreFailure
return sf
}

// FieldCollapse creates a field collapse configuration for sorting or grouping inner hits.
//
// This function allows you to collapse the inner hits results based on a specific field.
Expand Down
72 changes: 72 additions & 0 deletions es/inner_hits_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package es_test

import (
ScriptLanguage "github.com/Trendyol/es-query-builder/es/enums/script-language"
"testing"

Order "github.com/Trendyol/es-query-builder/es/enums/sort/order"
Expand Down Expand Up @@ -355,6 +356,77 @@ func Test_InnerHits_DocvalueFields_should_create_json_with_docvalue_fields_field
assert.Equal(t, "{\"docvalue_fields\":[{\"field\":\"id\"},{\"field\":\"name\"},{\"field\":\"partition\"}]}", bodyJSON)
}

func Test_InnerHits_should_have_ScriptField_method(t *testing.T) {
t.Parallel()
// Given
ih := es.InnerHits()

// When Then
assert.NotNil(t, ih.ScriptField)
}

func Test_InnerHits_ScriptField_should_create_json_with_script_fields_field_inside_inner_hits(t *testing.T) {
t.Parallel()
// Given
ih := es.InnerHits().
ScriptField("first_script",
es.ScriptField(es.ScriptID("key", ScriptLanguage.Painless).
Option("retry", "3").
Option("timeout", "300s").
Parameter("p1", 100).
Parameter("p2", "hello"),
).IgnoreFailure(false),
).
ScriptField("second_script",
es.ScriptField(es.ScriptID("value", ScriptLanguage.Mustache).
Option("retry", "5").
Parameter("p1", "world"),
).IgnoreFailure(true),
)
// When Then
assert.NotNil(t, ih)
bodyJSON := assert.MarshalWithoutError(t, ih)
// nolint:golint,lll
assert.Equal(t, "{\"script_fields\":{\"first_script\":{\"ignore_failure\":false,\"script\":{\"id\":\"key\",\"lang\":\"painless\",\"options\":{\"retry\":\"3\",\"timeout\":\"300s\"},\"params\":{\"p1\":100,\"p2\":\"hello\"}}},\"second_script\":{\"ignore_failure\":true,\"script\":{\"id\":\"value\",\"lang\":\"mustache\",\"options\":{\"retry\":\"5\"},\"params\":{\"p1\":\"world\"}}}}}", bodyJSON)
}

//// Script Field ////

func Test_ScriptField_should_exist_on_es_package(t *testing.T) {
t.Parallel()
// Given When Then
assert.NotNil(t, es.ScriptField)
}

func Test_ScriptField_method_should_create_scriptFieldType(t *testing.T) {
t.Parallel()
// Given
sf := es.ScriptField(es.ScriptID("key", ScriptLanguage.Painless))

// Then
assert.NotNil(t, sf)
assert.IsTypeString(t, "es.scriptFieldType", sf)
}

func Test_ScriptField_should_have_IgnoreFailure_method(t *testing.T) {
t.Parallel()
// Given
sf := es.ScriptField(es.ScriptID("key", ScriptLanguage.Painless))

// When Then
assert.NotNil(t, sf.IgnoreFailure)
}

func Test_ScriptField_IgnoreFailure_should_create_json_with_collapse_field_inside_field_collapse(t *testing.T) {
t.Parallel()
// Given
sf := es.ScriptField(es.ScriptID("key", ScriptLanguage.Painless)).IgnoreFailure(true)
// When Then
assert.NotNil(t, sf)
bodyJSON := assert.MarshalWithoutError(t, sf)
assert.Equal(t, "{\"ignore_failure\":true,\"script\":{\"id\":\"key\",\"lang\":\"painless\"}}", bodyJSON)
}

//// Field Collapse ////

func Test_FieldCollapse_should_exist_on_es_package(t *testing.T) {
Expand Down

0 comments on commit 1365e02

Please sign in to comment.