pg_eventstore 2.0.0 → 3.0.0

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 (303) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -1
  3. data/CHANGELOG.md +186 -20
  4. data/README.md +25 -15
  5. data/db/migrations/10_setup_pg_cron.rb +1 -23
  6. data/db/migrations/15_create_streams_global_index.sql +17 -0
  7. data/db/migrations/16_populate_streams_global_index.rb +150 -0
  8. data/db/migrations/17_create_events_global_index.sql +62 -0
  9. data/db/migrations/18_populate_events_global_index.rb +124 -0
  10. data/db/migrations/19_populate_events_global_index_p2.rb +25 -0
  11. data/db/migrations/20_migrate_subscriptions_current_position.sql +19 -0
  12. data/db/migrations/21_create_partitions_stats.sql +2 -0
  13. data/db/migrations/22_drop_streams_sql_view.sql +2 -0
  14. data/db/migrations/23_adjust_partitions_indexes.sql +5 -0
  15. data/db/migrations/24_drop_legacy_events_indexes.sql +3 -0
  16. data/db/migrations/25_add_parent_partitions_info_to_partitions.sql +16 -0
  17. data/db/migrations/26_change_events_stream_revision_type.rb +35 -0
  18. data/db/migrations/27_drop_events_horizon.sql +3 -0
  19. data/db/migrations/28_create_maintenance_tasks.sql +8 -0
  20. data/db/migrations/29_create_event_markers.sql +35 -0
  21. data/db/migrations/30_improve_partitions_search_in_admin_ui.rb +24 -0
  22. data/db/migrations/31_drop_events_id_default_value.sql +1 -0
  23. data/db/migrations/32_create_parittions_triggers.sql +101 -0
  24. data/docs/AGENTS.fragment.md +696 -0
  25. data/docs/admin_ui.md +3 -1
  26. data/docs/appending_events.md +110 -49
  27. data/docs/configuration.md +69 -36
  28. data/docs/event_tracing.md +55 -0
  29. data/docs/events_and_streams.md +54 -30
  30. data/docs/how_it_works.md +57 -16
  31. data/docs/linking_events.md +2 -1
  32. data/docs/maintenance.md +14 -5
  33. data/docs/multiple_commands.md +17 -8
  34. data/docs/reading_events.md +208 -43
  35. data/docs/reading_streams.md +50 -0
  36. data/docs/replication.md +99 -0
  37. data/docs/subscriptions.md +165 -35
  38. data/docs/writing_middleware.md +25 -8
  39. data/ext/pg_eventstore_ext/extconf.rb +5 -0
  40. data/ext/pg_eventstore_ext/pg_eventstore_ext.c +74 -0
  41. data/lib/pg_eventstore/async_runner.rb +63 -0
  42. data/lib/pg_eventstore/basic_config.rb +15 -0
  43. data/lib/pg_eventstore/chunks/chunk.rb +31 -0
  44. data/lib/pg_eventstore/chunks/read_api_events_index_chunk.rb +83 -0
  45. data/lib/pg_eventstore/chunks/replica_events_index_chunk.rb +35 -0
  46. data/lib/pg_eventstore/chunks/repository.rb +73 -0
  47. data/lib/pg_eventstore/chunks/subscription_checkpoint_chunk.rb +48 -0
  48. data/lib/pg_eventstore/chunks/subscription_events_index_chunk.rb +99 -0
  49. data/lib/pg_eventstore/chunks.rb +14 -0
  50. data/lib/pg_eventstore/client.rb +163 -50
  51. data/lib/pg_eventstore/commands/append.rb +62 -43
  52. data/lib/pg_eventstore/commands/delete_event.rb +7 -8
  53. data/lib/pg_eventstore/commands/delete_stream.rb +3 -3
  54. data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +9 -7
  55. data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +12 -7
  56. data/lib/pg_eventstore/commands/link_to.rb +5 -5
  57. data/lib/pg_eventstore/commands/read.rb +23 -5
  58. data/lib/pg_eventstore/commands/read_grouped.rb +46 -0
  59. data/lib/pg_eventstore/commands/read_streams.rb +18 -0
  60. data/lib/pg_eventstore/commands/read_streams_paginated.rb +56 -0
  61. data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +4 -4
  62. data/lib/pg_eventstore/commands/revision_check/current_revision.rb +51 -0
  63. data/lib/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rb +46 -0
  64. data/lib/pg_eventstore/commands/revision_check/expected_revision.rb +135 -0
  65. data/lib/pg_eventstore/commands/revision_check/stream_revision_check.rb +98 -0
  66. data/lib/pg_eventstore/commands/revision_check/stream_revision_comparison.rb +36 -0
  67. data/lib/pg_eventstore/commands/stream_revision.rb +14 -0
  68. data/lib/pg_eventstore/commands/system_stream_read_paginated.rb +4 -4
  69. data/lib/pg_eventstore/commands.rb +4 -2
  70. data/lib/pg_eventstore/config.rb +30 -9
  71. data/lib/pg_eventstore/connection.rb +21 -2
  72. data/lib/pg_eventstore/errors.rb +136 -10
  73. data/lib/pg_eventstore/event.rb +34 -2
  74. data/lib/pg_eventstore/event_deserializer.rb +5 -4
  75. data/lib/pg_eventstore/event_global_index.rb +118 -0
  76. data/lib/pg_eventstore/event_marker.rb +16 -0
  77. data/lib/pg_eventstore/event_marker_index.rb +25 -0
  78. data/lib/pg_eventstore/event_serializer.rb +6 -0
  79. data/lib/pg_eventstore/extensions/acts_as_configurable.rb +88 -0
  80. data/lib/pg_eventstore/extensions/options_defaults.rb +25 -0
  81. data/lib/pg_eventstore/extensions/options_extension.rb +2 -2
  82. data/lib/pg_eventstore/feature_marker.rb +18 -0
  83. data/lib/pg_eventstore/maintenance.rb +11 -6
  84. data/lib/pg_eventstore/middleware/event_tracing.rb +53 -0
  85. data/lib/pg_eventstore/partition.rb +6 -0
  86. data/lib/pg_eventstore/pg_connection.rb +70 -14
  87. data/lib/pg_eventstore/queries/event_marker_queries.rb +74 -0
  88. data/lib/pg_eventstore/queries/event_queries.rb +7 -93
  89. data/lib/pg_eventstore/queries/events_global_index_queries.rb +131 -0
  90. data/lib/pg_eventstore/queries/index_filtering_queries.rb +179 -0
  91. data/lib/pg_eventstore/queries/links_resolver.rb +4 -4
  92. data/lib/pg_eventstore/queries/maintenance_queries.rb +159 -42
  93. data/lib/pg_eventstore/queries/partition_queries.rb +45 -103
  94. data/lib/pg_eventstore/queries/replica_queries.rb +110 -0
  95. data/lib/pg_eventstore/queries/streams_global_index_queries.rb +163 -0
  96. data/lib/pg_eventstore/queries/transaction_queries.rb +52 -10
  97. data/lib/pg_eventstore/queries.rb +29 -1
  98. data/lib/pg_eventstore/query_builders/basic_filtering.rb +38 -5
  99. data/lib/pg_eventstore/query_builders/event_markers_filtering.rb +47 -0
  100. data/lib/pg_eventstore/query_builders/event_markers_index_filtering.rb +275 -0
  101. data/lib/pg_eventstore/query_builders/event_subscription_positions_filtering.rb +37 -0
  102. data/lib/pg_eventstore/query_builders/events_filtering.rb +15 -164
  103. data/lib/pg_eventstore/query_builders/events_global_index_filtering.rb +227 -0
  104. data/lib/pg_eventstore/query_builders/filters/collection.rb +324 -0
  105. data/lib/pg_eventstore/query_builders/filters/event_type_filter.rb +37 -0
  106. data/lib/pg_eventstore/query_builders/filters/filter_row.rb +54 -0
  107. data/lib/pg_eventstore/query_builders/filters/marker_filter.rb +20 -0
  108. data/lib/pg_eventstore/query_builders/filters/marker_filter_row.rb +49 -0
  109. data/lib/pg_eventstore/query_builders/filters/stream_filter.rb +48 -0
  110. data/lib/pg_eventstore/query_builders/index_based_events_filtering.rb +260 -0
  111. data/lib/pg_eventstore/query_builders/partitions_filtering.rb +175 -51
  112. data/lib/pg_eventstore/query_builders/read_cursor/stream_cursor.rb +122 -0
  113. data/lib/pg_eventstore/query_builders/streams_global_index_filtering.rb +83 -0
  114. data/lib/pg_eventstore/query_builders/subscription_events_filtering.rb +79 -0
  115. data/lib/pg_eventstore/query_strategy/async.rb +64 -0
  116. data/lib/pg_eventstore/query_strategy/foreground.rb +25 -0
  117. data/lib/pg_eventstore/query_strategy.rb +19 -0
  118. data/lib/pg_eventstore/raw_event.rb +46 -0
  119. data/lib/pg_eventstore/rspec/test_helpers.rb +21 -8
  120. data/lib/pg_eventstore/sql_builder.rb +87 -8
  121. data/lib/pg_eventstore/stream.rb +13 -16
  122. data/lib/pg_eventstore/stream_global_index.rb +28 -0
  123. data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +5 -17
  124. data/lib/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rb +36 -0
  125. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +12 -0
  126. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +21 -7
  127. data/lib/pg_eventstore/subscriptions/events_processor.rb +20 -18
  128. data/lib/pg_eventstore/subscriptions/events_processor_consumer/multiple.rb +63 -0
  129. data/lib/pg_eventstore/subscriptions/events_processor_consumer/replica.rb +68 -0
  130. data/lib/pg_eventstore/subscriptions/events_processor_consumer/single.rb +62 -0
  131. data/lib/pg_eventstore/subscriptions/events_processor_consumer.rb +44 -0
  132. data/lib/pg_eventstore/subscriptions/events_subscription_position_worker.rb +68 -0
  133. data/lib/pg_eventstore/subscriptions/queries/event_subscription_position_queries.rb +177 -0
  134. data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +78 -54
  135. data/lib/pg_eventstore/subscriptions/replica_subscription_handler.rb +140 -0
  136. data/lib/pg_eventstore/subscriptions/replica_subscription_runner.rb +9 -0
  137. data/lib/pg_eventstore/subscriptions/subscription.rb +2 -1
  138. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/collection.rb +36 -0
  139. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/index_read_strategy.rb +82 -0
  140. data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/replication_strategy.rb +75 -0
  141. data/lib/pg_eventstore/subscriptions/subscription_feed_strategy.rb +31 -0
  142. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +24 -0
  143. data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +4 -2
  144. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +20 -9
  145. data/lib/pg_eventstore/subscriptions/subscription_runner_commands/reset_position.rb +1 -1
  146. data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +9 -18
  147. data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +0 -12
  148. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +74 -14
  149. data/lib/pg_eventstore/{subscriptions/synchronized_array.rb → synchronized_array.rb} +15 -0
  150. data/lib/pg_eventstore/tasks/setup.rake +2 -2
  151. data/lib/pg_eventstore/utils.rb +31 -0
  152. data/lib/pg_eventstore/version.rb +1 -1
  153. data/lib/pg_eventstore/web/application.rb +100 -16
  154. data/lib/pg_eventstore/web/paginator/base_collection.rb +5 -1
  155. data/lib/pg_eventstore/web/paginator/event_types_collection.rb +14 -4
  156. data/lib/pg_eventstore/web/paginator/events_collection.rb +29 -33
  157. data/lib/pg_eventstore/web/paginator/helpers.rb +16 -9
  158. data/lib/pg_eventstore/web/paginator/markers_collection.rb +48 -0
  159. data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +13 -2
  160. data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +21 -11
  161. data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +18 -4
  162. data/lib/pg_eventstore/web/paginator/streams_collection.rb +95 -0
  163. data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +49 -22
  164. data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
  165. data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +1 -1
  166. data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
  167. data/lib/pg_eventstore/web/subscriptions/with_state/subscriptions.rb +1 -1
  168. data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -9
  169. data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +18 -5
  170. data/lib/pg_eventstore/web/views/home/partials/events.erb +38 -1
  171. data/lib/pg_eventstore/web/views/home/partials/markers_filter.erb +18 -0
  172. data/lib/pg_eventstore/web/views/layouts/application.erb +6 -0
  173. data/lib/pg_eventstore/web/views/streams/index.erb +110 -0
  174. data/lib/pg_eventstore/web/views/streams/partials/streams.erb +11 -0
  175. data/lib/pg_eventstore/web/views/subscriptions/index.erb +4 -1
  176. data/lib/pg_eventstore/web.rb +2 -0
  177. data/lib/pg_eventstore.rb +23 -60
  178. data/pg_eventstore.gemspec +6 -4
  179. data/sig/interfaces/_raw_events_handler.rbs +3 -0
  180. data/sig/interfaces/subscription_handler.rbs +1 -1
  181. data/sig/pg/result.rbs +9 -0
  182. data/sig/pg_eventstore/async_runner.rbs +13 -0
  183. data/sig/pg_eventstore/basic_config.rbs +9 -0
  184. data/sig/pg_eventstore/chunks/chunk.rbs +13 -0
  185. data/sig/pg_eventstore/chunks/read_api_events_index_chunk.rbs +30 -0
  186. data/sig/pg_eventstore/chunks/replica_events_index_chunk.rbs +11 -0
  187. data/sig/pg_eventstore/chunks/repository.rbs +22 -0
  188. data/sig/pg_eventstore/chunks/subscription_checkpoint_chunk.rbs +18 -0
  189. data/sig/pg_eventstore/chunks/subscription_events_index_chunk.rbs +37 -0
  190. data/sig/pg_eventstore/client.rbs +40 -42
  191. data/sig/pg_eventstore/commands/append.rbs +11 -25
  192. data/sig/pg_eventstore/commands/event_modifiers/prepare_link_event.rbs +9 -12
  193. data/sig/pg_eventstore/commands/event_modifiers/prepare_regular_event.rbs +5 -4
  194. data/sig/pg_eventstore/commands/link_to.rbs +6 -11
  195. data/sig/pg_eventstore/commands/read.rbs +2 -5
  196. data/sig/pg_eventstore/commands/read_grouped.rbs +7 -0
  197. data/sig/pg_eventstore/commands/read_streams.rbs +7 -0
  198. data/sig/pg_eventstore/commands/read_streams_paginated.rbs +16 -0
  199. data/sig/pg_eventstore/commands/regular_stream_read_paginated.rbs +9 -17
  200. data/sig/pg_eventstore/commands/revision_check/current_revision.rbs +25 -0
  201. data/sig/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rbs +14 -0
  202. data/sig/pg_eventstore/commands/revision_check/expected_revision.rbs +67 -0
  203. data/sig/pg_eventstore/commands/revision_check/stream_revision_check.rbs +20 -0
  204. data/sig/pg_eventstore/commands/revision_check/stream_revision_comparison.rbs +9 -0
  205. data/sig/pg_eventstore/commands/stream_revision.rbs +7 -0
  206. data/sig/pg_eventstore/commands/system_stream_read_paginated.rbs +9 -14
  207. data/sig/pg_eventstore/config.rbs +15 -6
  208. data/sig/pg_eventstore/connection.rbs +11 -18
  209. data/sig/pg_eventstore/errors.rbs +64 -34
  210. data/sig/pg_eventstore/event.rbs +16 -2
  211. data/sig/pg_eventstore/event_deserializer.rbs +5 -10
  212. data/sig/pg_eventstore/event_global_index.rbs +60 -0
  213. data/sig/pg_eventstore/event_marker.rbs +9 -0
  214. data/sig/pg_eventstore/event_marker_index.rbs +12 -0
  215. data/sig/pg_eventstore/extensions/acts_as_configurable.rbs +29 -0
  216. data/sig/pg_eventstore/extensions/options_defaults.rbs +7 -0
  217. data/sig/pg_eventstore/feature_marker.rbs +10 -0
  218. data/sig/pg_eventstore/maintenance.rbs +12 -8
  219. data/sig/pg_eventstore/middleware/event_trace.rbs +14 -0
  220. data/sig/pg_eventstore/partition.rbs +2 -4
  221. data/sig/pg_eventstore/pg_connection.rbs +8 -0
  222. data/sig/pg_eventstore/queries/event_marker_queries.rbs +21 -0
  223. data/sig/pg_eventstore/queries/event_queries.rbs +4 -36
  224. data/sig/pg_eventstore/queries/events_global_index_queries.rbs +37 -0
  225. data/sig/pg_eventstore/queries/index_filtering_queries.rbs +50 -0
  226. data/sig/pg_eventstore/queries/links_resolver.rbs +5 -9
  227. data/sig/pg_eventstore/queries/maintenance_queries.rbs +12 -9
  228. data/sig/pg_eventstore/queries/partition_queries.rbs +32 -67
  229. data/sig/pg_eventstore/queries/replica_queries.rbs +31 -0
  230. data/sig/pg_eventstore/queries/streams_global_index_queries.rbs +39 -0
  231. data/sig/pg_eventstore/queries/transaction_queries.rbs +2 -0
  232. data/sig/pg_eventstore/queries.rbs +11 -21
  233. data/sig/pg_eventstore/query_builders/basic_filtering.rbs +10 -3
  234. data/sig/pg_eventstore/query_builders/event_markers_filtering.rbs +17 -0
  235. data/sig/pg_eventstore/query_builders/event_markers_index_filtering.rbs +69 -0
  236. data/sig/pg_eventstore/query_builders/event_subscription_positions_filtering.rbs +15 -0
  237. data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +9 -51
  238. data/sig/pg_eventstore/query_builders/events_global_index_filtering.rbs +68 -0
  239. data/sig/pg_eventstore/query_builders/filters/collection.rbs +84 -0
  240. data/sig/pg_eventstore/query_builders/filters/event_type_filter.rbs +19 -0
  241. data/sig/pg_eventstore/query_builders/filters/filter_row.rbs +21 -0
  242. data/sig/pg_eventstore/query_builders/filters/marker_filter.rbs +13 -0
  243. data/sig/pg_eventstore/query_builders/filters/marker_filter_row.rbs +19 -0
  244. data/sig/pg_eventstore/query_builders/filters/stream_filter.rbs +24 -0
  245. data/sig/pg_eventstore/query_builders/index_based_events_filtering.rbs +60 -0
  246. data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +29 -10
  247. data/sig/pg_eventstore/query_builders/read_cursor/stream_cursor.rbs +47 -0
  248. data/sig/pg_eventstore/query_builders/streams_global_index_filtering.rbs +27 -0
  249. data/sig/pg_eventstore/query_builders/subscription_events_filtering.rbs +26 -0
  250. data/sig/pg_eventstore/query_strategy/async.rbs +17 -0
  251. data/sig/pg_eventstore/query_strategy/foreground.rbs +11 -0
  252. data/sig/pg_eventstore/query_strategy.rbs +7 -0
  253. data/sig/pg_eventstore/raw_event.rbs +20 -0
  254. data/sig/pg_eventstore/sql_builder.rbs +32 -10
  255. data/sig/pg_eventstore/stream.rbs +10 -18
  256. data/sig/pg_eventstore/stream_global_index.rbs +14 -0
  257. data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +5 -4
  258. data/sig/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rbs +9 -0
  259. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +17 -12
  260. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +17 -6
  261. data/sig/pg_eventstore/subscriptions/event_subscription_position_queries.rbs +30 -0
  262. data/sig/pg_eventstore/subscriptions/events_processor.rbs +12 -5
  263. data/sig/pg_eventstore/subscriptions/events_processor_consumer/multiple.rbs +17 -0
  264. data/sig/pg_eventstore/subscriptions/events_processor_consumer/replica.rbs +17 -0
  265. data/sig/pg_eventstore/subscriptions/events_processor_consumer/single.rbs +17 -0
  266. data/sig/pg_eventstore/subscriptions/events_processor_consumer.rbs +14 -0
  267. data/sig/pg_eventstore/subscriptions/events_subscription_position_worker.rbs +28 -0
  268. data/sig/pg_eventstore/subscriptions/queries/subscription_queries.rbs +7 -39
  269. data/sig/pg_eventstore/subscriptions/replica_subscription_handler.rbs +33 -0
  270. data/sig/pg_eventstore/subscriptions/replica_subscription_runner.rbs +4 -0
  271. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/collection.rbs +14 -0
  272. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/index_read_strategy.rbs +23 -0
  273. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/replication_strategy.rbs +23 -0
  274. data/sig/pg_eventstore/subscriptions/subscription_feed_strategy.rbs +11 -0
  275. data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +13 -10
  276. data/sig/pg_eventstore/subscriptions/subscription_handler_performance.rbs +1 -1
  277. data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +13 -2
  278. data/sig/pg_eventstore/subscriptions/subscription_runners_feeder.rbs +1 -8
  279. data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +4 -6
  280. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +29 -43
  281. data/sig/pg_eventstore/synchronized_array.rbs +4 -0
  282. data/sig/pg_eventstore/utils.rbs +10 -11
  283. data/sig/pg_eventstore/web/application.rbs +13 -3
  284. data/sig/pg_eventstore/web/paginator/base_collection.rbs +2 -0
  285. data/sig/pg_eventstore/web/paginator/event_types_collection.rbs +4 -0
  286. data/sig/pg_eventstore/web/paginator/events_collection.rbs +11 -11
  287. data/sig/pg_eventstore/web/paginator/helpers.rbs +6 -15
  288. data/sig/pg_eventstore/web/paginator/markers_collection.rbs +13 -0
  289. data/sig/pg_eventstore/web/paginator/stream_contexts_collection.rbs +4 -0
  290. data/sig/pg_eventstore/web/paginator/stream_names_collection.rbs +4 -0
  291. data/sig/pg_eventstore/web/paginator/streams_collection.rbs +30 -0
  292. data/sig/pg_eventstore.rbs +8 -26
  293. metadata +175 -20
  294. data/Dockerfile +0 -3
  295. data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +0 -69
  296. data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +0 -31
  297. data/lib/pg_eventstore/subscriptions/queries/subscription_service_queries.rb +0 -78
  298. data/lib/pg_eventstore/web/views/home/partials/system_stream_filter.erb +0 -15
  299. data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +0 -16
  300. data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +0 -8
  301. data/sig/pg_eventstore/subscriptions/queries/subscription_service_queries.rbs +0 -19
  302. data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +0 -4
  303. /data/sig/interfaces/{_raw_event_handler.rbs → raw_event_handler.rbs} +0 -0
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ module Filters
6
+ # @!visibility private
7
+ class MarkerFilterRow
8
+ include Extensions::OptionsExtension
9
+ include Extensions::OptionsDefaults
10
+
11
+ # @!attribute stream_filter
12
+ # @return [PgEventstore::QueryBuilders::Filters::StreamFilter, nil]
13
+ attribute(:stream_filter)
14
+ # @!attribute stream_filter
15
+ # @return [PgEventstore::QueryBuilders::Filters::MarkerFilter]
16
+ attribute(:marker_filter)
17
+
18
+ # @return [Array<PgEventstore::QueryBuilders::Filters::MarkerFilterRow>]
19
+ def flatten
20
+ return [dup] if marker_filter.markers.size == 1
21
+
22
+ marker_filter.markers.map do |marker|
23
+ marker_filter = MarkerFilter.new(event_type: self.marker_filter.event_type, markers: [marker])
24
+ self.class.new(stream_filter:, marker_filter:)
25
+ end
26
+ end
27
+
28
+ # Event type is ambiguous in case we can't tell which partition it belongs to. We **can** tell the exact
29
+ # partition of the given event type only when we have context and stream name as well.
30
+ # @return [Boolean]
31
+ def ambiguous_event_type?
32
+ return true if !stream_filter.nil? && !stream_filter.stream? && marker_filter.event_type.nil?
33
+
34
+ (stream_filter.nil? || stream_filter.context?) && !marker_filter.event_type.nil?
35
+ end
36
+
37
+ # @return [PgEventstore::QueryBuilders::Filters::FilterRow]
38
+ def to_filter_row
39
+ if marker_filter.event_type
40
+ event_type_filter = EventTypeFilter.new(event_type: marker_filter.event_type, prefix: false)
41
+ FilterRow.new(stream_filter:, event_type_filters: [event_type_filter])
42
+ else
43
+ FilterRow.new(stream_filter:, event_type_filters: [])
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ module Filters
6
+ # @!visibility private
7
+ class StreamFilter
8
+ include Extensions::OptionsExtension
9
+ include Extensions::OptionsDefaults
10
+
11
+ # @!attribute context
12
+ # @return [String]
13
+ attribute(:context)
14
+ # @!attribute stream_name
15
+ # @return [String, nil]
16
+ attribute(:stream_name)
17
+ # @!attribute stream_id
18
+ # @return [String, nil]
19
+ attribute(:stream_id)
20
+
21
+ # @return [Boolean]
22
+ def context?
23
+ stream_name.nil? && stream_id.nil?
24
+ end
25
+
26
+ # @return [Boolean]
27
+ def stream_name?
28
+ !stream_name.nil? && stream_id.nil?
29
+ end
30
+
31
+ # @return [Boolean]
32
+ def stream?
33
+ !stream_name.nil? && !stream_id.nil?
34
+ end
35
+
36
+ # @return [Hash]
37
+ def to_partition_h
38
+ { context:, stream_name: }.compact
39
+ end
40
+
41
+ # @return [Hash]
42
+ def to_h
43
+ { context:, stream_name:, stream_id: }.compact
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ class IndexBasedEventsFiltering
6
+ include BasicFiltering
7
+
8
+ # @return [Integer]
9
+ DEFAULT_LIMIT = 1_000
10
+ # @return [String]
11
+ ABSTRACT_TABLE_NAME = 'events_index'
12
+
13
+ class << self
14
+ # @param stream [PgEventstore::Stream]
15
+ # @param expected_revision [PgEventstore::Commands::RevisionCheck::ExpectedRevision::EventTypeRevision]
16
+ # @return [PgEventstore::SQLBuilder]
17
+ def sql_builder_for_event_type_revision_validation(stream, expected_revision)
18
+ cursor = QueryBuilders::ReadCursor::StreamCursor.from_stream_and_options(
19
+ stream, { direction: :desc, max_count: 1 }
20
+ )
21
+ filters_collection = Filters::Collection.from_stream_and_options(
22
+ stream, { filter: { event_types: [expected_revision.event_type] } }
23
+ )
24
+ rows = filters_collection.collection
25
+ Utils.assert!(rows.size == 1, filters_collection.inspect)
26
+
27
+ EventsGlobalIndexFiltering.for_revision_validation_per_type(
28
+ rows.first, cursor, expected_revision.sequence_number
29
+ )
30
+ end
31
+
32
+ # @param stream [PgEventstore::Stream]
33
+ # @param expected_revision [PgEventstore::Commands::RevisionCheck::ExpectedRevision::EventTypeRevisionWithMarkers]
34
+ # @return [PgEventstore::SQLBuilder]
35
+ def sql_builder_for_event_type_with_markers_revision_validation(stream, expected_revision)
36
+ cursor = QueryBuilders::ReadCursor::StreamCursor.from_stream_and_options(
37
+ stream, { direction: :desc, max_count: 1 }
38
+ )
39
+ filters_collection = Filters::Collection.from_stream_and_options(
40
+ stream,
41
+ {
42
+ filter: {
43
+ event_types: [{ type: expected_revision.event_type, markers: expected_revision.markers }],
44
+ },
45
+ }
46
+ )
47
+ rows = filters_collection.collection
48
+ Utils.assert!(rows.size == 1, filters_collection.inspect)
49
+
50
+ builders = rows.first.flatten.map do |flattened|
51
+ EventMarkersIndexFiltering.for_revision_validation_per_type(
52
+ flattened, cursor, expected_revision.sequence_number
53
+ )
54
+ end
55
+ final = SQLBuilder.new.select('*')
56
+ final.from(SQLBuilder.union_builders(builders, mode: :union_distinct))
57
+ final.order('stream_revision desc')
58
+ final.limit(1)
59
+ end
60
+
61
+ # @param stream [PgEventstore::Stream]
62
+ # @param expected_revision [PgEventstore::Commands::RevisionCheck::ExpectedRevision::MarkersRevision]
63
+ # @return [PgEventstore::SQLBuilder]
64
+ def sql_builder_for_markers_revision_validation(stream, expected_revision)
65
+ cursor = QueryBuilders::ReadCursor::StreamCursor.from_stream_and_options(
66
+ stream, { direction: :desc, max_count: 1 }
67
+ )
68
+ filters_collection = Filters::Collection.from_stream_and_options(
69
+ stream, { filter: { event_types: [{ markers: expected_revision.markers }] } }
70
+ )
71
+ rows = filters_collection.collection
72
+ Utils.assert!(rows.size == 1, filters_collection.inspect)
73
+
74
+ builders = rows.first.flatten.map do |flattened|
75
+ EventMarkersIndexFiltering.for_revision_validation_per_type(
76
+ flattened, cursor, expected_revision.sequence_number
77
+ )
78
+ end
79
+ final = SQLBuilder.new.select('*')
80
+ final.from(SQLBuilder.union_builders(builders, mode: :union_distinct))
81
+ final.order('stream_revision desc')
82
+ final.limit(1)
83
+ end
84
+
85
+ # @param filter_rows [Array<PgEventstore::QueryBuilders::Filters::FilterRow,
86
+ # PgEventstore::QueryBuilders::Filters::MarkerFilterRow>]
87
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
88
+ # @return [PgEventstore::SQLBuilder]
89
+ def sql_builder_for_read_grouped(filter_rows, cursor)
90
+ cursor = cursor.dup
91
+ cursor.max_count = 1
92
+ has_markers = false
93
+ builders = filter_rows.flat_map do |filter_row|
94
+ case filter_row
95
+ when Filters::FilterRow
96
+ filter_row.flatten.map do |flattened|
97
+ EventsGlobalIndexFiltering.for_read_common(flattened, cursor)
98
+ end
99
+ when Filters::MarkerFilterRow
100
+ has_markers ||= true
101
+ filter_row.flatten.map do |flattened|
102
+ EventMarkersIndexFiltering.for_read_common(flattened, cursor)
103
+ end
104
+ else
105
+ Utils.missing_implementation!(filter_row)
106
+ end
107
+ end
108
+ union_builders(builders, cursor, mode: has_markers ? :union_distinct : :union_all, requires_limit: false)
109
+ end
110
+
111
+ # @param filter_rows [Array<PgEventstore::QueryBuilders::Filters::FilterRow,
112
+ # PgEventstore::QueryBuilders::Filters::MarkerFilterRow>]
113
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
114
+ # @return [PgEventstore::SQLBuilder]
115
+ def sql_builder_for_read_common(filter_rows, cursor)
116
+ return EventsGlobalIndexFiltering.default_filtering(cursor).to_sql_builder if filter_rows.empty?
117
+
118
+ has_markers = false
119
+ builders = filter_rows.flat_map do |filter_row|
120
+ case filter_row
121
+ when Filters::FilterRow
122
+ filter_row.flatten.map do |flattened|
123
+ EventsGlobalIndexFiltering.for_read_common(flattened, cursor)
124
+ end
125
+ when Filters::MarkerFilterRow
126
+ has_markers ||= true
127
+ filter_row.flatten.map do |flattened|
128
+ EventMarkersIndexFiltering.for_read_common(flattened, cursor)
129
+ end
130
+ else
131
+ Utils.missing_implementation!(filter_row)
132
+ end
133
+ end
134
+ union_builders(builders, cursor, mode: has_markers ? :union_distinct : :union_all)
135
+ end
136
+
137
+ # @param filter_rows [Array<PgEventstore::QueryBuilders::Filters::FilterRow,
138
+ # PgEventstore::QueryBuilders::Filters::MarkerFilterRow>]
139
+ # @return [PgEventstore::SQLBuilder]
140
+ def sql_builder_for_estimate_count(filter_rows)
141
+ cursor = ReadCursor::StreamCursor.from_options({})
142
+ if filter_rows.empty?
143
+ return EventsGlobalIndexFiltering.default_filtering(cursor).to_sql_builder.remove_limit.remove_order
144
+ end
145
+
146
+ has_markers = false
147
+ builders = filter_rows.flat_map do |filter_row|
148
+ case filter_row
149
+ when Filters::FilterRow
150
+ filter_row.flatten.map do |flattened|
151
+ EventsGlobalIndexFiltering.for_read_common(flattened, cursor)
152
+ end
153
+ when Filters::MarkerFilterRow
154
+ has_markers ||= true
155
+ filter_row.flatten.map do |flattened|
156
+ EventMarkersIndexFiltering.for_read_common(flattened, cursor)
157
+ end
158
+ else
159
+ Utils.missing_implementation!(filter_row)
160
+ end
161
+ end
162
+ builders.each { _1.remove_limit.remove_order }
163
+ top_builder = SQLBuilder.new.select('*')
164
+ top_builder.from(SQLBuilder.union_builders(builders, mode: has_markers ? :union_distinct : :union_all))
165
+ top_builder
166
+ end
167
+
168
+ # @param filter_rows [Array<PgEventstore::QueryBuilders::Filters::FilterRow,
169
+ # PgEventstore::QueryBuilders::Filters::MarkerFilterRow>]
170
+ # @return [PgEventstore::SQLBuilder]
171
+ def sql_builder_for_subscriptions(filter_rows)
172
+ if filter_rows.empty?
173
+ sql_builder = EventsGlobalIndexFiltering.new.tap(&:for_subscription).to_sql_builder
174
+ return finalize_subscription_builders([sql_builder])
175
+ end
176
+
177
+ builders = []
178
+ grouped = filter_rows.group_by(&:class)
179
+ grouped.each_value do |rows|
180
+ case rows.first
181
+ when Filters::FilterRow
182
+ filtering = EventsGlobalIndexFiltering.new
183
+ filtering.for_subscription
184
+ rows.each(&filtering.method(:add_filter_row))
185
+ builders.push(filtering.to_sql_builder)
186
+ when Filters::MarkerFilterRow
187
+ filtering = EventMarkersIndexFiltering.new
188
+ filtering.for_subscription
189
+ rows.each(&filtering.method(:add_marker_filter_row))
190
+ builders.push(filtering.to_sql_builder)
191
+ else
192
+ Utils.missing_implementation!(rows.first)
193
+ end
194
+ end
195
+
196
+ finalize_subscription_builders(builders)
197
+ end
198
+
199
+ private
200
+
201
+ # @param builders [Array<PgEventstore::SQLBuilder>]
202
+ # @return [PgEventstore::SQLBuilder]
203
+ def finalize_subscription_builders(builders)
204
+ return builders.first if builders.size == 1
205
+
206
+ final = SQLBuilder.new.select(
207
+ 'events_idx.global_position, events_idx.event_type_partition_id, events_idx.subscription_position'
208
+ )
209
+ final.from(SQLBuilder.union_builders(builders, mode: :union_distinct), table_alias: 'events_idx')
210
+ final.order('events_idx.subscription_position')
211
+ final.limit('max_count')
212
+ end
213
+
214
+ # @param builders [Array<PgEventstore::SQLBuilder>]
215
+ # @param cursor [PgEventstore::QueryBuilders::ReadCursor::StreamCursor]
216
+ # @param mode [Symbol]
217
+ # @param requires_limit [Boolean]
218
+ # @return [PgEventstore::SQLBuilder]
219
+ def union_builders(builders, cursor, mode:, requires_limit: true)
220
+ return builders.first if builders.size == 1
221
+
222
+ union_builder = SQLBuilder.union_builders(builders, mode:)
223
+ top_filtering = new
224
+ # Union builder always has fixed columns in SELECT (global_position and event_type_partition_id). Thus,
225
+ # define SystemStreamOptions that is resolved to the sorting by global_position instead of possible
226
+ # sorting by stream_revision which may not be present among the columns list
227
+ cursor = ReadCursor::StreamCursor.from_options(direction: cursor.direction, max_count: cursor.max_count)
228
+ top_filtering.add_global_position_direction(cursor.direction)
229
+ top_filtering.add_limit(cursor.max_count) if requires_limit
230
+ top_filtering.to_sql_builder.from(union_builder)
231
+ end
232
+ end
233
+
234
+ def initialize
235
+ @sql_builder = SQLBuilder.new
236
+ @sql_builder.select('global_position, event_type_partition_id')
237
+ end
238
+
239
+ def to_table_name
240
+ ''
241
+ end
242
+
243
+ def to_sql_builder
244
+ @sql_builder
245
+ end
246
+
247
+ # @param direction [String, Symbol, nil]
248
+ # @return [void]
249
+ def add_global_position_direction(direction)
250
+ @sql_builder.order("global_position #{SQL_DIRECTIONS[direction]}")
251
+ end
252
+
253
+ # @param limit [Integer, nil]
254
+ # @return [void]
255
+ def add_limit(limit)
256
+ @sql_builder.limit(limit || DEFAULT_LIMIT)
257
+ end
258
+ end
259
+ end
260
+ end
@@ -3,88 +3,212 @@
3
3
  module PgEventstore
