From 2b9cbbb99e764f253c50d28a32f956c2d9b3346f Mon Sep 17 00:00:00 2001 From: Felipe Rafael Montenegro Morriberon Date: Wed, 7 Oct 2026 15:19:13 -0500 Subject: [PATCH] docs: bring README/CHANGELOG up to date with the real published versions, add Spanish translations README.md and CHANGELOG.md still showed the pre-release versions (Vali-Flow 1.3.4, InMemory 1.1.5, Sql 1.1.1, NoSql.* 1.1.0, Couchbase/CosmosDb/Firestore 1.0.0) even though all 12 packages were just published for real. Also missing entirely: Vali-Flow.Abstractions and Vali-Flow.NoSql, the two shared packages every other package depends on. - README.md: version tables updated to match what's live on NuGet.org, added the two missing rows. - CHANGELOG.md: split the [Unreleased] block into 12 per-package versioned sections matching the file's existing convention, since that work is now actually released. [Unreleased] stays as an empty header for the next round. - README.es.md, CHANGELOG.es.md: new, full Spanish translations of both, with a language-switcher line at the top of all four files. --- CHANGELOG.es.md | 288 ++++++++++++++++++++++++++++++ CHANGELOG.md | 161 +++++++++++++++-- README.es.md | 465 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 24 +-- 4 files changed, 909 insertions(+), 29 deletions(-) create mode 100644 CHANGELOG.es.md create mode 100644 README.es.md diff --git a/CHANGELOG.es.md b/CHANGELOG.es.md new file mode 100644 index 0000000..aca2117 --- /dev/null +++ b/CHANGELOG.es.md @@ -0,0 +1,288 @@ +# Changelog + +[English](CHANGELOG.md) | **Español** + +Todos los cambios relevantes del ecosistema Vali-Flow se documentan en este archivo. + +El formato sigue [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) y este proyecto sigue [Versionado Semántico](https://semver.org/spec/v2.0.0.html). + +--- + +## [Unreleased] + +--- + +## Vali-Flow.Abstractions — 1.2.0 + +### Agregado + +- **`ValiFlowDiagnostics`** — helper compartido basado en `ActivitySource` con `StartActivity`/`RecordException` para trazabilidad compatible con OpenTelemetry, ahora conectado en el evaluador de EF Core, `Vali-Flow.InMemory`, los seis builders de `Vali-Flow.Sql`, y los traductores de Mongo/DynamoDB/Elasticsearch/Redis. + +--- + +## Vali-Flow — 1.4.0 + +### Agregado + +- **`GenericRepository`** — un repositorio ligero que envuelve a `ValiFlowEvaluator` con `GetById`/`GetAll`/`GetPaged`/`Add`/`Update`/`Delete`/`SaveChanges`, para consumidores que prefieren una superficie de repositorio convencional en lugar de llamar al evaluador directamente. +- Benchmark de EF Core usando el proveedor `EFCore.InMemory` (`Vali-Flow.Benchmarks`). +- Trazabilidad de `ValiFlowDiagnostics` conectada en las rutas de lectura/escritura del evaluador. + +### Corregido + +- `UpsertRangeAsync` perdía entradas con claves duplicadas (usaba un acumulador de lista en lugar de un diccionario) — corregido a un acumulador de diccionario. +- `UpsertRangeAsync.PartitionUpsertRange` podía descartar la clave primaria de una entidad rastreada — ahora se preserva. +- `BulkInsertOrUpdateAsync` podía entrar en condición de carrera bajo llamadas concurrentes para el mismo tipo de entidad + columnas clave — las llamadas ahora se serializan por esa combinación. +- **Contención en upsert masivo bajo concurrencia**: se reemplazó el retry ad-hoc por una política de retry transitorio genérica (`BulkUpsertRetryPolicy`) que cubre SQL Server, PostgreSQL, MySQL y SQLite, controlada por tabla (no por el conjunto de claves de `UpdateByProperties`) para evitar serializar upserts no relacionados entre sí. El presupuesto de reintentos se aumentó en dos pasos (4 → 7 → 12 intentos) según la contención observada bajo carga. +- Se eliminó una prueba inestable de captura de `Activity` (la aserción dependiente del tiempo fue reemplazada por una captura determinista). + +### Cambiado + +- La lógica de construcción de consultas se extrajo de `ValiFlowEvaluator` hacia un `QuerySpecificationBuilder` dedicado (separación por responsabilidad única, sin cambios en la API pública). +- La cobertura de pruebas subió de 74.37% a 97.42%. + +--- + +## Vali-Flow.InMemory — 1.2.0 + +### Agregado + +- Trazabilidad de `ValiFlowDiagnostics` conectada en las rutas de lectura/escritura del evaluador. + +### Corregido + +- `Update(entity, externalList)` podía filtrar la entidad actualizada hacia el almacén interno en una llamada posterior a `SaveChanges()` sin argumentos. +- Se eliminó una prueba inestable de captura de `Activity` (la aserción dependiente del tiempo fue reemplazada por una captura determinista). + +### Cambiado + +- `ValiFlowEvaluator` (904 líneas) se dividió en archivos parciales `Bridge`/`Read`/`Write`/`Grouped`; sin cambios en la API pública. + +--- + +## Vali-Flow.Sql — 1.2.0 + +### Agregado + +- `SqlInsertBuilder`/`SqlUpdateBuilder.SetAllFrom(entity, exclude...)` — mapeador masivo de columnas basado en reflexión. +- Trazabilidad de `ValiFlowDiagnostics` conectada en los seis builders. + +### Corregido + +- **Seguridad — inyección de identificadores SQL**: los builders interpolaban directamente nombres de tabla/esquema/tipo; se agregó `SqlIdentifierGuard` (lista blanca por expresión regular) que valida todos los identificadores antes de interpolarlos. +- Error de guardia de dialecto en `OrIgnore`/`OrReplace`, pérdida silenciosa de datos al llamar `Set()` después de `SelectFrom()`, y llamada doble a `Where()` que sobrescribía silenciosamente la primera condición en lugar de combinarla — todo corregido. +- Se agregó la guardia `AllowDeleteUnmatched()`, requerida antes de `WhenNotMatchedBySourceDelete` en `SqlMergeBuilder` (evita eliminaciones no controladas accidentales en un MERGE). +- Se eliminó una prueba inestable de captura de `Activity` (la aserción dependiente del tiempo fue reemplazada por una captura determinista). + +--- + +## Vali-Flow.NoSql — 1.1.1 + +### Agregado + +- Trazabilidad de `ValiFlowDiagnostics` conectada en la base de traducción compartida usada por los proveedores Mongo y Elasticsearch. + +### Corregido + +- Los valores `null` capturados por closure en una condición `ValiFlow` no se traducían al mismo `NullNode` que un `null` literal, produciendo filtros inconsistentes entre `.EqualTo(x => x.Prop, someNullVariable)` y `.EqualTo(x => x.Prop, null)`. +- Las listas de valores `IN` sin límite podían exceder los límites propios del motor; se agregaron topes de `MaxInValues` (Mongo 10,000; Elasticsearch 65,536, en línea con `index.max_terms_count`). + +--- + +## Vali-Flow.NoSql.MongoDB — 1.2.0 + +### Agregado + +- Trazabilidad de `ValiFlowDiagnostics` conectada en `MongoFilterTranslator`. + +--- + +## Vali-Flow.NoSql.Elasticsearch — 1.2.0 + +### Agregado + +- Trazabilidad de `ValiFlowDiagnostics` conectada en `ElasticsearchFilterTranslator`. + +### Corregido + +- Excepción interna silenciada en la conversión `ToDouble` — los fallos se mostraban como un error genérico sin causa raíz. + +--- + +## Vali-Flow.NoSql.Redis — 1.2.0 + +### Agregado + +- Trazabilidad de `ValiFlowDiagnostics` conectada en `RedisSearchFilterTranslator`. + +### Corregido + +- **Seguridad — inyección de consultas RediSearch**: los caracteres especiales de la sintaxis de consulta (p. ej. `)`/`|`) no se escapaban en los patrones `LIKE` antes de envolverlos en comodines, lo que permitía inyección de estructura de consulta. Ahora se escapan antes de aplicar los comodines. +- `customConverter` no se aplicaba en `VisitComparison`/`VisitIn` numérico, ignorando silenciosamente las conversiones de tipo personalizadas en condiciones de comparación e `IN`. + +--- + +## Vali-Flow.NoSql.DynamoDB — 1.2.0 + +### Agregado + +- Trazabilidad de `ValiFlowDiagnostics` conectada en `DynamoFilterTranslator`. + +--- + +## Vali-Flow.NoSql.Couchbase — 1.1.0 *(lanzamiento inicial)* + +### Agregado + +- `CouchbaseFilterTranslator` — traduce nodos IR de `ValiFlow` a un fragmento WHERE en N1QL/SQL++, devuelto como `CouchbaseFilterExpression { WhereClause, Parameters }` — sin dependencia del SDK de Couchbase; el consumidor aplica el fragmento con su propio cliente. +- `ValiFlowCouchbaseExtensions` — método de extensión sobre `ValiFlow`. + +### Corregido + +- Los valores `decimal` se vinculaban como literales de cadena en N1QL en lugar de números nativos. + +--- + +## Vali-Flow.NoSql.CosmosDb — 1.1.0 *(lanzamiento inicial)* + +### Agregado + +- `CosmosFilterTranslator` — traduce nodos IR de `ValiFlow` a un fragmento WHERE de la SQL API de Azure Cosmos DB, devuelto como `CosmosFilterExpression { WhereClause, Parameters }` — sin dependencia de `Microsoft.Azure.Cosmos`; el consumidor aplica el fragmento con su propio cliente. +- `ValiFlowCosmosExtensions` — método de extensión sobre `ValiFlow`. + +--- + +## Vali-Flow.NoSql.Firestore — 1.1.0 *(lanzamiento inicial)* + +### Agregado + +- `FirestoreFilterTranslator` — traduce nodos IR de `ValiFlow` a un `Filter` nativo de Google Cloud Firestore, dependiendo del SDK oficial `Google.Cloud.Firestore`. +- `ValiFlowFirestoreExtensions` — método de extensión sobre `ValiFlow`. +- Nota: `LikeNode` (los 3 operadores) y el `NotNode` genérico lanzan `NotSupportedException` — el SDK de Firestore no tiene consulta por coincidencia de patrones ni NOT sobre un sub-filtro arbitrario, solo negación a nivel de campo (`EqualNode.IsNegated` → `NotEqualTo`/`NotInArray`). + +--- + +## Vali-Flow — 1.3.3 + +### Corregido +- Se eliminó el modificador `sealed` de todas las declaraciones parciales de `ValiFlowEvaluator`: + - ValiFlowEvaluator.cs (principal) + - ValiFlowEvaluator.Read.cs + - ValiFlowEvaluator.Write.cs + - ValiFlowEvaluator.Aggregates.cs + - ValiFlowEvaluator.Grouped.cs + - ValiFlowEvaluator.Bridge.cs + +--- + +## Vali-Flow.InMemory — 1.1.4 + +### Corregido +- Se eliminó el modificador `sealed` de todas las clases para habilitar herencia completa: + - `ValiFlowEvaluator` (evaluador principal) + - `AsyncInMemoryAdapter` (envoltorio async) + - `InMemoryWriteStore` (almacén de operaciones de escritura) + - `PagedResult` (modelo de resultado de paginación) + +Los usuarios ahora pueden crear implementaciones personalizadas extendiendo cualquiera de estas clases. + +--- + +## Vali-Flow — 1.1.0 + +### Agregado +- `EvaluatePagedAsync` devuelve un `PagedResult` con `Items`, `TotalCount`, `TotalPages`, `HasNextPage` y `HasPreviousPage` +- `EvaluateTopByGroupAsync` — entidades top-N por clave de grupo +- `EvaluateDistinctAsync` y `EvaluateDuplicatesAsync` — detección de distintos y duplicados por selector de clave +- `EvaluateAggregateAsync` — agregación genérica sobre cualquier selector numérico +- `EvaluateGroupedAsync`, `EvaluateCountByGroupAsync`, `EvaluateSumByGroupAsync`, `EvaluateMinByGroupAsync`, `EvaluateMaxByGroupAsync`, `EvaluateAverageByGroupAsync` — superficie completa de agregados agrupados +- `ExecuteUpdateAsync` — actualización masiva in-place vía `ExecuteUpdate` (sin cargar entidades) +- `ExecuteTransactionAsync` — envuelve múltiples operaciones en una sola transacción de EF Core +- `BulkInsertAsync`, `BulkUpdateAsync`, `BulkDeleteAsync`, `BulkInsertOrUpdateAsync` — operaciones masivas de alto rendimiento vía `EFCore.BulkExtensions` +- `UpsertRangeAsync` — upsert por lotes con selector de clave configurable +- `DeleteByConditionAsync` — eliminación por predicado directo sin cargar entidades +- `EvaluateGetLastAsync` / `EvaluateGetLastFailedAsync` — recuperación de la última coincidencia +- `QuerySpecification.WithTop`, `WithPagination`, `WithOrderBy`, `AddThenBy`, `WithValiSort` — builder fluido de especificación completo +- `BasicSpecification` — especificación simplificada sin ordenamiento/paginación +- Documentación XML en todos los tipos y miembros públicos + +### Cambiado +- `ValiFlowEvaluator` ahora es una `sealed partial class` dividida en archivos enfocados (Read, Write, Grouped, Aggregates, Bridge) +- Mensajes de excepción mejorados para combinaciones de especificación inválidas (Top + Pagination, Pagination sin OrderBy) +- `EvaluateAllAsync` y `EvaluateAllFailedAsync` marcados `[Obsolete]` — usar `EvaluateQueryAsync` / `EvaluateQueryFailedAsync` +- La licencia cambió de Apache-2.0 a MIT + +### Corregido +- Problemas de seguridad de hilos (thread-safety) en uso concurrente del evaluador +- Violaciones de caché y DRY en la ejecución diferida de `UpsertCore` +- Advertencias de nulabilidad CS87xx en las clases del evaluador y de especificación + +--- + +## Vali-Flow.InMemory — 1.0.0 *(lanzamiento inicial)* + +### Agregado +- `ValiFlowEvaluator` — evaluador síncrono en memoria sobre `IEnumerable` +- Superficie de lectura completa: `Evaluate`, `EvaluateAny`, `EvaluateCount`, `GetFirst`, `GetLast`, `EvaluateAll`, `EvaluateAllFailed`, `EvaluatePaged`, `EvaluatePagedResult`, `EvaluateTop`, `EvaluateDistinct`, `EvaluateDuplicates`, `GetFirstMatchIndex`, `GetLastMatchIndex` +- Superficie de agregados completa: `EvaluateMin`, `EvaluateMax`, `EvaluateAverage`, `EvaluateSum`, `EvaluateAggregate` +- Superficie agrupada completa: `EvaluateGrouped`, `EvaluateCountByGroup`, `EvaluateSumByGroup`, `EvaluateMinByGroup`, `EvaluateMaxByGroup`, `EvaluateAverageByGroup`, `EvaluateDuplicatesByGroup`, `EvaluateUniquesByGroup`, `EvaluateTopByGroup` +- Superficie de escritura completa: `Add`, `Update`, `Delete`, `AddRange`, `UpdateRange`, `DeleteRange`, `Upsert`, `UpsertRange`, `DeleteByCondition`, `SaveChanges` +- `SetValiFlow` — reemplaza el filtro activo en tiempo de ejecución +- `InMemoryWriteStore` — almacén de escritura extraído para facilitar pruebas y cumplir SRP +- Documentación XML en todos los tipos y miembros públicos + +--- + +## Vali-Flow.Sql — 1.0.0 *(lanzamiento inicial)* + +### Agregado +- `SqlQueryBuilder` — builder fluido de SELECT con JOINs, WHERE, GROUP BY, HAVING, ORDER BY, UNION, EXISTS, CASE WHEN, paginación, hints de consulta +- `SqlInsertBuilder` — INSERT con mapeo de columnas y soporte multi-fila +- `SqlUpdateBuilder` — UPDATE con cláusulas SET y condiciones WHERE +- `SqlDeleteBuilder` — DELETE con condiciones WHERE +- `SqlTruncateBuilder` — TRUNCATE TABLE +- `SqlMergeBuilder` — builder de instrucciones MERGE / UPSERT +- `SqlWhereBuilder` — builder independiente de cláusula WHERE con encadenamiento fluido AND/OR +- `SqlHavingBuilder` — builder independiente de cláusula HAVING +- `CaseWhenBuilder` — builder fluido de CASE WHEN / THEN / ELSE +- `SqlExpressionTranslator` — convierte árboles de expresión de `ValiFlow` a SQL parametrizado +- `SqlResult` — contiene la cadena SQL generada y el diccionario de parámetros; `ApplyTo(IDbCommand)` aplica los parámetros a comandos ADO.NET +- `SqlQueryResult` — combina `SqlResult` con metadatos de paginación +- Soporte de dialectos: `SqlServerDialect`, `PostgreSqlDialect`, `MySqlDialect`, `SqliteDialect`, `OracleDialect` +- `ValiFlowSqlExtensions.ToSql(dialect)` — método de extensión sobre `ValiFlow` +- Documentación XML en todos los tipos y miembros públicos + +--- + +## Vali-Flow.NoSql — incluido con los paquetes proveedores + +### Vali-Flow.NoSql.MongoDB — 1.0.0 *(lanzamiento inicial)* +- `MongoFilterTranslator` — traduce nodos IR a `FilterDefinition` de MongoDB +- `ValiFlowMongoExtensions.ToMongoFilter()` — extensión sobre `ValiFlow` + +### Vali-Flow.NoSql.DynamoDB — 1.0.0 *(lanzamiento inicial)* +- `DynamoFilterTranslator` — traduce nodos IR a `FilterExpression` + `ExpressionAttributeValues` de DynamoDB +- `DynamoFilterExpression` — contiene la cadena de expresión y el mapa de atributos +- `ValiFlowDynamoExtensions.ToDynamoFilter()` — extensión sobre `ValiFlow` + +### Vali-Flow.NoSql.Elasticsearch — 1.0.0 *(lanzamiento inicial)* +- `ElasticsearchFilterTranslator` — traduce nodos IR a `Query` de Elasticsearch (Elastic.Clients.Elasticsearch) +- `ValiFlowElasticsearchExtensions.ToElasticsearchQuery()` — extensión sobre `ValiFlow` + +### Vali-Flow.NoSql.Redis — 1.0.0 *(lanzamiento inicial)* +- `RedisSearchFilterTranslator` — traduce nodos IR a cadenas de consulta RediSearch +- `ValiFlowRedisSearchExtensions.ToRedisQuery()` — extensión sobre `ValiFlow` + +--- + +## Vali-Flow — 1.0.0 *(lanzamiento inicial)* + +### Agregado +- `ValiFlowEvaluator` — evaluador async de EF Core que implementa `IEvaluatorRead` e `IEvaluatorWrite` +- `BasicSpecification` y `QuerySpecification` — patrón de especificación con API de builder fluido +- `EvaluateAsync`, `EvaluateAnyAsync`, `EvaluateCountAsync`, `EvaluateGetFirstAsync`, `EvaluateGetLastAsync`, `EvaluateQueryAsync`, `EvaluateQueryFailedAsync` +- `AddAsync`, `UpdateAsync`, `DeleteAsync`, `AddRangeAsync`, `UpdateRangeAsync`, `DeleteRangeAsync`, `UpsertAsync`, `SaveChangesAsync` +- `IEvaluatorRead`, `IEvaluatorWrite`, `ISpecification`, `IBasicSpecification`, `IQuerySpecification` +- `EfInclude`, `EfOrderBy`, `EfOrderThenBy` — envoltorios de opciones de EF Core +- `PagedResult` — modelo de resultado de paginación +- Dependencia de `Vali-Flow.Core` para la construcción de árboles de expresión (`ValiFlow`, `ValiSort`) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08584d8..30b6ef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +**English** | [Español](CHANGELOG.es.md) + All notable changes to the Vali-Flow ecosystem are documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). @@ -8,34 +10,155 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and ## [Unreleased] +--- + +## Vali-Flow.Abstractions — 1.2.0 + +### Added + +- **`ValiFlowDiagnostics`** — shared `ActivitySource`-based `StartActivity`/`RecordException` helper for OpenTelemetry-compatible tracing, now wired through the EF Core evaluator, `Vali-Flow.InMemory`, `Vali-Flow.Sql`'s six builders, and the Mongo/DynamoDB/Elasticsearch/Redis translators. + +--- + +## Vali-Flow — 1.4.0 + ### Added -- **`Vali-Flow.NoSql.Couchbase`, `Vali-Flow.NoSql.CosmosDb`, `Vali-Flow.NoSql.Firestore`** — three new NoSQL translator packages, same pattern as the existing MongoDB/DynamoDB/Elasticsearch/Redis adapters (pure `ValiFlow` expression tree → native query/filter translators, no connection or execution concerns). -- **`GenericRepository`** (`Vali-Flow`) — a thin repository wrapping `ValiFlowEvaluator` with `GetById`/`GetAll`/`GetPaged`/`Add`/`Update`/`Delete`/`SaveChanges`, for consumers who want a conventional repository surface instead of calling the evaluator directly. -- **`ValiFlowDiagnostics`** (`Vali-Flow.Abstractions`) — shared `ActivitySource`-based `StartActivity`/`RecordException` helper for OpenTelemetry-compatible tracing, now wired through the EF Core evaluator, `Vali-Flow.InMemory`, `Vali-Flow.Sql`'s six builders, and the Mongo/DynamoDB/Elasticsearch/Redis translators. +- **`GenericRepository`** — a thin repository wrapping `ValiFlowEvaluator` with `GetById`/`GetAll`/`GetPaged`/`Add`/`Update`/`Delete`/`SaveChanges`, for consumers who want a conventional repository surface instead of calling the evaluator directly. - EF Core benchmark using the `EFCore.InMemory` provider (`Vali-Flow.Benchmarks`). -- `SqlInsertBuilder`/`SqlUpdateBuilder.SetAllFrom(entity, exclude...)` — reflection-based bulk column mapper. +- `ValiFlowDiagnostics` tracing wired into the evaluator's read/write paths. + +### Fixed + +- `UpsertRangeAsync` dropped entries on duplicate keys (list accumulator instead of dictionary) — fixed to a dictionary accumulator. +- `UpsertRangeAsync.PartitionUpsertRange` could discard a tracked entity's primary key — now preserved. +- `BulkInsertOrUpdateAsync` could race under concurrent calls for the same entity type + key columns — calls are now serialized per that combination. +- **Bulk upsert contention under concurrency**: replaced the ad-hoc retry with a generic transient-retry policy (`BulkUpsertRetryPolicy`) covering SQL Server, PostgreSQL, MySQL, and SQLite, gated per table (not per `UpdateByProperties` key set) to avoid serializing unrelated upserts against each other. Retry budget raised in two steps (4 → 7 → 12 attempts) based on observed contention under load. +- Eliminated a flaky `Activity`-capture test (timing-dependent assertion replaced with deterministic capture). + +### Changed + +- Query-construction logic extracted from `ValiFlowEvaluator` into a dedicated `QuerySpecificationBuilder` (single-responsibility split, no public API change). +- Test coverage raised from 74.37% to 97.42%. + +--- + +## Vali-Flow.InMemory — 1.2.0 + +### Added + +- `ValiFlowDiagnostics` tracing wired into the evaluator's read/write paths. ### Fixed -- **Security — SQL identifier injection**: `Vali-Flow.Sql`'s builders interpolated table/schema/type names directly; added `SqlIdentifierGuard` (regex whitelist) validating all identifiers before interpolation. -- **Security — RediSearch query injection**: `Vali-Flow.NoSql.Redis` didn't escape RediSearch query-syntax special characters (e.g. `)`/`|`) in `LIKE` patterns before wrapping them in wildcards, allowing query-structure injection. Now escaped before wildcarding. -- **`Vali-Flow.NoSql` (Mongo/Elasticsearch)**: closure-captured `null` values in a `ValiFlow` condition weren't translated to the same `NullNode` as a literal `null`, producing inconsistent filters between `.EqualTo(x => x.Prop, someNullVariable)` and `.EqualTo(x => x.Prop, null)`. -- **`Vali-Flow.NoSql.Redis`**: `customConverter` wasn't applied in `VisitComparison`/numeric `VisitIn`, silently ignoring custom type mappings for comparison and `IN` conditions. -- **`Vali-Flow.NoSql.Elasticsearch`**: swallowed inner exception in `ToDouble` conversion — failures surfaced as a generic error with no root cause. -- **`Vali-Flow.NoSql` (Mongo/Elasticsearch)**: unbounded `IN` value lists could exceed the engine's own limits; added `MaxInValues` caps (Mongo 10,000; Elasticsearch 65,536, matching `index.max_terms_count`). -- **`Vali-Flow.NoSql.Couchbase`**: `decimal` values were bound as N1QL string literals instead of native numbers. -- **`Vali-Flow.Sql`**: `OrIgnore`/`OrReplace` dialect guard bug, `Set()`-after-`SelectFrom()` silent data loss, `Where()` double-call silently overwriting the first condition instead of combining — all fixed. Added `AllowDeleteUnmatched()` guard required before `WhenNotMatchedBySourceDelete` in `SqlMergeBuilder` (prevents accidental unguarded deletes in a MERGE). -- **`Vali-Flow` (EF Core evaluator)**: `UpsertRangeAsync` dropped entries on duplicate keys (list accumulator instead of dictionary) — fixed to a dictionary accumulator. `UpsertRangeAsync.PartitionUpsertRange` could discard a tracked entity's primary key — now preserved. `BulkInsertOrUpdateAsync` could race under concurrent calls for the same entity type + key columns — calls are now serialized per that combination. -- **`Vali-Flow` (EF Core evaluator) — bulk upsert contention under concurrency**: replaced the ad-hoc retry with a generic transient-retry policy (`BulkUpsertRetryPolicy`) covering SQL Server, PostgreSQL, MySQL, and SQLite, gated per table (not per `UpdateByProperties` key set) to avoid serializing unrelated upserts against each other. Retry budget raised in two steps (4 → 7 → 12 attempts) based on observed contention under load. -- **`Vali-Flow.InMemory`**: `Update(entity, externalList)` could leak the updated entity into the internal store on a later bare `SaveChanges()` call. +- `Update(entity, externalList)` could leak the updated entity into the internal store on a later bare `SaveChanges()` call. +- Eliminated a flaky `Activity`-capture test (timing-dependent assertion replaced with deterministic capture). ### Changed -- **`Vali-Flow.InMemory`**: `ValiFlowEvaluator` (904 lines) split into `Bridge`/`Read`/`Write`/`Grouped` partial files; no public API change. -- **`Vali-Flow` (EF Core evaluator)**: query-construction logic extracted from `ValiFlowEvaluator` into a dedicated `QuerySpecificationBuilder` (single-responsibility split, no public API change). -- EF Core evaluator test coverage raised from 74.37% to 97.42%. -- Eliminated flaky `Activity`-capture tests across the `Sql`/EF-Core/`InMemory` test assemblies (timing-dependent assertions replaced with deterministic capture). +- `ValiFlowEvaluator` (904 lines) split into `Bridge`/`Read`/`Write`/`Grouped` partial files; no public API change. + +--- + +## Vali-Flow.Sql — 1.2.0 + +### Added + +- `SqlInsertBuilder`/`SqlUpdateBuilder.SetAllFrom(entity, exclude...)` — reflection-based bulk column mapper. +- `ValiFlowDiagnostics` tracing wired into all six builders. + +### Fixed + +- **Security — SQL identifier injection**: the builders interpolated table/schema/type names directly; added `SqlIdentifierGuard` (regex whitelist) validating all identifiers before interpolation. +- `OrIgnore`/`OrReplace` dialect guard bug, `Set()`-after-`SelectFrom()` silent data loss, `Where()` double-call silently overwriting the first condition instead of combining — all fixed. +- Added `AllowDeleteUnmatched()` guard required before `WhenNotMatchedBySourceDelete` in `SqlMergeBuilder` (prevents accidental unguarded deletes in a MERGE). +- Eliminated a flaky `Activity`-capture test (timing-dependent assertion replaced with deterministic capture). + +--- + +## Vali-Flow.NoSql — 1.1.1 + +### Added + +- `ValiFlowDiagnostics` tracing wired into the shared translator base used by the Mongo and Elasticsearch providers. + +### Fixed + +- Closure-captured `null` values in a `ValiFlow` condition weren't translated to the same `NullNode` as a literal `null`, producing inconsistent filters between `.EqualTo(x => x.Prop, someNullVariable)` and `.EqualTo(x => x.Prop, null)`. +- Unbounded `IN` value lists could exceed the engine's own limits; added `MaxInValues` caps (Mongo 10,000; Elasticsearch 65,536, matching `index.max_terms_count`). + +--- + +## Vali-Flow.NoSql.MongoDB — 1.2.0 + +### Added + +- `ValiFlowDiagnostics` tracing wired into `MongoFilterTranslator`. + +--- + +## Vali-Flow.NoSql.Elasticsearch — 1.2.0 + +### Added + +- `ValiFlowDiagnostics` tracing wired into `ElasticsearchFilterTranslator`. + +### Fixed + +- Swallowed inner exception in `ToDouble` conversion — failures surfaced as a generic error with no root cause. + +--- + +## Vali-Flow.NoSql.Redis — 1.2.0 + +### Added + +- `ValiFlowDiagnostics` tracing wired into `RedisSearchFilterTranslator`. + +### Fixed + +- **Security — RediSearch query injection**: special query-syntax characters (e.g. `)`/`|`) weren't escaped in `LIKE` patterns before wrapping them in wildcards, allowing query-structure injection. Now escaped before wildcarding. +- `customConverter` wasn't applied in `VisitComparison`/numeric `VisitIn`, silently ignoring custom type mappings for comparison and `IN` conditions. + +--- + +## Vali-Flow.NoSql.DynamoDB — 1.2.0 + +### Added + +- `ValiFlowDiagnostics` tracing wired into `DynamoFilterTranslator`. + +--- + +## Vali-Flow.NoSql.Couchbase — 1.1.0 *(initial release)* + +### Added + +- `CouchbaseFilterTranslator` — translates `ValiFlow` IR nodes to a N1QL/SQL++ WHERE fragment, returned as `CouchbaseFilterExpression { WhereClause, Parameters }` — no Couchbase SDK dependency, the consumer applies the fragment with their own client. +- `ValiFlowCouchbaseExtensions` — extension method on `ValiFlow`. + +### Fixed + +- `decimal` values were bound as N1QL string literals instead of native numbers. + +--- + +## Vali-Flow.NoSql.CosmosDb — 1.1.0 *(initial release)* + +### Added + +- `CosmosFilterTranslator` — translates `ValiFlow` IR nodes to an Azure Cosmos DB SQL API WHERE fragment, returned as `CosmosFilterExpression { WhereClause, Parameters }` — no `Microsoft.Azure.Cosmos` dependency, the consumer applies the fragment with their own client. +- `ValiFlowCosmosExtensions` — extension method on `ValiFlow`. + +--- + +## Vali-Flow.NoSql.Firestore — 1.1.0 *(initial release)* + +### Added + +- `FirestoreFilterTranslator` — translates `ValiFlow` IR nodes to a native Google Cloud Firestore `Filter`, depending on the official `Google.Cloud.Firestore` SDK. +- `ValiFlowFirestoreExtensions` — extension method on `ValiFlow`. +- Note: `LikeNode` (all 3 ops) and the generic `NotNode` throw `NotSupportedException` — the Firestore SDK has no pattern-matching query and no NOT over an arbitrary sub-filter, only field-level negation (`EqualNode.IsNegated` → `NotEqualTo`/`NotInArray`). --- diff --git a/README.es.md b/README.es.md new file mode 100644 index 0000000..3435b43 --- /dev/null +++ b/README.es.md @@ -0,0 +1,465 @@ +# Vali-Flow + +[English](README.md) | **Español** + +## Descripción general + +Vali-Flow es un ecosistema completo de librerías .NET para construir criterios de consulta reutilizables y componibles mediante una API fluida. Permite definir la lógica de filtrado una sola vez usando un DSL simple basado en expresiones, y traducirla a: + +- Consultas de **Entity Framework Core** (async) +- **SQL parametrizado** para Dapper / ADO.NET (SQL Server, PostgreSQL, MySQL, SQLite) +- Filtros **MongoDB BSON** +- **Elasticsearch Query DSL** +- Consultas **Redis (RediSearch)** +- Expresiones de filtro de **AWS DynamoDB** +- Evaluación **en memoria** (LINQ-to-Objects) + +Todo construido sobre **Vali-Flow.Core** — un constructor de expresiones ligero, sin dependencias adicionales. + +**Plataformas soportadas:** .NET 8.0, .NET 9.0 — todos los paquetes también corren sin modificaciones en **.NET 10** gracias a la compatibilidad hacia adelante de .NET (una librería construida para un TFM anterior funciona bien en un runtime más nuevo). + +--- + +## El problema que resuelve + +Cuando se trabaja con múltiples almacenes de datos o patrones de ORM, es habitual dispersar la lógica de filtrado entre repositorios, duplicar predicados por cada almacén, o acoplar la lógica de negocio al código de acceso a datos: + +```csharp +// ❌ Enfoque tradicional: la lógica de filtrado está dispersa +public async Task> GetActiveOrdersEF(DbContext db, decimal minTotal) +{ + return await db.Orders + .Where(o => o.Status == "Active" && o.Total > minTotal) + .ToListAsync(); +} + +public List GetActiveOrdersMongo(IMongoCollection coll, decimal minTotal) +{ + return coll.Find(Builders.Filter.And( + Builders.Filter.Eq(o => o.Status, "Active"), + Builders.Filter.Gt(o => o.Total, minTotal) + )).ToList(); +} + +public DataTable GetActiveOrdersSQL(SqlConnection conn, decimal minTotal) +{ + var cmd = new SqlCommand( + "SELECT * FROM Orders WHERE Status = @status AND Total > @total", conn); + cmd.Parameters.AddWithValue("@status", "Active"); + cmd.Parameters.AddWithValue("@total", minTotal); + // ... +} +``` + +**Con Vali-Flow:** + +```csharp +// ✅ Una sola definición de filtro +var filter = new ValiFlow() + .EqualTo(x => x.Status, "Active") + .GreaterThan(x => x.Total, minTotal); + +// Usa el mismo filtro en todas partes +var efOrders = await new ValiFlowEvaluator(dbContext) + .EvaluateQueryAsync(new BasicSpecification().WithFilter(filter)); + +var mongoOrders = mongoCollection.Find(filter.ToMongo()).ToList(); + +var sqlResult = filter.ToSql(new SqlServerDialect()); +var sqlOrders = await connection.QueryAsync( + $"SELECT * FROM Orders WHERE {sqlResult.Sql}", + sqlResult.Parameters); +``` + +--- + +## Ecosistema de paquetes + +### Paquete principal + +| Paquete | Propósito | Versión | +|---------|---------|---------| +| **Vali-Flow.Core** | Constructor de expresiones fluido (`ValiFlow`) - compartido por todos los paquetes | [2.0.0](https://www.nuget.org/packages/Vali-Flow.Core) | +| **Vali-Flow.Abstractions** | Contratos agnósticos de proveedor compartidos por todos los paquetes (`IQueryEvaluator`, `IExpressionTranslator`, `ExpressionInspector`) más `ValiFlowDiagnostics`, un helper de trazabilidad basado en `ActivitySource` compatible con OpenTelemetry | [1.2.0](https://www.nuget.org/packages/Vali-Flow.Abstractions) | + +### Paquetes de acceso a datos + +| Paquete | Propósito | Destino | Versión | +|---------|---------|--------|---------| +| **Vali-Flow** | Evaluador async de EF Core + especificaciones (lectura/escritura) | `DbContext` | [1.4.0](https://www.nuget.org/packages/Vali-Flow) | +| **Vali-Flow.InMemory** | Evaluador síncrono en memoria para pruebas y caché | `IEnumerable` | [1.2.0](https://www.nuget.org/packages/Vali-Flow.InMemory) | +| **Vali-Flow.Sql** | Constructor de consultas SQL parametrizadas | Dapper / ADO.NET | [1.2.0](https://www.nuget.org/packages/Vali-Flow.Sql) | + +### Paquetes NoSQL + +| Paquete | Base de datos | Tipo de salida | Versión | +|---------|----------|-------------|---------| +| **Vali-Flow.NoSql** | IR y base de traducción NoSQL compartida, usada por todos los paquetes proveedor listados abajo | — | [1.1.1](https://www.nuget.org/packages/Vali-Flow.NoSql) | +| **Vali-Flow.NoSql.MongoDB** | MongoDB | `BsonDocument` | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.MongoDB) | +| **Vali-Flow.NoSql.Elasticsearch** | Elasticsearch | `Query` (Elastic.Clients) | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Elasticsearch) | +| **Vali-Flow.NoSql.Redis** | Redis (RediSearch) | Cadena de consulta | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Redis) | +| **Vali-Flow.NoSql.DynamoDB** | AWS DynamoDB | `DynamoFilterExpression` | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.DynamoDB) | +| **Vali-Flow.NoSql.Couchbase** | Couchbase | Fragmento WHERE en N1QL + parámetros | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Couchbase) | +| **Vali-Flow.NoSql.CosmosDb** | Azure Cosmos DB (SQL API) | Fragmento WHERE en SQL + parámetros | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.CosmosDb) | +| **Vali-Flow.NoSql.Firestore** | Google Cloud Firestore | `Filter` (Google.Cloud.Firestore) | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Firestore) | + +### Arquitectura + +``` +Vali-Flow.Core (constructor de expresiones — ValiFlow) + │ + ├─── Vali-Flow (EF Core async) + ├─── Vali-Flow.InMemory (en memoria, síncrono) + ├─── Vali-Flow.Sql (SQL: SQL Server, PostgreSQL, MySQL, SQLite, Oracle) + │ + └─── Vali-Flow.NoSql + ├─── Vali-Flow.NoSql.MongoDB (MongoDB BSON) + ├─── Vali-Flow.NoSql.Elasticsearch (Elasticsearch Query DSL) + ├─── Vali-Flow.NoSql.Redis (RediSearch) + ├─── Vali-Flow.NoSql.DynamoDB (expresiones de filtro de DynamoDB) + ├─── Vali-Flow.NoSql.Couchbase (N1QL / SQL++) + ├─── Vali-Flow.NoSql.CosmosDb (Cosmos DB SQL API) + └─── Vali-Flow.NoSql.Firestore (Filter nativo de Firestore) +``` + +--- + +## Inicio rápido + +### 1. Definir un filtro una sola vez + +```csharp +using Vali_Flow.Core; + +var filter = new ValiFlow() + .EqualTo(x => x.Status, "Active") + .GreaterThan(x => x.Total, 100m) + .IsAfter(x => x.CreatedAt, DateTime.UtcNow.AddDays(-30)); +``` + +### 2. Usarlo con EF Core + +```csharp +using Vali_Flow; + +var evaluator = new ValiFlowEvaluator(dbContext); +var spec = new BasicSpecification() + .WithFilter(filter) + .WithAsNoTracking(true) + .AddInclude(x => x.Customer); + +var orders = await evaluator.EvaluateQueryAsync(spec, cancellationToken); +``` + +### 3. O con SQL/Dapper + +```csharp +using Vali_Flow.Sql.Extensions; +using Vali_Flow.Sql.Dialects; + +var result = filter.ToSql(new PostgreSqlDialect()); + +var orders = await connection.QueryAsync( + $"SELECT * FROM orders WHERE {result.Sql}", + result.Parameters); +``` + +### 4. O con MongoDB + +```csharp +using Vali_Flow.NoSql.MongoDB.Extensions; + +var bsonFilter = filter.ToMongo(); +var orders = await collection.Find(bsonFilter).ToListAsync(); +``` + +### 5. O en memoria (pruebas) + +```csharp +using Vali_Flow.InMemory; + +var evaluator = new ValiFlowEvaluator(orders, null, x => x.Id); +var filtered = evaluator.EvaluateAll( + orders, + orderBy: x => x.CreatedAt, + valiFlow: filter); +``` + +--- + +## Instalación + +**Instala el o los paquetes que necesites:** + +```bash +# EF Core (producción) +dotnet add package Vali-Flow + +# En memoria (pruebas / caché) +dotnet add package Vali-Flow.InMemory + +# Consultas SQL (Dapper / ADO.NET) +dotnet add package Vali-Flow.Sql + +# MongoDB +dotnet add package Vali-Flow.NoSql.MongoDB + +# Elasticsearch +dotnet add package Vali-Flow.NoSql.Elasticsearch + +# Redis (RediSearch) +dotnet add package Vali-Flow.NoSql.Redis + +# AWS DynamoDB +dotnet add package Vali-Flow.NoSql.DynamoDB +``` + +Todos los paquetes incluyen automáticamente **Vali-Flow.Core** como dependencia transitiva. + +--- + +## Características principales + +### DSL de filtros fluido (`ValiFlow`) + +Construye filtros complejos con una API natural y encadenable: + +```csharp +var filter = new ValiFlow() + // Comparación + .EqualTo(x => x.Category, "Electronics") + .GreaterThanOrEqualTo(x => x.Price, 100m) + // Operaciones de cadena + .Contains(x => x.Name, "phone") + .StartsWith(x => x.Sku, "PROD") + // Rangos numéricos + .Between(x => x.Quantity, 1, 1000) + // Fechas + .IsAfter(x => x.CreatedAt, DateTime.UtcNow.AddDays(-90)) + // Colección + .NotEmpty(x => x.Reviews) + // Booleano + .IsTrue(x => x.IsActive) + // Operadores lógicos + .Or() + .EqualTo(x => x.Category, "Accessories"); +``` + +Consulta [Vali-Flow.Core](https://github.com/UBF21/vali-flow-core) para la lista completa de más de 50 predicados. + +### Especificaciones + +Encapsulan criterios de consulta, ordenamiento, paginación y carga ansiosa (eager loading): + +```csharp +var spec = new QuerySpecification() + .WithFilter(filter) + .WithOrderBy(x => x.CreatedAt, ascending: false) + .AddThenBy(x => x.Total, ascending: true) + .WithPagination(page: 1, pageSize: 20) + .AddInclude(x => x.Customer) + .AddInclude(x => x.OrderLines) + .WithAsNoTracking(true); +``` + +### EF Core: operaciones de lectura + +```csharp +var evaluator = new ValiFlowEvaluator(dbContext); + +// Existencia y conteo +bool exists = await evaluator.EvaluateAnyAsync(spec); +int count = await evaluator.EvaluateCountAsync(spec); + +// Entidades individuales +Order? first = await evaluator.EvaluateGetFirstAsync(spec); +Order? last = await evaluator.EvaluateGetLastAsync(spec); + +// Consulta completa +IQueryable query = await evaluator.EvaluateQueryAsync(spec); + +// Distintos y duplicados +IQueryable distinct = await evaluator.EvaluateDistinctAsync(spec, x => x.CustomerId); +IQueryable duplicates = await evaluator.EvaluateDuplicatesAsync(spec, x => x.CustomerId); + +// Agregados +decimal minTotal = await evaluator.EvaluateMinAsync(spec, x => x.Total); +decimal maxTotal = await evaluator.EvaluateMaxAsync(spec, x => x.Total); +decimal avgTotal = await evaluator.EvaluateAverageAsync(spec, x => x.Total); +decimal sumTotal = await evaluator.EvaluateSumAsync(spec, x => x.Total); + +// Agregados agrupados +Dictionary countByStatus = + await evaluator.EvaluateCountByGroupAsync(spec, x => x.Status); + +Dictionary sumByStatus = + await evaluator.EvaluateSumByGroupAsync(spec, x => x.Status, x => x.Total); +``` + +### EF Core: operaciones de escritura + +```csharp +var evaluator = new ValiFlowEvaluator(dbContext); + +// Entidad individual +var added = await evaluator.AddAsync(order, saveChanges: true); +var updated = await evaluator.UpdateAsync(order, saveChanges: true); +await evaluator.DeleteAsync(order, saveChanges: true); + +// Por lotes +await evaluator.AddRangeAsync(orders); +await evaluator.UpdateRangeAsync(orders); +await evaluator.DeleteRangeAsync(orders); + +// Eliminación condicional +await evaluator.DeleteByConditionAsync( + condition: x => x.Status == "Expired" && x.CreatedAt < cutoffDate); + +// Upsert (inserta si no existe, actualiza si existe) +var upserted = await evaluator.UpsertAsync( + entity: order, + matchCondition: x => x.Id == order.Id); + +// Operaciones masivas (vía EFCore.BulkExtensions) +await evaluator.BulkInsertAsync(orders, new BulkConfig { BatchSize = 5000 }); +await evaluator.BulkUpdateAsync(orders, new BulkConfig { BatchSize = 5000 }); +await evaluator.BulkInsertOrUpdateAsync(orders); + +// Transacciones +await evaluator.ExecuteTransactionAsync(async () => +{ + await evaluator.AddAsync(order1, saveChanges: false); + await evaluator.UpdateAsync(order2, saveChanges: false); + await evaluator.SaveChangesAsync(); +}); +``` + +### Constructor de consultas SQL (Dapper / ADO.NET) + +Cuatro dialectos listos para usar: SQL Server, PostgreSQL, MySQL, SQLite. + +```csharp +// Cláusula WHERE simple +var result = filter.ToSql(new PostgreSqlDialect()); +var orders = await connection.QueryAsync( + $"SELECT * FROM orders WHERE {result.Sql}", + result.Parameters); + +// SELECT completo con JOIN, GROUP BY, agregados +var query = new SqlQueryBuilder(new SqlServerDialect()) + .Select(x => x.Id, x => x.Status, x => x.Total) + .From("orders") + .Where(w => w.EqualTo(x => x.Status, "Active")) + .OrderBy(x => x.CreatedAt, ascending: false) + .Paginate(page: 1, pageSize: 20); + +var result = query.Build(); +``` + +### Evaluador en memoria (pruebas / caché) + +Evaluación síncrona y sin dependencias contra `IEnumerable`: + +```csharp +var evaluator = new ValiFlowEvaluator(orders, null, x => x.Id); + +var filter = new ValiFlow().EqualTo(x => x.Status, "Active"); + +int count = evaluator.EvaluateCount(orders, filter); +Order? first = evaluator.GetFirst(orders, filter); + +IEnumerable filtered = evaluator.EvaluateAll( + orders, + orderBy: x => x.CreatedAt, + valiFlow: filter); + +Dictionary countByStatus = + evaluator.EvaluateCountByGroup(orders, x => x.Status, filter); +``` + +--- + +## Soporte NoSQL + +### MongoDB + +```csharp +using Vali_Flow.NoSql.MongoDB.Extensions; + +var filter = new ValiFlow() + .EqualTo(x => x.IsActive, true) + .GreaterThan(x => x.Age, 18); + +BsonDocument bsonFilter = filter.ToMongo(); +var users = await collection.Find(bsonFilter).ToListAsync(); +``` + +### Elasticsearch + +```csharp +using Vali_Flow.NoSql.Elasticsearch.Extensions; + +var filter = new ValiFlow() + .EqualTo(x => x.Category, "Electronics") + .GreaterThanOrEqualTo(x => x.Price, 100m) + .Contains(x => x.Name, "phone"); + +Query esQuery = filter.ToElasticsearch(); +var response = await client.SearchAsync(s => s.Query(esQuery)); +``` + +### Redis (RediSearch) + +```csharp +using Vali_Flow.NoSql.Redis.Extensions; + +string redisQuery = filter.ToRedisSearch(); +var results = db.FT().Search("idx:products", new Query(redisQuery)); +``` + +### DynamoDB + +```csharp +using Vali_Flow.NoSql.DynamoDB.Extensions; + +DynamoFilterExpression f = filter.ToDynamoDB(); + +var request = new ScanRequest +{ + TableName = "Orders", + FilterExpression = f.FilterExpression, + ExpressionAttributeNames = f.ExpressionAttributeNames.ToDictionary(), + ExpressionAttributeValues = f.ExpressionAttributeValues.ToDictionary() +}; +``` + +--- + +## Documentación + +- **[Guía completa de funcionalidades](docs/FEATURES.md)** — Ejemplos detallados de cada paquete +- **[Guía de arquitectura](docs/ARCHITECTURE.md)** — Patrones de diseño y justificación de decisiones +- **[Referencia de dialectos SQL](Vali-Flow.Sql/README.md)** — Capacidades del SQL Builder +- **[Vali-Flow.Core](https://github.com/UBF21/vali-flow-core)** — Predicados del constructor de expresiones + +--- + +## Licencia + +Distribuido bajo la [Licencia MIT](LICENSE). +Copyright © 2025 Felipe Rafael Montenegro Morriberon. Todos los derechos reservados. + +--- + +## Soporte + +- **Issues y solicitudes de funcionalidades:** [GitHub Issues](https://github.com/UBF21/vali-flow/issues) +- **Discusiones:** [GitHub Discussions](https://github.com/UBF21/vali-flow/discussions) + +### Contribuir + +¡Las contribuciones son bienvenidas! Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para las pautas. + +Si este proyecto te resulta útil, considera apoyar su desarrollo: +- **Latinoamérica** — [MercadoPago](https://link.mercadopago.com.pe/felipermm) +- **Internacional** — [PayPal](https://paypal.me/felipeRMM?country.x=PE&locale.x=es_XC) diff --git a/README.md b/README.md index 75862ca..13773a2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Vali-Flow +**English** | [Español](README.es.md) + ## Overview Vali-Flow is a comprehensive .NET library ecosystem for building reusable, composable query criteria with a fluent API. It enables you to define filter logic once using a simple, expression-based DSL and translate it into: @@ -78,26 +80,28 @@ var sqlOrders = await connection.QueryAsync( | Package | Purpose | Version | |---------|---------|---------| | **Vali-Flow.Core** | Fluent expression builder (`ValiFlow`) - shared by all packages | [2.0.0](https://www.nuget.org/packages/Vali-Flow.Core) | +| **Vali-Flow.Abstractions** | Provider-agnostic contracts shared by every package (`IQueryEvaluator`, `IExpressionTranslator`, `ExpressionInspector`) plus `ValiFlowDiagnostics`, an `ActivitySource`-based tracing helper for OpenTelemetry-compatible observability | [1.2.0](https://www.nuget.org/packages/Vali-Flow.Abstractions) | ### Data Access Packages | Package | Purpose | Target | Version | |---------|---------|--------|---------| -| **Vali-Flow** | EF Core async evaluator + specifications (read/write) | `DbContext` | [1.3.4](https://www.nuget.org/packages/Vali-Flow) | -| **Vali-Flow.InMemory** | Synchronous in-memory evaluator for testing & caching | `IEnumerable` | [1.1.5](https://www.nuget.org/packages/Vali-Flow.InMemory) | -| **Vali-Flow.Sql** | SQL query builder for parameterized queries | Dapper / ADO.NET | [1.1.1](https://www.nuget.org/packages/Vali-Flow.Sql) | +| **Vali-Flow** | EF Core async evaluator + specifications (read/write) | `DbContext` | [1.4.0](https://www.nuget.org/packages/Vali-Flow) | +| **Vali-Flow.InMemory** | Synchronous in-memory evaluator for testing & caching | `IEnumerable` | [1.2.0](https://www.nuget.org/packages/Vali-Flow.InMemory) | +| **Vali-Flow.Sql** | SQL query builder for parameterized queries | Dapper / ADO.NET | [1.2.0](https://www.nuget.org/packages/Vali-Flow.Sql) | ### NoSQL Packages | Package | Database | Output Type | Version | |---------|----------|-------------|---------| -| **Vali-Flow.NoSql.MongoDB** | MongoDB | `BsonDocument` | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.MongoDB) | -| **Vali-Flow.NoSql.Elasticsearch** | Elasticsearch | `Query` (Elastic.Clients) | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Elasticsearch) | -| **Vali-Flow.NoSql.Redis** | Redis (RediSearch) | Query string | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Redis) | -| **Vali-Flow.NoSql.DynamoDB** | AWS DynamoDB | `DynamoFilterExpression` | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.DynamoDB) | -| **Vali-Flow.NoSql.Couchbase** | Couchbase | N1QL WHERE fragment + parameters | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Couchbase) | -| **Vali-Flow.NoSql.CosmosDb** | Azure Cosmos DB (SQL API) | SQL WHERE fragment + parameters | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.CosmosDb) | -| **Vali-Flow.NoSql.Firestore** | Google Cloud Firestore | `Filter` (Google.Cloud.Firestore) | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Firestore) | +| **Vali-Flow.NoSql** | Shared NoSQL IR + translator base used by every provider package below | — | [1.1.1](https://www.nuget.org/packages/Vali-Flow.NoSql) | +| **Vali-Flow.NoSql.MongoDB** | MongoDB | `BsonDocument` | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.MongoDB) | +| **Vali-Flow.NoSql.Elasticsearch** | Elasticsearch | `Query` (Elastic.Clients) | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Elasticsearch) | +| **Vali-Flow.NoSql.Redis** | Redis (RediSearch) | Query string | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Redis) | +| **Vali-Flow.NoSql.DynamoDB** | AWS DynamoDB | `DynamoFilterExpression` | [1.2.0](https://www.nuget.org/packages/Vali-Flow.NoSql.DynamoDB) | +| **Vali-Flow.NoSql.Couchbase** | Couchbase | N1QL WHERE fragment + parameters | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Couchbase) | +| **Vali-Flow.NoSql.CosmosDb** | Azure Cosmos DB (SQL API) | SQL WHERE fragment + parameters | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.CosmosDb) | +| **Vali-Flow.NoSql.Firestore** | Google Cloud Firestore | `Filter` (Google.Cloud.Firestore) | [1.1.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Firestore) | ### Architecture