Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decay functions missing #8119

Open
insulationman opened this issue Apr 12, 2024 · 7 comments
Open

Decay functions missing #8119

insulationman opened this issue Apr 12, 2024 · 7 comments
Labels
8.x Relates to 8.x client version Area: Generator Category: Feature

Comments

@insulationman
Copy link

insulationman commented Apr 12, 2024

Is your feature request related to a problem? Please describe.
I want to use decay functions to boost recently created documents. This functionality seems to be missing from the client.

Describe the solution you'd like
I would like to be able to be able to add a decay function as described in the following code from the official docs:

curl localhost:9200/blogposts/_search -d '
"query": {
  "function_score": {
    "query": {
      "match": { "title": "elasticsearch"}
    },
    "functions": [
      { "boost":  1 },
      {
        "gauss": {
          "timestamp": {
            "scale": "4w"
          }
        }
      }
    ],
    "score_mode": "sum"
  }
}'

I believe an api similar to the one for FieldValueFactor function would work well.

Link to Release Note

Describe alternatives you've considered
I have considered not using the official client, but I'm looking for better workarounds.

Additional context
I want to use this functionality to get recently created documents to score higher in searches.

@flobernd flobernd added 8.x Relates to 8.x client version Area: Generator labels Apr 12, 2024
@flobernd
Copy link
Member

Hi @insulationman,

thanks for requesting this feature. The decay functions will be implemented soon. They require some special handling to get generated correctly.

@jasielmacedo
Copy link

jasielmacedo commented Apr 26, 2024

@flobernd So I think it's better to at least give us options to create our own solution for that. Like I'm stuck trying to think in a workaround to use gauss function and even the Serializer function you put internal ctor So I can't create my own descriptor for example locally

It's been a year #7428

You don't have time to create this function but you don't allow anyone else to at least try to create their own solution for that locally? This version 8 is incomplete and you have to give devs options to keep using it.

@flobernd
Copy link
Member

From the other post:

you could consider dropping to the Transport layer (available from the client instance), where you can send a complete request as JSON and handle the JSON response

An example of how to use the low level transport can be found in the migration guide.

@jasielmacedo
Copy link

@flobernd thanks for the suggestion but intercepting the JSON or executing a complete JSON request? why use this library anyway?
I appreciate you came here to answer this, but I think suggesting to not use this library with a product that was developed using this version 8 of the library is insane.

What I'm asking is just a possibility of creating my own FunctionScore so I don't need to rewrite the whole code just to add one additional feature

@flobernd
Copy link
Member

Hi @jasielmacedo,

but intercepting the JSON or executing a complete JSON request? why use this library anyway?

This library supports more than 450 endpoints and nearly 3000 types. I spent the past 6+ months continuing the good work of Martijn and Steve to get the code generator to a point where it finally supports 95%+ of the overall available Elasticsearch functionality. For me, these are at least 450 reasons to use the library, even if I would have to manually craft one JSON request myself - but that's of course just my personal opinion.

I think suggesting to not use this library with a product that was developed using this version 8 of the library is insane.

I think we misunderstood here. I'm strongly suggesting to use the library! You can use the regular client for everything besides the query that uses decay functions. For this particular case, you can fall-back to the low-level client (which still saves you from having to build the complete transport level layer yourself). The low level client is exposed by the regular client:
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/migration-guide.html#_workarounds_for_missing_features

I know that the above workaround is not perfect. At the same time, please note, that a lot has been added to the library in the last weeks and a lot of bugs have been fixed and usability problems have been addressed. I'm sorry, that the decay functions were not part of this yet, but they are on my todo list and I excpect then to be available within weeks.

What I'm asking is just a possibility of creating my own FunctionScore so I don't need to rewrite the whole code just to add one additional feature

I can give you another hacky alternative. Using reflection you could access the internal constructor of FunctionScore to manually create an instance with a custom variant:

internal FunctionScore(string variantName, object variant)

For variantName use one of linear, gauss or exp.

For variant, create your own typed function score class or use JSON for this part like this:

var gauss = """
{
  "timestamp": {
    "scale": "4w"
  }
}
"""

var variant = new RawJsonString(gauss);

The RawJsonString class is a special struct that serializes the contained string as raw json (without quotes).

@jasielmacedo
Copy link

jasielmacedo commented Apr 30, 2024

@flobernd Thanks. Yeah, I was wrong. It wasn't clear in the beginning but reading the code of the library I figured out.
I ended up creating a similar solution using the Transport class you suggested, let me share here so if someone has the same issue they could adapt for their case as well while the final solution is not ready:

For the request I just updated the way I execute the query. I just created a delegate so I can intercept the json.

var json = _client.RequestResponseSerializer.SerializeToString(searchRequestDescriptor);
json = AddGaussFunction(json);

var queryParameters = new DefaultRequestParameters();
// I added this because I'm using aggregations and suggester
queryParameters.SetQueryString("typed_keys", true);

var response = await _client.Transport.RequestAsync<SearchResponse<T>>(Elastic.Transport.HttpMethod.POST, $"/{indexName}/_search", PostData.String(json), queryParameters).ConfigureAwait(false);
if (!response.IsValidResponse)
{
    /// throw error or do whatever you want to do with the error
}

return response;

So My case the function score was being added right after the first query so it was simple to update the json, but with a simple tweak it can be used anywhere

void AddGaussFunction(string jsonString, int scale, int offset)
{
    try
    {
        JsonObject jsonObject = JsonNode.Parse(jsonString).AsObject();
        if (jsonObject["query"]?["function_score"]?["functions"] is JsonArray functionsArray)
        {
            var now = ((DateTimeOffset)DateTime.UtcNow).ToUnixTimeSeconds();            
            var gaussFunction = JsonNode.Parse($@"
                {{
                    ""gauss"": {{
                        """": {{
                            ""origin"": ""{now}"",
                            ""scale"": ""{scale}"",
                            ""offset"": ""{offset}"",
                            ""decay"": 0.7
                        }}
                    }},
                    ""weight"": 1.2,
                    ""filter"": {{
                        ""term"": {{
                            ""customfield"": ""CustomType""
                        }}
                    }}
                }}
            ").AsObject();

            functionsArray.Add(gaussFunction);
            return jsonObject.ToJsonString(new JsonSerializerOptions { WriteIndented = true });
        }
        else
        {
            return jsonString;
        }
    }
    catch (JsonException e)
    {
        throw new InvalidOperationException("Error parsing JSON.", e);
    }
}

@flobernd
Copy link
Member

flobernd commented May 7, 2024

Related to: elastic/elasticsearch-specification#2545

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
8.x Relates to 8.x client version Area: Generator Category: Feature
Projects
None yet
Development

No branches or pull requests

3 participants