Skip to main content

Reference

Reference​

Both overloads are in Vali_Flow.NoSql.Elasticsearch.Extensions.ValiFlowElasticsearchExtensions.

ToElasticsearch<T>(this ValiFlow<T> flow, Func<object?, FieldValue?>? customConverter = null)​

Translates the conditions accumulated in a ValiFlow<T> builder into an Elasticsearch Query.

ParameterTypeRequiredDescription
flowValiFlow<T>YesThe builder containing the conditions.
customConverterFunc<object?, FieldValue?>?NoHook for mapping CLR types not handled by the built-in switch. Return null to fall through to the default conversion.

Returns: Query — pass directly to Search, Count, DeleteByQuery, etc.

ToElasticsearch<T>(this Expression<Func<T, bool>> expression, Func<object?, FieldValue?>? customConverter = null)​

Same translation for a pre-built Expression<Func<T, bool>>.

Expression<Func<Product, bool>> expr = p => p.Category == "Electronics" && p.Price < 500m;
Query filter = expr.ToElasticsearch();

Supported Operations​

ValiFlow methodIR nodeElasticsearch query
.EqualTo(x => x.Field, v)EqualNode(IsNegated: false)term { field: value }
.NotEqualTo(x => x.Field, v)EqualNode(IsNegated: true)bool { must_not: [term { field: value }] }
.GreaterThan(x => x.Field, v)ComparisonNode(GT)range { field: { gt: value } }
.GreaterThanOrEqualTo(x => x.Field, v)ComparisonNode(GTE)range { field: { gte: value } }
.LessThan(x => x.Field, v)ComparisonNode(LT)range { field: { lt: value } }
.LessThanOrEqualTo(x => x.Field, v)ComparisonNode(LTE)range { field: { lte: value } }
.Contains(x => x.Field, "txt")LikeNode(Contains)wildcard { field: "*txt*", case_insensitive: true }
.StartsWith(x => x.Field, "pre")LikeNode(StartsWith)wildcard { field: "pre*", case_insensitive: true }
.EndsWith(x => x.Field, "suf")LikeNode(EndsWith)wildcard { field: "*suf", case_insensitive: true }
.In(x => x.Field, list)InNodeterms { field: [...] }
.IsNull(x => x.Field)NullNode(IsNull)bool { must_not: [exists { field }] }
.IsNotNull(x => x.Field)NullNode(IsNotNull)exists { field }
.And(a, b)AndNodebool { must: [a, b] }
.Or(a, b)OrNodebool { should: [a, b], minimum_should_match: 1 }
.Not(inner)NotNodebool { must_not: [inner] }

Range queries use NumberRangeQuery, which requires a numeric field. The value is converted to double via Convert.ToDouble. Passing a non-numeric value to a range comparison throws NotSupportedException.

Wildcard special characters (*, ?, \) in pattern strings are escaped before building the Elasticsearch pattern.


Type Mapping​

The built-in ToFieldValue switch handles these CLR types for term and terms queries:

CLR typeFieldValue produced
nullFieldValue.Null
boolFieldValue.Boolean(b)
intFieldValue.Long(i)
longFieldValue.Long(l)
doubleFieldValue.Double(d)
floatFieldValue.Double(f)
decimalFieldValue.Double((double)dec)
stringFieldValue.String(s)
EnumFieldValue.Long(Convert.ToInt64(e))
anything elseFieldValue.String(v.ToString())

Custom Value Converter​

Use customConverter when your domain types are not in the table above, or when you need a different FieldValue representation than the default.

The converter is called before the built-in switch. Return null to let the default handle the value.

// Domain type
record Money(decimal Amount, string Currency);

// Converter: represent Money as a double FieldValue
Query filter = new ValiFlow<Product>()
.LessThan(x => x.Price, new Money(500m, "USD"))
.ToElasticsearch(value =>
{
if (value is Money m)
return FieldValue.Double((double)m.Amount);
return null;
});

Forcing decimal to use string representation in a keyword field:

Query filter = new ValiFlow<Invoice>()
.EqualTo(x => x.TaxRate, 0.21m)
.ToElasticsearch(value =>
{
if (value is decimal d)
return FieldValue.String(d.ToString("G", CultureInfo.InvariantCulture));
return null;
});

Advanced Example​

var query = new ValiFlow<Product>()
.IsNotNullOrEmpty(x => x.Category)
.GreaterThan(x => x.Price, 25m)
.ToElasticsearch();