Skip to content

[Backport 8.5] Add support for raw JSON query. #7369

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

Merged
merged 1 commit into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/Elastic.Clients.Elasticsearch/Types/QueryDsl/Query.cs
Original file line number Diff line number Diff line change
@@ -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<T>([NotNullWhen(true)]out T? query)
{
query = default(T);

if (Variant is T variant)
{
query = variant;
return true;
}

return false;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ namespace Elastic.Clients.Elasticsearch.QueryDsl;

public sealed partial class QueryDescriptor<TDocument>
{
/// <summary>
/// A query defined as a raw JSON string. This can be useful when support for a built-in query is not yet available.
/// </summary>
public QueryDescriptor<TDocument> RawJson(string rawJson) => Set(new RawJsonQuery(rawJson), "raw_json");

public void MatchAll() =>
Set<MatchAllQueryDescriptor>(_ => { }, "match_all");

Expand All @@ -18,6 +23,11 @@ public void Term<TValue>(Expression<Func<TDocument, TValue>> field, object value

public sealed partial class QueryDescriptor
{
/// <summary>
/// A query defined as a raw JSON string. This can be useful when support for a built-in query is not yet available.
/// </summary>
public QueryDescriptor RawJson(string rawJson) => Set(new RawJsonQuery(rawJson), "raw_json");

public void MatchAll() =>
Set<MatchAllQueryDescriptor>(_ => { }, "match_all");

Expand Down
18 changes: 2 additions & 16 deletions src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RangeQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,12 @@

namespace Elastic.Clients.Elasticsearch.QueryDsl;

public partial class Query
{
public bool TryGet<T>([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<RangeQuery>
Expand Down
31 changes: 31 additions & 0 deletions src/Elastic.Clients.Elasticsearch/Types/QueryDsl/RawJsonQuery.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// 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.
/// </summary>
public sealed class RawJsonQuery : SearchQuery
{
public RawJsonQuery(string rawQuery) => Raw = rawQuery;

/// <summary>
/// The raw JSON representing the query to be executed.
/// </summary>
public string Raw { get; }

public static implicit operator Query(RawJsonQuery rawJsonQuery) => Query.RawJson(rawJsonQuery);
}

internal sealed class RawJsonQueryConverter : JsonConverter<RawJsonQuery>
{
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery?>(ref reader, options);
reader.Read();
return new Query(propertyName, variant);
}

if (propertyName == "regexp")
{
var variant = JsonSerializer.Deserialize<Elastic.Clients.Elasticsearch.QueryDsl.RegexpQuery?>(ref reader, options);
Expand Down Expand Up @@ -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<RawJsonQuery>(out var rawJsonQuery))
{
writer.WriteRawValue(rawJsonQuery.Raw);
return;
}

writer.WriteStartObject();
writer.WritePropertyName(value.VariantName);
switch (value.VariantName)
Expand Down Expand Up @@ -527,6 +541,9 @@ public override void Write(Utf8JsonWriter writer, Query value, JsonSerializerOpt
case "rank_feature":
JsonSerializer.Serialize<Elastic.Clients.Elasticsearch.QueryDsl.RankFeatureQuery>(writer, (Elastic.Clients.Elasticsearch.QueryDsl.RankFeatureQuery)value.Variant, options);
break;
case "raw_json":
JsonSerializer.Serialize<Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery>(writer, (Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery)value.Variant, options);
break;
case "regexp":
JsonSerializer.Serialize<Elastic.Clients.Elasticsearch.QueryDsl.RegexpQuery>(writer, (Elastic.Clients.Elasticsearch.QueryDsl.RegexpQuery)value.Variant, options);
break;
Expand Down Expand Up @@ -676,6 +693,7 @@ private QueryDescriptor<TDocument> Set(object variant, string variantName)
public QueryDescriptor<TDocument> Range(Action<RangeQueryDescriptor<TDocument>> configure) => Set(configure, "range");
public QueryDescriptor<TDocument> RankFeature(RankFeatureQuery rankFeatureQuery) => Set(rankFeatureQuery, "rank_feature");
public QueryDescriptor<TDocument> RankFeature(Action<RankFeatureQueryDescriptor<TDocument>> configure) => Set(configure, "rank_feature");
public QueryDescriptor<TDocument> RawJson(Elastic.Clients.Elasticsearch.QueryDsl.RawJsonQuery rawJsonQuery) => Set(rawJsonQuery, "raw_json");
public QueryDescriptor<TDocument> Regexp(RegexpQuery regexpQuery) => Set(regexpQuery, "regexp");
public QueryDescriptor<TDocument> Regexp(Action<RegexpQueryDescriptor<TDocument>> configure) => Set(configure, "regexp");
public QueryDescriptor<TDocument> Script(ScriptQuery scriptQuery) => Set(scriptQuery, "script");
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -849,6 +873,7 @@ private QueryDescriptor Set(object variant, string variantName)
public QueryDescriptor RankFeature(RankFeatureQuery rankFeatureQuery) => Set(rankFeatureQuery, "rank_feature");
public QueryDescriptor RankFeature(Action<RankFeatureQueryDescriptor> configure) => Set(configure, "rank_feature");
public QueryDescriptor RankFeature<TDocument>(Action<RankFeatureQueryDescriptor<TDocument>> 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<RegexpQueryDescriptor> configure) => Set(configure, "regexp");
public QueryDescriptor Regexp<TDocument>(Action<RegexpQueryDescriptor<TDocument>> configure) => Set(configure, "regexp");
Expand Down Expand Up @@ -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)
Expand Down
25 changes: 25 additions & 0 deletions tests/Tests/QueryDsl/RawJsonQueryUsageTests.cs
Original file line number Diff line number Diff line change
@@ -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<Project> QueryFluent(QueryDescriptor<Project> queryDescriptor) =>
queryDescriptor
.RawJson(RawTermQuery);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
query: {
term: {
fieldname: value
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
query: {
term: {
fieldname: value
}
}
}