4
4
  module QueryBuilders
5
5
  # @!visibility private
6
- class PartitionsFiltering < BasicFiltering
6
+ class PartitionsFiltering
7
+ include BasicFiltering
8
+
7
9
  # @return [String]
8
10
  TABLE_NAME = 'partitions'
9
- private_constant :TABLE_NAME
10
11
 
11
12
  class << self
12
- # @param options [Hash]
13
- # @return [Array<String>]
14
- def extract_event_types_filter(options)
15
- options in { filter: { event_types: Array => event_types } }
16
- event_types = event_types&.select { _1.is_a?(String) }
17
- event_types || []
18
- end
13
+ # @param filters_collection [PgEventstore::QueryBuilders::Filters::Collection]
14
+ # @param scope [Symbol] what kind of partition we want to receive. Available options are :event_type, :context,
15
+ # :stream_name and :auto. In :auto mode the scope will be calculated based on stream_filters and event_filters
16
+ # @return [PgEventstore::SQLBuilder]
17
+ def assemble_sql_builder(filters_collection, scope: :event_type)
18
+ if filters_collection.has_event_types?
19
+ # When event type filters are present - they apply constraints as is. Thus, we can't look up partitions by
20
+ # stream attributes separately.
21
+ filtering = new
22
+ filters_collection.collection.each(&filtering.method(:add_filter_row))
23
+ set_partitions_scope(filtering, has_stream_name_filter?(filters_collection), true, scope)
24
+ else
25
+ # When event type filters are absent - we can look up partitions by context and context/stream_name
26
+ # separately, thus potentially producing one-to-one mapping of filter-to-partition with :auto scope. For
27
+ # example, let's say we have stream attributes filter like
28
+ # [{ context: 'FooCtx', stream_name: 'Bar'}, { context: 'BarCtx' }], then we would be able to look up
29
+ # partitions by the exact match, returning only two of them according to the provided filters - stream
30
+ # partition for first filter and context partition for second filter.
31
+ builders = filters_collection.collection.map do |filter_row|
32
+ filtering = new
33
+ filtering.add_filter_row(filter_row)
34
+ set_partitions_scope(filtering, filter_row.stream_filter.stream_name?, false, scope)
35
+ end
19
36
 
