mongoeco 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. mongoeco-2.0.0/COMPATIBILITY.md +326 -0
  2. mongoeco-2.0.0/DIALECTS.md +454 -0
  3. mongoeco-2.0.0/LICENSE +199 -0
  4. mongoeco-2.0.0/MANIFEST.in +4 -0
  5. mongoeco-2.0.0/PKG-INFO +352 -0
  6. mongoeco-2.0.0/README.md +314 -0
  7. mongoeco-2.0.0/TODO.md +106 -0
  8. mongoeco-2.0.0/pyproject.toml +186 -0
  9. mongoeco-2.0.0/setup.cfg +4 -0
  10. mongoeco-2.0.0/src/mongoeco/__init__.py +270 -0
  11. mongoeco-2.0.0/src/mongoeco/_version.py +2 -0
  12. mongoeco-2.0.0/src/mongoeco/api/__init__.py +13 -0
  13. mongoeco-2.0.0/src/mongoeco/api/_async/__init__.py +7 -0
  14. mongoeco-2.0.0/src/mongoeco/api/_async/_materialized_cursor.py +61 -0
  15. mongoeco-2.0.0/src/mongoeco/api/_async/aggregation_cursor.py +569 -0
  16. mongoeco-2.0.0/src/mongoeco/api/_async/client.py +662 -0
  17. mongoeco-2.0.0/src/mongoeco/api/_async/collection.py +2874 -0
  18. mongoeco-2.0.0/src/mongoeco/api/_async/cursor.py +548 -0
  19. mongoeco-2.0.0/src/mongoeco/api/_async/database_admin.py +1561 -0
  20. mongoeco-2.0.0/src/mongoeco/api/_async/database_commands.py +814 -0
  21. mongoeco-2.0.0/src/mongoeco/api/_async/index_cursor.py +14 -0
  22. mongoeco-2.0.0/src/mongoeco/api/_async/listing_cursor.py +14 -0
  23. mongoeco-2.0.0/src/mongoeco/api/_async/raw_batch_cursor.py +49 -0
  24. mongoeco-2.0.0/src/mongoeco/api/_async/search_index_cursor.py +14 -0
  25. mongoeco-2.0.0/src/mongoeco/api/_sync/__init__.py +7 -0
  26. mongoeco-2.0.0/src/mongoeco/api/_sync/_materialized_cursor.py +53 -0
  27. mongoeco-2.0.0/src/mongoeco/api/_sync/aggregation_cursor.py +130 -0
  28. mongoeco-2.0.0/src/mongoeco/api/_sync/client.py +746 -0
  29. mongoeco-2.0.0/src/mongoeco/api/_sync/collection.py +1098 -0
  30. mongoeco-2.0.0/src/mongoeco/api/_sync/cursor.py +396 -0
  31. mongoeco-2.0.0/src/mongoeco/api/_sync/database_admin.py +127 -0
  32. mongoeco-2.0.0/src/mongoeco/api/_sync/database_commands.py +41 -0
  33. mongoeco-2.0.0/src/mongoeco/api/_sync/index_cursor.py +12 -0
  34. mongoeco-2.0.0/src/mongoeco/api/_sync/listing_cursor.py +12 -0
  35. mongoeco-2.0.0/src/mongoeco/api/_sync/raw_batch_cursor.py +20 -0
  36. mongoeco-2.0.0/src/mongoeco/api/_sync/search_index_cursor.py +12 -0
  37. mongoeco-2.0.0/src/mongoeco/api/admin_parsing.py +297 -0
  38. mongoeco-2.0.0/src/mongoeco/api/operations.py +418 -0
  39. mongoeco-2.0.0/src/mongoeco/api/public_api.py +329 -0
  40. mongoeco-2.0.0/src/mongoeco/change_streams.py +263 -0
  41. mongoeco-2.0.0/src/mongoeco/compat/__init__.py +113 -0
  42. mongoeco-2.0.0/src/mongoeco/compat/base.py +586 -0
  43. mongoeco-2.0.0/src/mongoeco/compat/catalog.py +759 -0
  44. mongoeco-2.0.0/src/mongoeco/compat/operation_support.py +39 -0
  45. mongoeco-2.0.0/src/mongoeco/compat/registry.py +204 -0
  46. mongoeco-2.0.0/src/mongoeco/core/__init__.py +0 -0
  47. mongoeco-2.0.0/src/mongoeco/core/aggregation/__init__.py +109 -0
  48. mongoeco-2.0.0/src/mongoeco/core/aggregation/accumulators.py +716 -0
  49. mongoeco-2.0.0/src/mongoeco/core/aggregation/array_string_expressions.py +746 -0
  50. mongoeco-2.0.0/src/mongoeco/core/aggregation/compiled_aggregation.py +353 -0
  51. mongoeco-2.0.0/src/mongoeco/core/aggregation/compiled_pipeline.py +305 -0
  52. mongoeco-2.0.0/src/mongoeco/core/aggregation/control_object_expressions.py +229 -0
  53. mongoeco-2.0.0/src/mongoeco/core/aggregation/cost.py +34 -0
  54. mongoeco-2.0.0/src/mongoeco/core/aggregation/date_expressions.py +634 -0
  55. mongoeco-2.0.0/src/mongoeco/core/aggregation/extensions.py +139 -0
  56. mongoeco-2.0.0/src/mongoeco/core/aggregation/grouping_stages.py +468 -0
  57. mongoeco-2.0.0/src/mongoeco/core/aggregation/join_stages.py +135 -0
  58. mongoeco-2.0.0/src/mongoeco/core/aggregation/numeric_expressions.py +456 -0
  59. mongoeco-2.0.0/src/mongoeco/core/aggregation/planning.py +238 -0
  60. mongoeco-2.0.0/src/mongoeco/core/aggregation/runtime.py +773 -0
  61. mongoeco-2.0.0/src/mongoeco/core/aggregation/scalar_expressions.py +437 -0
  62. mongoeco-2.0.0/src/mongoeco/core/aggregation/spill.py +169 -0
  63. mongoeco-2.0.0/src/mongoeco/core/aggregation/stages.py +402 -0
  64. mongoeco-2.0.0/src/mongoeco/core/aggregation/transform_stages.py +197 -0
  65. mongoeco-2.0.0/src/mongoeco/core/bson_ordering.py +82 -0
  66. mongoeco-2.0.0/src/mongoeco/core/bson_scalars.py +434 -0
  67. mongoeco-2.0.0/src/mongoeco/core/codec.py +330 -0
  68. mongoeco-2.0.0/src/mongoeco/core/collation.py +114 -0
  69. mongoeco-2.0.0/src/mongoeco/core/compiled_query.py +194 -0
  70. mongoeco-2.0.0/src/mongoeco/core/filtering.py +1300 -0
  71. mongoeco-2.0.0/src/mongoeco/core/identity.py +13 -0
  72. mongoeco-2.0.0/src/mongoeco/core/json_compat.py +48 -0
  73. mongoeco-2.0.0/src/mongoeco/core/operation_limits.py +14 -0
  74. mongoeco-2.0.0/src/mongoeco/core/operators.py +525 -0
  75. mongoeco-2.0.0/src/mongoeco/core/paths.py +151 -0
  76. mongoeco-2.0.0/src/mongoeco/core/projections.py +125 -0
  77. mongoeco-2.0.0/src/mongoeco/core/query_plan.py +570 -0
  78. mongoeco-2.0.0/src/mongoeco/core/schema_validation.py +488 -0
  79. mongoeco-2.0.0/src/mongoeco/core/search.py +477 -0
  80. mongoeco-2.0.0/src/mongoeco/core/sorting.py +359 -0
  81. mongoeco-2.0.0/src/mongoeco/core/sql_translation.py +62 -0
  82. mongoeco-2.0.0/src/mongoeco/core/update_array_operators.py +361 -0
  83. mongoeco-2.0.0/src/mongoeco/core/update_paths.py +177 -0
  84. mongoeco-2.0.0/src/mongoeco/core/update_scalar_operators.py +272 -0
  85. mongoeco-2.0.0/src/mongoeco/core/upserts.py +38 -0
  86. mongoeco-2.0.0/src/mongoeco/core/validation.py +19 -0
  87. mongoeco-2.0.0/src/mongoeco/driver/__init__.py +128 -0
  88. mongoeco-2.0.0/src/mongoeco/driver/connections.py +317 -0
  89. mongoeco-2.0.0/src/mongoeco/driver/discovery.py +130 -0
  90. mongoeco-2.0.0/src/mongoeco/driver/execution.py +197 -0
  91. mongoeco-2.0.0/src/mongoeco/driver/monitoring.py +113 -0
  92. mongoeco-2.0.0/src/mongoeco/driver/policies.py +161 -0
  93. mongoeco-2.0.0/src/mongoeco/driver/requests.py +57 -0
  94. mongoeco-2.0.0/src/mongoeco/driver/runtime.py +294 -0
  95. mongoeco-2.0.0/src/mongoeco/driver/security.py +69 -0
  96. mongoeco-2.0.0/src/mongoeco/driver/topology.py +125 -0
  97. mongoeco-2.0.0/src/mongoeco/driver/topology_monitor.py +156 -0
  98. mongoeco-2.0.0/src/mongoeco/driver/transports.py +164 -0
  99. mongoeco-2.0.0/src/mongoeco/driver/uri.py +410 -0
  100. mongoeco-2.0.0/src/mongoeco/engines/__init__.py +5 -0
  101. mongoeco-2.0.0/src/mongoeco/engines/base.py +177 -0
  102. mongoeco-2.0.0/src/mongoeco/engines/memory.py +1896 -0
  103. mongoeco-2.0.0/src/mongoeco/engines/mvcc.py +73 -0
  104. mongoeco-2.0.0/src/mongoeco/engines/profiling.py +104 -0
  105. mongoeco-2.0.0/src/mongoeco/engines/semantic_core.py +388 -0
  106. mongoeco-2.0.0/src/mongoeco/engines/sqlite.py +5264 -0
  107. mongoeco-2.0.0/src/mongoeco/engines/sqlite_planner.py +241 -0
  108. mongoeco-2.0.0/src/mongoeco/engines/sqlite_query.py +818 -0
  109. mongoeco-2.0.0/src/mongoeco/engines/virtual_indexes.py +424 -0
  110. mongoeco-2.0.0/src/mongoeco/error_catalog.py +81 -0
  111. mongoeco-2.0.0/src/mongoeco/errors.py +146 -0
  112. mongoeco-2.0.0/src/mongoeco/session.py +344 -0
  113. mongoeco-2.0.0/src/mongoeco/types.py +1726 -0
  114. mongoeco-2.0.0/src/mongoeco/wire/__init__.py +15 -0
  115. mongoeco-2.0.0/src/mongoeco/wire/auth.py +73 -0
  116. mongoeco-2.0.0/src/mongoeco/wire/bson_bridge.py +110 -0
  117. mongoeco-2.0.0/src/mongoeco/wire/capabilities.py +83 -0
  118. mongoeco-2.0.0/src/mongoeco/wire/connections.py +82 -0
  119. mongoeco-2.0.0/src/mongoeco/wire/cursors.py +127 -0
  120. mongoeco-2.0.0/src/mongoeco/wire/executor.py +234 -0
  121. mongoeco-2.0.0/src/mongoeco/wire/handshake.py +54 -0
  122. mongoeco-2.0.0/src/mongoeco/wire/protocol.py +201 -0
  123. mongoeco-2.0.0/src/mongoeco/wire/proxy.py +168 -0
  124. mongoeco-2.0.0/src/mongoeco/wire/requests.py +19 -0
  125. mongoeco-2.0.0/src/mongoeco/wire/sessions.py +109 -0
  126. mongoeco-2.0.0/src/mongoeco/wire/surface.py +41 -0
  127. mongoeco-2.0.0/src/mongoeco.egg-info/PKG-INFO +352 -0
  128. mongoeco-2.0.0/src/mongoeco.egg-info/SOURCES.txt +129 -0
  129. mongoeco-2.0.0/src/mongoeco.egg-info/dependency_links.txt +1 -0
  130. mongoeco-2.0.0/src/mongoeco.egg-info/requires.txt +24 -0
  131. mongoeco-2.0.0/src/mongoeco.egg-info/top_level.txt +1 -0
