diff --git a/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj b/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj
index 9e2c9899e43..8f99619c5e5 100644
--- a/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj
+++ b/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj
@@ -108,6 +108,7 @@
+
diff --git a/src/Nest.Tests.Unit/Search/Query/Singles/FunctionScoreQueryJson.cs b/src/Nest.Tests.Unit/Search/Query/Singles/FunctionScoreQueryJson.cs
new file mode 100644
index 00000000000..b377c6491ff
--- /dev/null
+++ b/src/Nest.Tests.Unit/Search/Query/Singles/FunctionScoreQueryJson.cs
@@ -0,0 +1,47 @@
+using NUnit.Framework;
+using Nest.DSL.Query;
+using Nest.Tests.MockData.Domain;
+
+namespace Nest.Tests.Unit.Search.Query.Singles
+{
+ [TestFixture]
+ public class FunctionScoreQueryTests
+ {
+ [Test]
+ public void FunctionScoreQuery()
+ {
+ var s = new SearchDescriptor().From(0).Size(10)
+ .Query(q => q
+ .FunctionScore(fs => fs
+ .Query(qq => qq.MatchAll())
+ .Functions(
+ f => f.Gauss(x=>x.StartedOn, d=>d.Scale("42w")),
+ f => f.Linear(x => x.FloatValue, d => d.Scale("0.3")),
+ f => f.Exp(x => x.DoubleValue, d => d.Scale("0.5")),
+ f => f.BoostFactor(2)
+ )
+ .ScoreMode(FunctionScoreMode.sum)
+ )
+ ).Fields(x=>x.Content);
+
+ var json = TestElasticClient.Serialize(s);
+ var expected = @"{
+ from: 0, size: 10,
+ query : {
+ function_score : {
+ functions: [
+ {gauss: { startedOn : { scale: '42w'}}},
+ {linear: { floatValue : { scale: '0.3'}}},
+ {exp: { doubleValue: { scale: '0.5'}}},
+ {boost_factor: 2.0 }
+ ],
+ query : { match_all : {} },
+ score_mode: 'sum'
+ }
+ },
+ fields: [""content""]
+ }";
+ Assert.True(json.JsonEquals(expected), json);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Nest/DSL/Query.cs b/src/Nest/DSL/Query.cs
index 94b96775075..95c99baa124 100644
--- a/src/Nest/DSL/Query.cs
+++ b/src/Nest/DSL/Query.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Nest.DSL.Query;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Nest.Resolvers.Converters;
@@ -237,6 +238,11 @@ public static BaseQuery Wildcard(string field, string value, double? Boost = nul
{
return new QueryDescriptor().Wildcard(field, value, Boost);
}
+
+ public static BaseQuery FunctionScore(Action> functionScoreQuery)
+ {
+ return new QueryDescriptor().FunctionScore(functionScoreQuery);
+ }
}
diff --git a/src/Nest/DSL/Query/FunctionScoreQueryDescriptor.cs b/src/Nest/DSL/Query/FunctionScoreQueryDescriptor.cs
new file mode 100644
index 00000000000..9e5170315bd
--- /dev/null
+++ b/src/Nest/DSL/Query/FunctionScoreQueryDescriptor.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using Nest.Resolvers;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace Nest.DSL.Query
+{
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class FunctionScoreQueryDescriptor : IQuery where T : class
+ {
+ [JsonProperty(PropertyName = "functions")]
+ internal IEnumerable> _Functions { get; set; }
+
+ [JsonProperty(PropertyName = "query")]
+ internal BaseQuery _Query { get; set; }
+
+ [JsonProperty(PropertyName = "score_mode")]
+ [JsonConverter(typeof(StringEnumConverter))]
+ FunctionScoreMode _ScoreMode { get; set; }
+
+
+ internal bool IsConditionless
+ {
+ get
+ {
+ return this._Query == null || this._Query.IsConditionless;
+ }
+ }
+
+ public FunctionScoreQueryDescriptor Query(Func, BaseQuery> querySelector)
+ {
+ querySelector.ThrowIfNull("querySelector");
+ var query = new QueryDescriptor();
+ var q = querySelector(query);
+
+ this._Query = q;
+ return this;
+ }
+
+ public FunctionScoreQueryDescriptor Functions(params Func, FunctionScoreFunction>[] functions)
+ {
+ var descriptor = new FunctionScoreFunctionsDescriptor();
+
+ foreach (var f in functions)
+ {
+ f(descriptor);
+ }
+
+ _Functions = descriptor;
+
+ return this;
+ }
+
+ public FunctionScoreQueryDescriptor ScoreMode(FunctionScoreMode mode)
+ {
+ this._ScoreMode = mode;
+ return this;
+ }
+ }
+
+ public enum FunctionScoreMode
+ {
+ multiply,
+ sum,
+ avg,
+ first,
+ max,
+ min
+ }
+
+ public class FunctionScoreFunctionsDescriptor : IEnumerable>
+ {
+ internal List> _Functions { get; set; }
+
+ public FunctionScoreFunctionsDescriptor()
+ {
+ this._Functions = new List>();
+ }
+
+ public FunctionScoreFunction Gauss(Expression> objectPath, Action db)
+ {
+ var fn = new GaussFunction(objectPath, db);
+ this._Functions.Add(fn);
+ return fn;
+ }
+
+ public FunctionScoreFunction Linear(Expression> objectPath, Action db)
+ {
+ var fn = new LinearFunction(objectPath, db);
+ this._Functions.Add(fn);
+ return fn;
+ }
+
+ public FunctionScoreFunction Exp(Expression> objectPath, Action db)
+ {
+ var fn = new ExpFunction(objectPath, db);
+ this._Functions.Add(fn);
+ return fn;
+ }
+
+ public BoostFactorFunction BoostFactor(double value)
+ {
+ var fn = new BoostFactorFunction(value);
+ this._Functions.Add(fn);
+ return fn;
+ }
+
+
+ public IEnumerator> GetEnumerator()
+ {
+ return _Functions.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _Functions.GetEnumerator();
+ }
+ }
+
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class FunctionScoreFunction
+ {
+ }
+
+ public class FunctionScoreDecayFieldDescriptor
+ {
+ [JsonProperty(PropertyName = "origin")]
+ internal string _Origin { get; set; }
+
+ [JsonProperty(PropertyName = "scale")]
+ internal string _Scale { get; set; }
+
+ [JsonProperty(PropertyName = "offset")]
+ internal string _Offset { get; set; }
+
+ [JsonProperty(PropertyName = "decay")]
+ internal double? _Decay { get; set; }
+
+ public FunctionScoreDecayFieldDescriptor Origin(string origin)
+ {
+ this._Origin = origin;
+ return this;
+ }
+
+ public FunctionScoreDecayFieldDescriptor Scale(string scale)
+ {
+ this._Scale = scale;
+ return this;
+ }
+
+ public FunctionScoreDecayFieldDescriptor Offset(string offset)
+ {
+ this._Offset = offset;
+ return this;
+ }
+
+ public FunctionScoreDecayFieldDescriptor Decay(double? decay)
+ {
+ this._Decay = decay;
+ return this;
+ }
+ }
+
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class FunctionScoreDecayFunction : FunctionScoreFunction
+ {
+ }
+
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class GaussFunction : FunctionScoreDecayFunction
+ {
+ [JsonProperty(PropertyName = "gauss")]
+ [JsonConverter(typeof(DictionaryKeysAreNotPropertyNamesJsonConverter))]
+ internal IDictionary _GaussDescriptor { get; set; }
+
+ public GaussFunction(Expression> objectPath, Action descriptorBuilder)
+ {
+ _GaussDescriptor = new Dictionary();
+
+ var resolver = new PropertyNameResolver();
+ var fieldName = resolver.Resolve(objectPath);
+ var descriptor = new FunctionScoreDecayFieldDescriptor();
+ descriptorBuilder(descriptor);
+ _GaussDescriptor[fieldName] = descriptor;
+ }
+ }
+
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class LinearFunction : FunctionScoreDecayFunction
+ {
+ [JsonProperty(PropertyName = "linear")]
+ [JsonConverter(typeof(DictionaryKeysAreNotPropertyNamesJsonConverter))]
+ internal IDictionary _LinearDescriptor { get; set; }
+
+ public LinearFunction(Expression> objectPath, Action descriptorBuilder)
+ {
+ _LinearDescriptor = new Dictionary();
+
+ var resolver = new PropertyNameResolver();
+ var fieldName = resolver.Resolve(objectPath);
+ var descriptor = new FunctionScoreDecayFieldDescriptor();
+ descriptorBuilder(descriptor);
+ _LinearDescriptor[fieldName] = descriptor;
+ }
+ }
+
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class ExpFunction : FunctionScoreDecayFunction
+ {
+ [JsonProperty(PropertyName = "exp")]
+ [JsonConverter(typeof(DictionaryKeysAreNotPropertyNamesJsonConverter))]
+ internal IDictionary _ExpDescriptor { get; set; }
+
+ public ExpFunction(Expression> objectPath, Action descriptorBuilder)
+ {
+ _ExpDescriptor = new Dictionary();
+
+ var resolver = new PropertyNameResolver();
+ var fieldName = resolver.Resolve(objectPath);
+ var descriptor = new FunctionScoreDecayFieldDescriptor();
+ descriptorBuilder(descriptor);
+ _ExpDescriptor[fieldName] = descriptor;
+ }
+ }
+
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ public class BoostFactorFunction : FunctionScoreFunction
+ {
+ [JsonProperty(PropertyName = "boost_factor")]
+ internal double _BoostFactor { get; set; }
+
+ public BoostFactorFunction(double boostFactor)
+ {
+ _BoostFactor = boostFactor;
+ }
+ }
+}
diff --git a/src/Nest/DSL/QueryDescriptor.cs b/src/Nest/DSL/QueryDescriptor.cs
index 4165e46542c..96a9128527d 100644
--- a/src/Nest/DSL/QueryDescriptor.cs
+++ b/src/Nest/DSL/QueryDescriptor.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Nest.DSL.Query;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Nest.Resolvers.Converters;
@@ -35,6 +36,9 @@ public QueryDescriptor()
internal BoostingQueryDescriptor BoostingQueryDescriptor { get; set; }
[JsonProperty(PropertyName = "ids")]
internal IdsQuery IdsQuery { get; set; }
+
+ [JsonProperty(PropertyName = "function_score")]
+ internal FunctionScoreQueryDescriptor FunctionScoreQueryDescriptor { get; set; }
[JsonProperty(PropertyName = "custom_score")]
internal CustomScoreQueryDescriptor CustomScoreQueryDescriptor { get; set; }
[JsonProperty(PropertyName = "custom_filters_score")]
@@ -128,6 +132,7 @@ internal QueryDescriptor Clone()
BoostingQueryDescriptor = BoostingQueryDescriptor,
IdsQuery = IdsQuery,
+ FunctionScoreQueryDescriptor = FunctionScoreQueryDescriptor,
CustomScoreQueryDescriptor = CustomScoreQueryDescriptor,
CustomBoostFactorQueryDescriptor = CustomBoostFactorQueryDescriptor,
ConstantScoreQueryDescriptor = ConstantScoreQueryDescriptor,
@@ -939,5 +944,20 @@ public BaseQuery SpanNot(Action> selector)
return new QueryDescriptor { SpanNotQueryDescriptor = this.SpanNotQueryDescriptor };
}
- }
+ ///
+ /// Function score query
+ ///
+ ///
+ public BaseQuery FunctionScore(Action> functionScoreQuery)
+ {
+ var query = new FunctionScoreQueryDescriptor();
+ functionScoreQuery(query);
+
+ if (query.IsConditionless)
+ return CreateConditionlessQueryDescriptor(query);
+
+ this.FunctionScoreQueryDescriptor = query;
+ return new QueryDescriptor { FunctionScoreQueryDescriptor = this.FunctionScoreQueryDescriptor };
+ }
+ }
}
diff --git a/src/Nest/Nest.csproj b/src/Nest/Nest.csproj
index b0b1fc518a8..402847edfe3 100644
--- a/src/Nest/Nest.csproj
+++ b/src/Nest/Nest.csproj
@@ -70,6 +70,7 @@
+