20
- # @param options [Hash]
21
- # @return [Array<Hash[Symbol, String]>]
22
- def extract_streams_filter(options)
23
- options in { filter: { streams: Array => streams } }
24
- streams = streams&.map do |stream_attrs|
25
- stream_attrs in { context: String | NilClass => context }
26
- stream_attrs in { stream_name: String | NilClass => stream_name }
27
- { context:, stream_name: }
37
+ sql_builder = SQLBuilder.union_builders(builders) if builders.any?
38
+ sql_builder ||
39
+ begin
40
+ filtering = new
41
+ set_partitions_scope(filtering, has_stream_name_filter?(filters_collection), false, scope)
42
+ end
28
43
  end
29
- streams || []
30
44
  end
31
45
 
32
- # @param stream_attrs [Hash]
46
+ # @param filter_row [PgEventstore::QueryBuilders::Filters::FilterRow]
47
+ # @param scope [Symbol]
48
+ # @return [PgEventstore::SQLBuilder]
49
+ def from_filter_row(filter_row, scope: :event_type)
50
+ filtering = new
51
+ filtering.add_filter_row(filter_row)
52
+ has_stream_name_filter = filter_row.stream_filter&.stream_name? || false
53
+ has_event_type_filters = filter_row.event_type_filters.any?
54
+ set_partitions_scope(filtering, has_stream_name_filter, has_event_type_filters, scope)
55
+ end
56
+
57
+ private
58
+
59
+ # @param filters_collection [PgEventstore::QueryBuilders::Filters::Collection]
33
60
  # @return [Boolean]