@@ -0,0 +1,326 @@
1
+ # Compatibility Guide
2
+
3
+ Esta guía resume cómo configurar `mongoeco` cuando quieres controlar:
4
+
5
+ * la semántica objetivo de MongoDB (`mongodb_dialect`)
6
+ * la superficie pública objetivo de PyMongo (`pymongo_profile`)
7
+
8
+ ## 1. Dos ejes distintos
9
+
10
+ `mongoeco` separa dos conceptos:
11
+
12
+ * `mongodb_dialect`
13
+ * controla semántica observable del servidor
14
+ * ejemplos: comparación con `null`, tratamiento de `undefined`, validaciones y deltas de MQL
15
+ * `pymongo_profile`
16
+ * controla compatibilidad de la API Python
17
+ * ejemplos: parámetros aceptados por métodos públicos o diferencias pequeñas de superficie
18
+
19
+ La versión instalada de `pymongo` **no** decide la semántica del servidor MongoDB.
20
+
21
+ ## 1.1 Baseline soportado
22
+
23
+ `mongoeco` no persigue compatibilidad hacia atrás por debajo de estos mínimos:
24
+
25
+ * MongoDB `7.0`
26
+ * PyMongo `4.9`
27
+
28
+ Consecuencias prácticas:
29
+
30
+ * no se aceptan como objetivo de diseño semánticas específicas de MongoDB `6.x` o anteriores
31
+ * no se aceptan como objetivo de diseño firmas o comportamientos específicos de PyMongo anteriores a `4.9`
32
+ * cuando se amplía superficie pública o semántica, la referencia es siempre PyMongo `4.9+` sobre dialectos MongoDB `7.0+`
33
+
34
+ ## 2. Configuración explícita recomendada
35
+
36
+ La forma más estable y reproducible es fijar ambos ejes explícitamente:
37
+
38
+ ```python
39
+ from mongoeco import AsyncMongoClient
40
+
41
+ client = AsyncMongoClient(
42
+ mongodb_dialect="7.0",
43
+ pymongo_profile="4.9",
44
+ )
45
+ ```
46
+
47
+ También puedes usar los objetos oficiales:
48
+
49
+ ```python
50
+ from mongoeco import AsyncMongoClient, MongoDialect70, PyMongoProfile411
51
+
52
+ client = AsyncMongoClient(
53
+ mongodb_dialect=MongoDialect70(),
54
+ pymongo_profile=PyMongoProfile411(),
55
+ )
56
+ ```
57
+
58
+ La misma idea se aplica a ambos ejes: `mongoeco` resuelve y conserva metadata
59
+ de la decisión tomada.
60
+
61
+ ## 3. Dialectos MongoDB disponibles
62
+
63
+ Hoy el catálogo oficial incluye:
64
+
65
+ * `7.0`
66
+ * `8.0`
67
+
68
+ Regla práctica:
69
+
70
+ * `7.0` es la baseline de desarrollo
71
+ * `8.0` se trata como compatibilidad adicional con deltas explícitos
72
+ * la selección del dialecto es explícita; `mongoeco` no autodetecta servidor en el flujo normal
73
+ * no existe catálogo oficial para versiones anteriores a `7.0`
74
+
75
+ ## 3.1 Resolución del dialecto MongoDB
76
+
77
+ La API pública ya expone una resolución estructurada equivalente a la de
78
+ `pymongo_profile`:
79
+
80
+ ```python
81
+ from mongoeco import resolve_mongodb_dialect_resolution
82
+
83
+ resolution = resolve_mongodb_dialect_resolution("8.0")
84
+
85
+ print(resolution.resolved_dialect.key)
86
+ print(resolution.resolution_mode)
87
+ ```
88
+
89
+ Campos disponibles:
90
+
91
+ * `requested`
92
+ * `detected_server_version`
93
+ * `resolved_dialect`
94
+ * `resolution_mode`
95
+
96
+ Modos posibles hoy:
97
+
98
+ * `default`
99
+ * `explicit-alias`
100
+ * `explicit-instance`
101
+
102
+ ## 4. Perfiles PyMongo disponibles
103
+
104
+ Hoy el catálogo oficial incluye:
105
+
106
+ * `4.9`
107
+ * `4.11`
108
+ * `4.13`
109
+
110
+ Regla práctica:
111
+
112
+ * `4.9` es la baseline de API pública
113
+ * `4.11` activa el primer delta real: `update_one(sort=...)`
114
+ * `4.13` queda disponible como perfil posterior compatible
115
+ * no existe catálogo oficial para perfiles anteriores a `4.9`
116
+
117
+ ## 5. Autodetección de PyMongo instalada
118
+
119
+ Puedes pedir a `mongoeco` que resuelva el perfil según la versión instalada del
120
+ paquete `pymongo`.
121
+
122
+ ### Modo flexible
123
+
124
+ ```python
125
+ from mongoeco import MongoClient
126
+
127
+ client = MongoClient(pymongo_profile="auto-installed")
128
+ ```
129
+
130
+ Política:
131
+
132
+ * si la versión instalada coincide con un perfil conocido, usa ese perfil
133
+ * si aparece una minor nueva dentro de la misma major conocida, cae al último
134
+ perfil compatible de esa major
135
+ * si aparece una major nueva no registrada, falla
136
+
137
+ Ejemplos actuales:
138
+
139
+ * `4.8.x` -> error explícito
140
+ * `4.10.x` -> `4.9`
141
+ * `4.12.x` -> `4.11`
142
+ * `4.14.x` -> `4.13`
143
+ * `5.x` -> error explícito
144
+
145
+ ### Modo estricto
146
+
147
+ ```python
148
+ from mongoeco import MongoClient
149
+
150
+ client = MongoClient(pymongo_profile="strict-auto-installed")
151
+ ```
152
+
153
+ Política:
154
+
155
+ * solo acepta versiones instaladas que encajen exactamente en un perfil
156
+ registrado
157
+ * si aparece una minor nueva todavía no modelada, falla
158
+
159
+ Este modo es el recomendable para CI o validación contractual estricta.
160
+
161
+ ## 6. Inspeccionar la resolución aplicada
162
+
163
+ Si quieres conocer exactamente qué política se ha aplicado, usa la API pública
164
+ de resolución:
165
+
166
+ ```python
167
+ from mongoeco import resolve_pymongo_profile_resolution
168
+
169
+ resolution = resolve_pymongo_profile_resolution("auto-installed")
170
+
171
+ print(resolution.installed_version)
172
+ print(resolution.resolved_profile.key)
173
+ print(resolution.resolution_mode)
174
+ ```
175
+
176
+ Campos disponibles:
177
+
178
+ * `requested`
179
+ * `installed_version`
180
+ * `resolved_profile`
181
+ * `resolution_mode`
182
+
183
+ Modos posibles hoy:
184
+
185
+ * `default`
186
+ * `explicit-alias`
187
+ * `explicit-instance`
188
+ * `auto-exact`
189
+ * `auto-compatible-minor-fallback`
190
+
191
+ También puedes inspeccionar la resolución ya aplicada en el cliente:
192
+
193
+ ```python
194
+ from mongoeco import MongoClient
195
+
196
+ client = MongoClient(pymongo_profile="auto-installed")
197
+
198
+ print(client.pymongo_profile.key)
199
+ print(client.pymongo_profile_resolution.installed_version)
200
+ print(client.pymongo_profile_resolution.resolution_mode)
201
+ ```
202
+
203
+ Y de forma simétrica para el dialecto:
204
+
205
+ ```python
206
+ from mongoeco import MongoClient
207
+
208
+ client = MongoClient(mongodb_dialect="8.0")
209
+
210
+ print(client.mongodb_dialect.key)
211
+ print(client.mongodb_dialect_resolution.resolution_mode)
212
+ ```
213
+
214
+ ## 7. Recomendación operativa
215
+
216
+ Para trabajo diario:
217
+
218
+ * `mongodb_dialect="7.0"`
219
+ * `pymongo_profile="auto-installed"`
220
+
221
+ Para CI y suites de compatibilidad:
222
+
223
+ * `mongodb_dialect` fijado explícitamente
224
+ * `pymongo_profile` fijado explícitamente, o `strict-auto-installed`
225
+
226
+ ## 8. Verificación contractual contra PyMongo real
227
+
228
+ La ampliación de superficie pública no debe decidirse por memoria ni por lectura
229
+ aislada de firmas.
230
+
231
+ El repositorio incluye un arnés repetible:
232
+
233
+ * [scripts/run_pymongo_profile_matrix.py](scripts/run_pymongo_profile_matrix.py)
234
+ * [tests/fixtures/pymongo_profile_matrix.json](tests/fixtures/pymongo_profile_matrix.json)
235
+
236
+ Uso recomendado:
237
+
238
+ ```bash
239
+ python3 scripts/run_pymongo_profile_matrix.py
240
+ ```
241
+
242
+ El script crea entornos aislados para `PyMongo 4.9`, `4.11` y `4.13`, ejecuta
243
+ una sonda de aceptación de parámetros reales y devuelve un JSON con los
244
+ resultados.
245
+
246
+ El JSON versionado en `tests/fixtures/` actúa como snapshot contractual del
247
+ último contraste validado y debe actualizarse cuando cambie la matriz real.
248
+
249
+ Regla de mantenimiento:
250
+
251
+ * cualquier parámetro nuevo en la API pública debe contrastarse primero con este
252
+ arnés
253
+ * solo se añade un hook nuevo a `PyMongoProfile` cuando la matriz real detecta
254
+ un delta observable entre perfiles
255
+
256
+ Matriz ya verificada:
257
+
258
+ * baseline común en `4.9/4.11/4.13`:
259
+ * `hint`, `comment` y `let` en `update_*`, `replace_one`, `delete_*`
260
+ * `comment` y `let` en `bulk_write`
261
+
262
+ ## 9. Superficie aceptada frente a semántica efectiva
263
+
264
+ No toda opción aceptada por la API pública tiene ya un efecto real en los
265
+ engines locales.
266
+
267
+ El proyecto distingue ahora entre:
268
+
269
+ * `effective`
270
+ * la opción ya participa en la semántica observable
271
+ * `accepted-noop`
272
+ * la opción se acepta y valida por compatibilidad, pero todavía no cambia el
273
+ comportamiento real del motor
274
+
275
+ API pública disponible:
276
+
277
+ ```python
278
+ from mongoeco import (
279
+ OPERATION_OPTION_SUPPORT,
280
+ OptionSupportStatus,
281
+ get_operation_option_support,
282
+ is_operation_option_effective,
283
+ )
284
+
285
+ support = get_operation_option_support("aggregate", "let")
286
+ assert support is not None
287
+ assert support.status is OptionSupportStatus.EFFECTIVE
288
+
289
+ assert is_operation_option_effective("find", "hint")
290
+ ```
291
+
292
+ Casos relevantes hoy:
293
+
294
+ * `aggregate(let=...)` -> `effective`
295
+ * `find(hint=...)` -> `effective`
296
+ * `find(comment=...)` -> `effective`
297
+ * `find(max_time_ms=...)` -> `effective`
298
+ * `find(batch_size=...)` -> `effective` con batching local del cursor
299
+ * `aggregate(batch_size=...)` -> `effective` en pipelines streamables; stages globales siguen materializando completo
300
+ * `update_one(let=...)` -> `effective` cuando el filtro usa `$expr`
301
+ * `replace_one(let=...)` -> `effective` cuando el filtro usa `$expr`
302
+ * `bulk_write(comment=...)` -> `effective`
303
+ * `bulk_write(let=...)` -> `effective` cuando las operaciones usan filtros con `$expr`
304
+
305
+ Regla de mantenimiento:
306
+
307
+ * no se debe promocionar una opción a `effective` sin test observable
308
+ * no se debe aceptar una opción nueva sin registrarla en esta matriz
309
+ * `max_time_ms` en `find_one_and_*`
310
+ * `hint`, `comment`, `let`, `batchSize/maxTimeMS` en `aggregate`
311
+ * delta real desde `4.11+`:
312
+ * `sort` en `update_one`
313
+ * `sort` en `replace_one`
314
+ * `sort` en `UpdateOne(...)` y `ReplaceOne(...)` para `bulk_write`
315
+ * explícitamente no soportado en `4.9+`:
316
+ * `max_time_ms` en `update_one`, `update_many`, `replace_one`,
317
+ `delete_one` y `delete_many`
318
+
319
+ ## 9. Qué no hace `mongoeco`
320
+
321
+ `mongoeco` no:
322
+
323
+ * infiere la semántica del servidor MongoDB a partir de la versión instalada de
324
+ `pymongo`
325
+ * acepta silenciosamente majors nuevas de `pymongo`
326
+ * mezcla dialecto de servidor y perfil de driver en una sola opción