diff --git a/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/Query.cs b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/Query.cs new file mode 100644 index 00000000000..ff63ed9070f --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/Query.cs @@ -0,0 +1,24 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace Elastic.Clients.Elasticsearch.QueryDsl; + +public partial class Query +{ + public bool TryGet([NotNullWhen(true)]out T? query) + { + query = default(T); + + if (Variant is T variant) + { + query = variant; + return true; + } + + return false; + } +} + diff --git a/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/QueryDescriptor.cs b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/QueryDescriptor.cs index 911c0e2d149..6d54f0390bd 100644 --- a/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/QueryDescriptor.cs +++ b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/QueryDescriptor.cs @@ -9,6 +9,11 @@ namespace Elastic.Clients.Elasticsearch.QueryDsl; public sealed partial class QueryDescriptor { + /// + /// A query defined as a raw JSON string. This can be useful when support for a built-in query is not yet available. + /// + public QueryDescriptor RawJson(string rawJson) => Set(new RawJsonQuery(rawJson), "raw_json"); + public void MatchAll() => Set(_ => { }, "match_all"); @@ -18,6 +23,11 @@ public void Term(Expression> field, object value public sealed partial class QueryDescriptor { + /// + /// A query defined as a raw JSON string. This can be useful when support for a built-in query is not yet available. + /// + public QueryDescriptor RawJson(string rawJson) => Set(new RawJsonQuery(rawJson), "raw_json"); + public void MatchAll() => Set(_ => { }, "match_all"); diff --git a/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RangeQuery.cs b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RangeQuery.cs index 81fe9090b62..12e47622f7d 100644 --- a/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RangeQuery.cs +++ b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RangeQuery.cs @@ -10,26 +10,12 @@ namespace Elastic.Clients.Elasticsearch.QueryDsl; -public partial class Query -{ - public bool TryGet([NotNullWhen(true)]out T? query) - { - query = default(T); - - if (Variant is T variant) - { - query = variant; - return true; - } - - return false; - } -} - [JsonConverter(typeof(RangeQueryConverter))] public class RangeQuery : SearchQuery { internal RangeQuery() { } + + public static implicit operator Query(RangeQuery rangeQuery) => QueryDsl.Query.Range(rangeQuery); } internal sealed class RangeQueryConverter : JsonConverter diff --git a/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RawJsonQuery.cs b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RawJsonQuery.cs new file mode 100644 index 00000000000..6161fdade14 --- /dev/null +++ b/src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RawJsonQuery.cs @@ -0,0 +1,31 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Elastic.Clients.Elasticsearch.QueryDsl; + +/// +/// Allows a query represented as a string of JSON to be defined. This can be useful when support for a built-in query is not yet available. +/// +public sealed class RawJsonQuery : SearchQuery +{ + public RawJsonQuery(string rawQuery) => Raw = rawQuery; + + /// + /// The raw JSON representing the query to be executed. + /// + public string Raw { get; } + + public static implicit operator Query(RawJsonQuery rawJsonQuery) => Query.RawJson(rawJsonQuery); +} + +internal sealed class RawJsonQueryConverter : JsonConverter +{ + public override RawJsonQuery? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException("We never expect to deserialize a raw query."); + + public override void Write(Utf8JsonWriter writer, RawJsonQuery value, JsonSerializerOptions options) => writer.WriteRawValue(value.Raw); +} diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/QueryDsl/Query.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/QueryDsl/Query.g.cs index 1778dfe6ebf..627eada3fb0 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/QueryDsl/Query.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/QueryDsl/Query.g.cs @@ -73,6 +73,7 @@ internal Query(string variantName, object variant) public static Query QueryString(Elastic.Clients.Elasticsearch.QueryDsl.QueryStringQuery queryStringQuery) => new Query("query_string", queryStringQuery); public static Query Range(Elastic.Clients.Elasticsearch.QueryDsl.RangeQuery rangeQuery) => new Query("range", rangeQuery); public static Query RankFeature(Elastic.Clients.Elasticsearch.QueryDsl.RankFeatureQuery rankFeatureQuery) => new Query("rank_feature", rankFeatureQuery); + public static Query RawJson(Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery rawJsonQuery) => new Query("raw_json", rawJsonQuery); public static Query Regexp(Elastic.Clients.Elasticsearch.QueryDsl.RegexpQuery regexpQuery) => new Query("regexp", regexpQuery); public static Query Script(Elastic.Clients.Elasticsearch.QueryDsl.ScriptQuery scriptQuery) => new Query("script", scriptQuery); public static Query ScriptScore(Elastic.Clients.Elasticsearch.QueryDsl.ScriptScoreQuery scriptScoreQuery) => new Query("script_score", scriptScoreQuery); @@ -312,6 +313,13 @@ public override Query Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe return new Query(propertyName, variant); } + if (propertyName == "raw_json") + { + var variant = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new Query(propertyName, variant); + } + if (propertyName == "regexp") { var variant = JsonSerializer.Deserialize(ref reader, options); @@ -436,6 +444,12 @@ public override Query Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe public override void Write(Utf8JsonWriter writer, Query value, JsonSerializerOptions options) { + if (value.VariantName == "raw_json" && value.TryGet(out var rawJsonQuery)) + { + writer.WriteRawValue(rawJsonQuery.Raw); + return; + } + writer.WriteStartObject(); writer.WritePropertyName(value.VariantName); switch (value.VariantName) @@ -527,6 +541,9 @@ public override void Write(Utf8JsonWriter writer, Query value, JsonSerializerOpt case "rank_feature": JsonSerializer.Serialize(writer, (Elastic.Clients.Elasticsearch.QueryDsl.RankFeatureQuery)value.Variant, options); break; + case "raw_json": + JsonSerializer.Serialize(writer, (Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery)value.Variant, options); + break; case "regexp": JsonSerializer.Serialize(writer, (Elastic.Clients.Elasticsearch.QueryDsl.RegexpQuery)value.Variant, options); break; @@ -676,6 +693,7 @@ private QueryDescriptor Set(object variant, string variantName) public QueryDescriptor Range(Action> configure) => Set(configure, "range"); public QueryDescriptor RankFeature(RankFeatureQuery rankFeatureQuery) => Set(rankFeatureQuery, "rank_feature"); public QueryDescriptor RankFeature(Action> configure) => Set(configure, "rank_feature"); + public QueryDescriptor RawJson(Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery rawJsonQuery) => Set(rawJsonQuery, "raw_json"); public QueryDescriptor Regexp(RegexpQuery regexpQuery) => Set(regexpQuery, "regexp"); public QueryDescriptor Regexp(Action> configure) => Set(configure, "regexp"); public QueryDescriptor Script(ScriptQuery scriptQuery) => Set(scriptQuery, "script"); @@ -718,6 +736,12 @@ protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions o return; } + if (ContainedVariantName == "raw_json") + { + writer.WriteRawValue(((RawJsonQuery)Variant).Raw); + return; + } + writer.WriteStartObject(); writer.WritePropertyName(ContainedVariantName); if (Variant is not null) @@ -849,6 +873,7 @@ private QueryDescriptor Set(object variant, string variantName) public QueryDescriptor RankFeature(RankFeatureQuery rankFeatureQuery) => Set(rankFeatureQuery, "rank_feature"); public QueryDescriptor RankFeature(Action configure) => Set(configure, "rank_feature"); public QueryDescriptor RankFeature(Action> configure) => Set(configure, "rank_feature"); + public QueryDescriptor RawJson(Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery rawJsonQuery) => Set(rawJsonQuery, "raw_json"); public QueryDescriptor Regexp(RegexpQuery regexpQuery) => Set(regexpQuery, "regexp"); public QueryDescriptor Regexp(Action configure) => Set(configure, "regexp"); public QueryDescriptor Regexp(Action> configure) => Set(configure, "regexp"); @@ -906,6 +931,12 @@ protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions o return; } + if (ContainedVariantName == "raw_json") + { + writer.WriteRawValue(((RawJsonQuery)Variant).Raw); + return; + } + writer.WriteStartObject(); writer.WritePropertyName(ContainedVariantName); if (Variant is not null) diff --git a/tests/Tests/QueryDsl/RawJsonQueryUsageTests.cs b/tests/Tests/QueryDsl/RawJsonQueryUsageTests.cs new file mode 100644 index 00000000000..65addd17ca5 --- /dev/null +++ b/tests/Tests/QueryDsl/RawJsonQueryUsageTests.cs @@ -0,0 +1,25 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +using Elastic.Clients.Elasticsearch.QueryDsl; +using Tests.Core.ManagedElasticsearch.Clusters; +using Tests.Domain; +using Tests.Framework.EndpointTests.TestState; + +namespace Tests.QueryDsl; + +public class RawJsonQueryUsageTests : QueryDslUsageTestsBase +{ + private static readonly string RawTermQuery = @"{""term"": { ""fieldname"":""value"" } }"; + + public RawJsonQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) + { + } + + protected override Query QueryInitializer => new RawJsonQuery(RawTermQuery); + + protected override QueryDescriptor QueryFluent(QueryDescriptor queryDescriptor) => + queryDescriptor + .RawJson(RawTermQuery); +} diff --git a/tests/Tests/_VerifySnapshots/RawJsonQueryUsageTests.VerifyDescriptorJson.verified.txt b/tests/Tests/_VerifySnapshots/RawJsonQueryUsageTests.VerifyDescriptorJson.verified.txt new file mode 100644 index 00000000000..cbd1dc12b9e --- /dev/null +++ b/tests/Tests/_VerifySnapshots/RawJsonQueryUsageTests.VerifyDescriptorJson.verified.txt @@ -0,0 +1,7 @@ +{ + query: { + term: { + fieldname: value + } + } +} \ No newline at end of file diff --git a/tests/Tests/_VerifySnapshots/RawJsonQueryUsageTests.VerifyInitializerJson.verified.txt b/tests/Tests/_VerifySnapshots/RawJsonQueryUsageTests.VerifyInitializerJson.verified.txt new file mode 100644 index 00000000000..cbd1dc12b9e --- /dev/null +++ b/tests/Tests/_VerifySnapshots/RawJsonQueryUsageTests.VerifyInitializerJson.verified.txt @@ -0,0 +1,7 @@ +{ + query: { + term: { + fieldname: value + } + } +} \ No newline at end of file