34
- def correct_stream_filter?(stream_attrs)
35
- result = (stream_attrs in { context: String, stream_name: String } | { context: String })
36
- return true if result
37
-
38
- PgEventstore.logger&.debug(<<~TEXT)
39
- Ignoring unsupported stream filter format for grouped read #{stream_attrs.compact.inspect}. \
40
- See docs/reading_events.md docs for supported formats.
41
- TEXT
42
- false
61
+ # rubocop:disable Naming/PredicatePrefix
62
+ def has_stream_name_filter?(filters_collection)
63
+ filters_collection.collection.any? { |filter_row| filter_row.stream_filter&.stream_name? }
64
+ end
65
+ # rubocop:enable Naming/PredicatePrefix
66
+
67
+ # @param partitions_filtering [PgEventstore::QueryBuilders::PartitionsFiltering]
68
+ # @param has_stream_name_filters [Boolean]
69
+ # @param has_event_type_filters [Boolean]
70
+ # @param scope [Symbol]
71
+ # @return [PgEventstore::SQLBuilder]
72
+ def set_partitions_scope(partitions_filtering, has_stream_name_filters, has_event_type_filters, scope)
73
+ case scope
74
+ when :event_type
75
+ partitions_filtering.with_event_types
76
+ partitions_filtering.order_by_event_type
77
+ partitions_filtering.to_sql_builder
78
+ when :stream_name
79
+ filter = QueryBuilders::PartitionsFiltering.new
80
+ filter.without_event_types
81
+ filter.order_by_stream_name
82
+ table_name = filter.to_table_name
83
+ builder = filter.to_sql_builder
84
+ builder.where(
85
+ "(#{table_name}.context, #{table_name}.stream_name) in ?",
86
+ partitions_filtering.
87
+ to_sql_builder.
88
+ unselect.
89
+ select(<<~SQL)
90
+ distinct on (#{table_name}.context, #{table_name}.stream_name)
91
+ #{table_name}.context, #{table_name}.stream_name
92
+ SQL
93
+ )
94
+ when :context
95
+ filter = QueryBuilders::PartitionsFiltering.new
96
+ filter.without_event_types
97
+ filter.without_stream_names
98
+ filter.order_by_context
99
+ builder = filter.to_sql_builder
100
+ builder.where(
101
+ "#{filter.to_table_name}.context in ?",
102
+ partitions_filtering.
103
+ to_sql_builder.
104
+ unselect.
105
+ select("distinct on (#{filter.to_table_name}.context) #{filter.to_table_name}.context")
106
+ )
107
+ when :auto
108
+ if has_event_type_filters
109
+ set_partitions_scope(partitions_filtering, has_stream_name_filters, has_event_type_filters, :event_type)
110
+ elsif has_stream_name_filters
111
+ set_partitions_scope(partitions_filtering, has_stream_name_filters, has_event_type_filters, :stream_name)
112
+ else
113
+ set_partitions_scope(partitions_filtering, has_stream_name_filters, has_event_type_filters, :context)
114
+ end
115
+ else
116
+ Utils.missing_implementation!(scope)
117
+ end
43
118
  end
44
119
  end
45
120
 
121
+ def initialize
122
+ @sql_builder = SQLBuilder.new.select("#{to_table_name}.*").from(to_table_name)
123
+ @has_event_types = []
124
+ @has_stream_names = []
125
+ @wants_event_type = false
126
+ @wants_stream_name = false
127
+ end
128
+
129
+ def to_sql_builder
130
+ if @wants_event_type && (@has_event_types.empty? || !@has_event_types.all?)
131
+ @sql_builder.where("#{to_table_name}.event_type IS NOT NULL")
132
+ end
133
+ if @wants_stream_name && (@has_stream_names.empty? || !@has_stream_names.all?)
134
+ @sql_builder.where("#{to_table_name}.stream_name IS NOT NULL")
135
+ end
136
+ @sql_builder
137
+ end
138
+
46
139
  # @return [String]
47
140
  def to_table_name
48
141
  TABLE_NAME
49
142
  end
50
143
 
51
- # @param context [String, nil]
52
- # @param stream_name [String, nil]
53
- # @return [PgEventstore::SQLBuilder]
54
- def add_stream_attrs(context: nil, stream_name: nil)
55
- stream_attrs = { context:, stream_name: }
56
- return @sql_builder unless self.class.correct_stream_filter?(stream_attrs)
57
-
58
- stream_attrs.compact!
59
- sql = stream_attrs.map do |attr, _|
60
- "#{to_table_name}.#{attr} = ?"
61
- end.join(' AND ')
62
- @sql_builder.where_or(sql, *stream_attrs.values)
63
- end
144
+ # @param filter_row [PgEventstore::QueryBuilders::Filters::FilterRow]
145
+ # @return [void]
146
+ def add_filter_row(filter_row)
147
+ stream_filter = filter_row.stream_filter
148
+ event_type_filters = filter_row.event_type_filters
149
+ @has_stream_names.push(stream_filter&.stream_name? || false)
150
+ @has_event_types.push(event_type_filters.any?)
64
151
 
65
- # @param event_types [Array<String>]
66
- # @return [PgEventstore::SQLBuilder]
67
- def add_event_types(event_types)
68
- return @sql_builder if event_types.empty?
152
+ stream_parts_filters = []
153
+ event_types_filters = []
154
+ stream_filter&.to_partition_h&.each do |attr, value|
155
+ stream_parts_filters.push(["#{to_table_name}.#{attr} = ?", value])
156
+ end
157
+ event_type_filters.each do |event_type_filter|
158
+ comparison_part = event_type_filter.prefix? ? 'like' : '='
159
+ event_types_filters.push(
160
+ ["#{to_table_name}.event_type #{comparison_part} ?", event_type_filter.to_sql_value]
161
+ )
162
+ end
69
163
 
70
- @sql_builder.where("#{to_table_name}.event_type = ANY(?::varchar[])", event_types)
164
+ stream_parts_filters_query, stream_parts_filters_values = stream_parts_filters.transpose
165
+ event_types_query, event_types_values = event_types_filters.transpose
166
+ sql = ''
167
+ sql += stream_parts_filters_query.join(' and ') if stream_parts_filters_query&.any?
168
+ sql += ' and ' if sql != '' && event_types_query&.any?
169
+ sql += "(#{event_types_query.join(' or ')})" if event_types_query&.any?
170
+ @sql_builder.where_or(sql, *stream_parts_filters_values, *event_types_values)
171
+ nil
71
172
  end
72
173
 
73
- # @return [PgEventstore::SQLBuilder]
174
+ # @return [void]
74
175
  def with_event_types
75
- @sql_builder.where('event_type IS NOT NULL')
176
+ @wants_event_type = true
76
177
  end
77
178
 
179
+ # @return [void]
78
180
  def with_stream_names
79
- @sql_builder.where('stream_name IS NOT NULL')
181
+ @wants_stream_name = true
80
182
  end
81
183
 
184
+ # @return [void]
82
185
  def without_event_types
83
- @sql_builder.where('event_type IS NULL')
186
+ @sql_builder.where("#{to_table_name}.event_type IS NULL")
187
+ nil
84
188
  end
85
189
 
190
+ # @return [void]
86
191
  def without_stream_names
87
- @sql_builder.where('stream_name IS NULL')
192
+ @sql_builder.where("#{to_table_name}.stream_name IS NULL")
193
+ nil
194
+ end
195
+
196
+ # @return [void]
197
+ def order_by_event_type
198
+ @sql_builder.order("#{to_table_name}.event_type asc, #{to_table_name}.id asc")
199
+ nil
200
+ end
201
+
202
+ # @return [void]
203
+ def order_by_stream_name
204
+ @sql_builder.order("#{to_table_name}.context asc, #{to_table_name}.stream_name asc")
205
+ nil
206
+ end
207
+
208
+ # @return [void]
209
+ def order_by_context
210
+ @sql_builder.order("#{to_table_name}.context asc")
211
+ nil
88
212
  end
89
213
  end
90
214
  end