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.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -1
- data/CHANGELOG.md +186 -20
- data/README.md +25 -15
- data/db/migrations/10_setup_pg_cron.rb +1 -23
- data/db/migrations/15_create_streams_global_index.sql +17 -0
- data/db/migrations/16_populate_streams_global_index.rb +150 -0
- data/db/migrations/17_create_events_global_index.sql +62 -0
- data/db/migrations/18_populate_events_global_index.rb +124 -0
- data/db/migrations/19_populate_events_global_index_p2.rb +25 -0
- data/db/migrations/20_migrate_subscriptions_current_position.sql +19 -0
- data/db/migrations/21_create_partitions_stats.sql +2 -0
- data/db/migrations/22_drop_streams_sql_view.sql +2 -0
- data/db/migrations/23_adjust_partitions_indexes.sql +5 -0
- data/db/migrations/24_drop_legacy_events_indexes.sql +3 -0
- data/db/migrations/25_add_parent_partitions_info_to_partitions.sql +16 -0
- data/db/migrations/26_change_events_stream_revision_type.rb +35 -0
- data/db/migrations/27_drop_events_horizon.sql +3 -0
- data/db/migrations/28_create_maintenance_tasks.sql +8 -0
- data/db/migrations/29_create_event_markers.sql +35 -0
- data/db/migrations/30_improve_partitions_search_in_admin_ui.rb +24 -0
- data/db/migrations/31_drop_events_id_default_value.sql +1 -0
- data/db/migrations/32_create_parittions_triggers.sql +101 -0
- data/docs/AGENTS.fragment.md +696 -0
- data/docs/admin_ui.md +3 -1
- data/docs/appending_events.md +110 -49
- data/docs/configuration.md +69 -36
- data/docs/event_tracing.md +55 -0
- data/docs/events_and_streams.md +54 -30
- data/docs/how_it_works.md +57 -16
- data/docs/linking_events.md +2 -1
- data/docs/maintenance.md +14 -5
- data/docs/multiple_commands.md +17 -8
- data/docs/reading_events.md +208 -43
- data/docs/reading_streams.md +50 -0
- data/docs/replication.md +99 -0
- data/docs/subscriptions.md +165 -35
- data/docs/writing_middleware.md +25 -8
- data/ext/pg_eventstore_ext/extconf.rb +5 -0
- data/ext/pg_eventstore_ext/pg_eventstore_ext.c +74 -0
- data/lib/pg_eventstore/async_runner.rb +63 -0
- data/lib/pg_eventstore/basic_config.rb +15 -0
- data/lib/pg_eventstore/chunks/chunk.rb +31 -0
- data/lib/pg_eventstore/chunks/read_api_events_index_chunk.rb +83 -0
- data/lib/pg_eventstore/chunks/replica_events_index_chunk.rb +35 -0
- data/lib/pg_eventstore/chunks/repository.rb +73 -0
- data/lib/pg_eventstore/chunks/subscription_checkpoint_chunk.rb +48 -0
- data/lib/pg_eventstore/chunks/subscription_events_index_chunk.rb +99 -0
- data/lib/pg_eventstore/chunks.rb +14 -0
- data/lib/pg_eventstore/client.rb +163 -50
- data/lib/pg_eventstore/commands/append.rb +62 -43
- data/lib/pg_eventstore/commands/delete_event.rb +7 -8
- data/lib/pg_eventstore/commands/delete_stream.rb +3 -3
- data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +9 -7
- data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +12 -7
- data/lib/pg_eventstore/commands/link_to.rb +5 -5
- data/lib/pg_eventstore/commands/read.rb +23 -5
- data/lib/pg_eventstore/commands/read_grouped.rb +46 -0
- data/lib/pg_eventstore/commands/read_streams.rb +18 -0
- data/lib/pg_eventstore/commands/read_streams_paginated.rb +56 -0
- data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +4 -4
- data/lib/pg_eventstore/commands/revision_check/current_revision.rb +51 -0
- data/lib/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rb +46 -0
- data/lib/pg_eventstore/commands/revision_check/expected_revision.rb +135 -0
- data/lib/pg_eventstore/commands/revision_check/stream_revision_check.rb +98 -0
- data/lib/pg_eventstore/commands/revision_check/stream_revision_comparison.rb +36 -0
- data/lib/pg_eventstore/commands/stream_revision.rb +14 -0
- data/lib/pg_eventstore/commands/system_stream_read_paginated.rb +4 -4
- data/lib/pg_eventstore/commands.rb +4 -2
- data/lib/pg_eventstore/config.rb +30 -9
- data/lib/pg_eventstore/connection.rb +21 -2
- data/lib/pg_eventstore/errors.rb +136 -10
- data/lib/pg_eventstore/event.rb +34 -2
- data/lib/pg_eventstore/event_deserializer.rb +5 -4
- data/lib/pg_eventstore/event_global_index.rb +118 -0
- data/lib/pg_eventstore/event_marker.rb +16 -0
- data/lib/pg_eventstore/event_marker_index.rb +25 -0
- data/lib/pg_eventstore/event_serializer.rb +6 -0
- data/lib/pg_eventstore/extensions/acts_as_configurable.rb +88 -0
- data/lib/pg_eventstore/extensions/options_defaults.rb +25 -0
- data/lib/pg_eventstore/extensions/options_extension.rb +2 -2
- data/lib/pg_eventstore/feature_marker.rb +18 -0
- data/lib/pg_eventstore/maintenance.rb +11 -6
- data/lib/pg_eventstore/middleware/event_tracing.rb +53 -0
- data/lib/pg_eventstore/partition.rb +6 -0
- data/lib/pg_eventstore/pg_connection.rb +70 -14
- data/lib/pg_eventstore/queries/event_marker_queries.rb +74 -0
- data/lib/pg_eventstore/queries/event_queries.rb +7 -93
- data/lib/pg_eventstore/queries/events_global_index_queries.rb +131 -0
- data/lib/pg_eventstore/queries/index_filtering_queries.rb +179 -0
- data/lib/pg_eventstore/queries/links_resolver.rb +4 -4
- data/lib/pg_eventstore/queries/maintenance_queries.rb +159 -42
- data/lib/pg_eventstore/queries/partition_queries.rb +45 -103
- data/lib/pg_eventstore/queries/replica_queries.rb +110 -0
- data/lib/pg_eventstore/queries/streams_global_index_queries.rb +163 -0
- data/lib/pg_eventstore/queries/transaction_queries.rb +52 -10
- data/lib/pg_eventstore/queries.rb +29 -1
- data/lib/pg_eventstore/query_builders/basic_filtering.rb +38 -5
- data/lib/pg_eventstore/query_builders/event_markers_filtering.rb +47 -0
- data/lib/pg_eventstore/query_builders/event_markers_index_filtering.rb +275 -0
- data/lib/pg_eventstore/query_builders/event_subscription_positions_filtering.rb +37 -0
- data/lib/pg_eventstore/query_builders/events_filtering.rb +15 -164
- data/lib/pg_eventstore/query_builders/events_global_index_filtering.rb +227 -0
- data/lib/pg_eventstore/query_builders/filters/collection.rb +324 -0
- data/lib/pg_eventstore/query_builders/filters/event_type_filter.rb +37 -0
- data/lib/pg_eventstore/query_builders/filters/filter_row.rb +54 -0
- data/lib/pg_eventstore/query_builders/filters/marker_filter.rb +20 -0
- data/lib/pg_eventstore/query_builders/filters/marker_filter_row.rb +49 -0
- data/lib/pg_eventstore/query_builders/filters/stream_filter.rb +48 -0
- data/lib/pg_eventstore/query_builders/index_based_events_filtering.rb +260 -0
- data/lib/pg_eventstore/query_builders/partitions_filtering.rb +175 -51
- data/lib/pg_eventstore/query_builders/read_cursor/stream_cursor.rb +122 -0
- data/lib/pg_eventstore/query_builders/streams_global_index_filtering.rb +83 -0
- data/lib/pg_eventstore/query_builders/subscription_events_filtering.rb +79 -0
- data/lib/pg_eventstore/query_strategy/async.rb +64 -0
- data/lib/pg_eventstore/query_strategy/foreground.rb +25 -0
- data/lib/pg_eventstore/query_strategy.rb +19 -0
- data/lib/pg_eventstore/raw_event.rb +46 -0
- data/lib/pg_eventstore/rspec/test_helpers.rb +21 -8
- data/lib/pg_eventstore/sql_builder.rb +87 -8
- data/lib/pg_eventstore/stream.rb +13 -16
- data/lib/pg_eventstore/stream_global_index.rb +28 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +5 -17
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rb +36 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +12 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +21 -7
- data/lib/pg_eventstore/subscriptions/events_processor.rb +20 -18
- data/lib/pg_eventstore/subscriptions/events_processor_consumer/multiple.rb +63 -0
- data/lib/pg_eventstore/subscriptions/events_processor_consumer/replica.rb +68 -0
- data/lib/pg_eventstore/subscriptions/events_processor_consumer/single.rb +62 -0
- data/lib/pg_eventstore/subscriptions/events_processor_consumer.rb +44 -0
- data/lib/pg_eventstore/subscriptions/events_subscription_position_worker.rb +68 -0
- data/lib/pg_eventstore/subscriptions/queries/event_subscription_position_queries.rb +177 -0
- data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +78 -54
- data/lib/pg_eventstore/subscriptions/replica_subscription_handler.rb +140 -0
- data/lib/pg_eventstore/subscriptions/replica_subscription_runner.rb +9 -0
- data/lib/pg_eventstore/subscriptions/subscription.rb +2 -1
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/collection.rb +36 -0
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/index_read_strategy.rb +82 -0
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategies/replication_strategy.rb +75 -0
- data/lib/pg_eventstore/subscriptions/subscription_feed_strategy.rb +31 -0
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +24 -0
- data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +4 -2
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +20 -9
- data/lib/pg_eventstore/subscriptions/subscription_runner_commands/reset_position.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +9 -18
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +0 -12
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +74 -14
- data/lib/pg_eventstore/{subscriptions/synchronized_array.rb → synchronized_array.rb} +15 -0
- data/lib/pg_eventstore/tasks/setup.rake +2 -2
- data/lib/pg_eventstore/utils.rb +31 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +100 -16
- data/lib/pg_eventstore/web/paginator/base_collection.rb +5 -1
- data/lib/pg_eventstore/web/paginator/event_types_collection.rb +14 -4
- data/lib/pg_eventstore/web/paginator/events_collection.rb +29 -33
- data/lib/pg_eventstore/web/paginator/helpers.rb +16 -9
- data/lib/pg_eventstore/web/paginator/markers_collection.rb +48 -0
- data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +13 -2
- data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +21 -11
- data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +18 -4
- data/lib/pg_eventstore/web/paginator/streams_collection.rb +95 -0
- data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +49 -22
- data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/with_state/subscriptions.rb +1 -1
- data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -9
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +18 -5
- data/lib/pg_eventstore/web/views/home/partials/events.erb +38 -1
- data/lib/pg_eventstore/web/views/home/partials/markers_filter.erb +18 -0
- data/lib/pg_eventstore/web/views/layouts/application.erb +6 -0
- data/lib/pg_eventstore/web/views/streams/index.erb +110 -0
- data/lib/pg_eventstore/web/views/streams/partials/streams.erb +11 -0
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +4 -1
- data/lib/pg_eventstore/web.rb +2 -0
- data/lib/pg_eventstore.rb +23 -60
- data/pg_eventstore.gemspec +6 -4
- data/sig/interfaces/_raw_events_handler.rbs +3 -0
- data/sig/interfaces/subscription_handler.rbs +1 -1
- data/sig/pg/result.rbs +9 -0
- data/sig/pg_eventstore/async_runner.rbs +13 -0
- data/sig/pg_eventstore/basic_config.rbs +9 -0
- data/sig/pg_eventstore/chunks/chunk.rbs +13 -0
- data/sig/pg_eventstore/chunks/read_api_events_index_chunk.rbs +30 -0
- data/sig/pg_eventstore/chunks/replica_events_index_chunk.rbs +11 -0
- data/sig/pg_eventstore/chunks/repository.rbs +22 -0
- data/sig/pg_eventstore/chunks/subscription_checkpoint_chunk.rbs +18 -0
- data/sig/pg_eventstore/chunks/subscription_events_index_chunk.rbs +37 -0
- data/sig/pg_eventstore/client.rbs +40 -42
- data/sig/pg_eventstore/commands/append.rbs +11 -25
- data/sig/pg_eventstore/commands/event_modifiers/prepare_link_event.rbs +9 -12
- data/sig/pg_eventstore/commands/event_modifiers/prepare_regular_event.rbs +5 -4
- data/sig/pg_eventstore/commands/link_to.rbs +6 -11
- data/sig/pg_eventstore/commands/read.rbs +2 -5
- data/sig/pg_eventstore/commands/read_grouped.rbs +7 -0
- data/sig/pg_eventstore/commands/read_streams.rbs +7 -0
- data/sig/pg_eventstore/commands/read_streams_paginated.rbs +16 -0
- data/sig/pg_eventstore/commands/regular_stream_read_paginated.rbs +9 -17
- data/sig/pg_eventstore/commands/revision_check/current_revision.rbs +25 -0
- data/sig/pg_eventstore/commands/revision_check/event_type_revisions_comparison.rbs +14 -0
- data/sig/pg_eventstore/commands/revision_check/expected_revision.rbs +67 -0
- data/sig/pg_eventstore/commands/revision_check/stream_revision_check.rbs +20 -0
- data/sig/pg_eventstore/commands/revision_check/stream_revision_comparison.rbs +9 -0
- data/sig/pg_eventstore/commands/stream_revision.rbs +7 -0
- data/sig/pg_eventstore/commands/system_stream_read_paginated.rbs +9 -14
- data/sig/pg_eventstore/config.rbs +15 -6
- data/sig/pg_eventstore/connection.rbs +11 -18
- data/sig/pg_eventstore/errors.rbs +64 -34
- data/sig/pg_eventstore/event.rbs +16 -2
- data/sig/pg_eventstore/event_deserializer.rbs +5 -10
- data/sig/pg_eventstore/event_global_index.rbs +60 -0
- data/sig/pg_eventstore/event_marker.rbs +9 -0
- data/sig/pg_eventstore/event_marker_index.rbs +12 -0
- data/sig/pg_eventstore/extensions/acts_as_configurable.rbs +29 -0
- data/sig/pg_eventstore/extensions/options_defaults.rbs +7 -0
- data/sig/pg_eventstore/feature_marker.rbs +10 -0
- data/sig/pg_eventstore/maintenance.rbs +12 -8
- data/sig/pg_eventstore/middleware/event_trace.rbs +14 -0
- data/sig/pg_eventstore/partition.rbs +2 -4
- data/sig/pg_eventstore/pg_connection.rbs +8 -0
- data/sig/pg_eventstore/queries/event_marker_queries.rbs +21 -0
- data/sig/pg_eventstore/queries/event_queries.rbs +4 -36
- data/sig/pg_eventstore/queries/events_global_index_queries.rbs +37 -0
- data/sig/pg_eventstore/queries/index_filtering_queries.rbs +50 -0
- data/sig/pg_eventstore/queries/links_resolver.rbs +5 -9
- data/sig/pg_eventstore/queries/maintenance_queries.rbs +12 -9
- data/sig/pg_eventstore/queries/partition_queries.rbs +32 -67
- data/sig/pg_eventstore/queries/replica_queries.rbs +31 -0
- data/sig/pg_eventstore/queries/streams_global_index_queries.rbs +39 -0
- data/sig/pg_eventstore/queries/transaction_queries.rbs +2 -0
- data/sig/pg_eventstore/queries.rbs +11 -21
- data/sig/pg_eventstore/query_builders/basic_filtering.rbs +10 -3
- data/sig/pg_eventstore/query_builders/event_markers_filtering.rbs +17 -0
- data/sig/pg_eventstore/query_builders/event_markers_index_filtering.rbs +69 -0
- data/sig/pg_eventstore/query_builders/event_subscription_positions_filtering.rbs +15 -0
- data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +9 -51
- data/sig/pg_eventstore/query_builders/events_global_index_filtering.rbs +68 -0
- data/sig/pg_eventstore/query_builders/filters/collection.rbs +84 -0
- data/sig/pg_eventstore/query_builders/filters/event_type_filter.rbs +19 -0
- data/sig/pg_eventstore/query_builders/filters/filter_row.rbs +21 -0
- data/sig/pg_eventstore/query_builders/filters/marker_filter.rbs +13 -0
- data/sig/pg_eventstore/query_builders/filters/marker_filter_row.rbs +19 -0
- data/sig/pg_eventstore/query_builders/filters/stream_filter.rbs +24 -0
- data/sig/pg_eventstore/query_builders/index_based_events_filtering.rbs +60 -0
- data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +29 -10
- data/sig/pg_eventstore/query_builders/read_cursor/stream_cursor.rbs +47 -0
- data/sig/pg_eventstore/query_builders/streams_global_index_filtering.rbs +27 -0
- data/sig/pg_eventstore/query_builders/subscription_events_filtering.rbs +26 -0
- data/sig/pg_eventstore/query_strategy/async.rbs +17 -0
- data/sig/pg_eventstore/query_strategy/foreground.rbs +11 -0
- data/sig/pg_eventstore/query_strategy.rbs +7 -0
- data/sig/pg_eventstore/raw_event.rbs +20 -0
- data/sig/pg_eventstore/sql_builder.rbs +32 -10
- data/sig/pg_eventstore/stream.rbs +10 -18
- data/sig/pg_eventstore/stream_global_index.rbs +14 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +5 -4
- data/sig/pg_eventstore/subscriptions/callback_handlers/events_subscription_position_worker_handlers.rbs +9 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +17 -12
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +17 -6
- data/sig/pg_eventstore/subscriptions/event_subscription_position_queries.rbs +30 -0
- data/sig/pg_eventstore/subscriptions/events_processor.rbs +12 -5
- data/sig/pg_eventstore/subscriptions/events_processor_consumer/multiple.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/events_processor_consumer/replica.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/events_processor_consumer/single.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/events_processor_consumer.rbs +14 -0
- data/sig/pg_eventstore/subscriptions/events_subscription_position_worker.rbs +28 -0
- data/sig/pg_eventstore/subscriptions/queries/subscription_queries.rbs +7 -39
- data/sig/pg_eventstore/subscriptions/replica_subscription_handler.rbs +33 -0
- data/sig/pg_eventstore/subscriptions/replica_subscription_runner.rbs +4 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/collection.rbs +14 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/index_read_strategy.rbs +23 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy/replication_strategy.rbs +23 -0
- data/sig/pg_eventstore/subscriptions/subscription_feed_strategy.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +13 -10
- data/sig/pg_eventstore/subscriptions/subscription_handler_performance.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +13 -2
- data/sig/pg_eventstore/subscriptions/subscription_runners_feeder.rbs +1 -8
- data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +4 -6
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +29 -43
- data/sig/pg_eventstore/synchronized_array.rbs +4 -0
- data/sig/pg_eventstore/utils.rbs +10 -11
- data/sig/pg_eventstore/web/application.rbs +13 -3
- data/sig/pg_eventstore/web/paginator/base_collection.rbs +2 -0
- data/sig/pg_eventstore/web/paginator/event_types_collection.rbs +4 -0
- data/sig/pg_eventstore/web/paginator/events_collection.rbs +11 -11
- data/sig/pg_eventstore/web/paginator/helpers.rbs +6 -15
- data/sig/pg_eventstore/web/paginator/markers_collection.rbs +13 -0
- data/sig/pg_eventstore/web/paginator/stream_contexts_collection.rbs +4 -0
- data/sig/pg_eventstore/web/paginator/stream_names_collection.rbs +4 -0
- data/sig/pg_eventstore/web/paginator/streams_collection.rbs +30 -0
- data/sig/pg_eventstore.rbs +8 -26
- metadata +175 -20
- data/Dockerfile +0 -3
- data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +0 -69
- data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +0 -31
- data/lib/pg_eventstore/subscriptions/queries/subscription_service_queries.rb +0 -78
- data/lib/pg_eventstore/web/views/home/partials/system_stream_filter.erb +0 -15
- data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +0 -16
- data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +0 -8
- data/sig/pg_eventstore/subscriptions/queries/subscription_service_queries.rbs +0 -19
- data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +0 -4
- /data/sig/interfaces/{_raw_event_handler.rbs → raw_event_handler.rbs} +0 -0
|
@@ -32,7 +32,7 @@ module PgEventstore
|
|
|
32
32
|
# @return [Array<Hash>, nil]
|
|
33
33
|
# rubocop:disable Style/HashConversion
|
|
34
34
|
def streams_filter
|
|
35
|
-
streams =
|
|
35
|
+
streams = extract_streams_filter(params)
|
|
36
36
|
streams = streams.select { _1 in { context: String, stream_name: String, stream_id: String } }
|
|
37
37
|
streams = streams.map do |stream_attrs|
|
|
38
38
|
Hash[stream_attrs.reject { |_, value| value == '' }].transform_keys(&:to_sym)
|
|
@@ -41,17 +41,32 @@ module PgEventstore
|
|
|
41
41
|
end
|
|
42
42
|
# rubocop:enable Style/HashConversion
|
|
43
43
|
|
|
44
|
-
# @return [String,
|
|
45
|
-
def system_stream
|
|
46
|
-
params in { filter: { system_stream: String => system_stream } }
|
|
47
|
-
system_stream if Stream::KNOWN_SYSTEM_STREAMS.include?(system_stream)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# @return [Array<String>, nil]
|
|
44
|
+
# @return [Array<String, Hash>]
|
|
51
45
|
def events_filter
|
|
52
46
|
event_filters = { filter: { event_types: params.dig(:filter, :events) } }
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
event_types = extract_event_types_filter(event_filters)
|
|
48
|
+
event_types.map do |filter|
|
|
49
|
+
case filter
|
|
50
|
+
when String
|
|
51
|
+
next if filter == ''
|
|
52
|
+
when Hash
|
|
53
|
+
next if filter[:type] == ''
|
|
54
|
+
|
|
55
|
+
filter[:markers] = normalize_markers(filter)
|
|
56
|
+
else
|
|
57
|
+
next
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
filter
|
|
61
|
+
end.compact
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @return [Array<String>]
|
|
65
|
+
def markers_filter
|
|
66
|
+
params in { filter: Hash => filter }
|
|
67
|
+
return [] unless filter
|
|
68
|
+
|
|
69
|
+
normalize_markers(filter)
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
# @return [Symbol]
|
|
@@ -137,13 +152,55 @@ module PgEventstore
|
|
|
137
152
|
def unescape_empty_string(string)
|
|
138
153
|
string == EMPTY_STRING_SIGN ? '' : string
|
|
139
154
|
end
|
|
155
|
+
|
|
156
|
+
# @param options [Hash]
|
|
157
|
+
# @return [Array<String, Hash>]
|
|
158
|
+
def extract_event_types_filter(options)
|
|
159
|
+
options in { filter: { event_types: Array => event_types } }
|
|
160
|
+
event_types = event_types&.select do |event_type|
|
|
161
|
+
event_type.is_a?(String) || (event_type in { type: String })
|
|
162
|
+
end
|
|
163
|
+
event_types || []
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# @param options [Hash]
|
|
167
|
+
# @return [Array<Hash[Symbol, String]>]
|
|
168
|
+
def extract_streams_filter(options)
|
|
169
|
+
options in { filter: { streams: Array => streams } }
|
|
170
|
+
streams = streams&.map do |stream_attrs|
|
|
171
|
+
stream_attrs in { context: String | NilClass => context }
|
|
172
|
+
stream_attrs in { stream_name: String | NilClass => stream_name }
|
|
173
|
+
stream_attrs in { stream_id: String | NilClass => stream_id }
|
|
174
|
+
{ context:, stream_name:, stream_id: }
|
|
175
|
+
end
|
|
176
|
+
streams || []
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
# @param hash [Hash]
|
|
182
|
+
# @return [Array<String>]
|
|
183
|
+
def normalize_markers(hash)
|
|
184
|
+
hash in { markers: Array => markers }
|
|
185
|
+
markers ||= []
|
|
186
|
+
markers.grep(String).reject { _1 == '' }
|
|
187
|
+
end
|
|
140
188
|
end
|
|
141
189
|
|
|
142
190
|
get '/' do
|
|
143
191
|
streams_filter = self.streams_filter&.map do |attrs|
|
|
144
192
|
attrs.transform_values { unescape_empty_string(_1) }
|
|
145
193
|
end
|
|
146
|
-
events_filter = self.events_filter
|
|
194
|
+
events_filter = self.events_filter.map do |event_type|
|
|
195
|
+
next unescape_empty_string(event_type) if event_type.is_a?(String)
|
|
196
|
+
|
|
197
|
+
event_type[:type] = unescape_empty_string(event_type[:type])
|
|
198
|
+
event_type[:markers] = event_type[:markers]&.grep(String) || []
|
|
199
|
+
event_type
|
|
200
|
+
end
|
|
201
|
+
markers_filter = self.markers_filter.map(&method(:unescape_empty_string))
|
|
202
|
+
events_filter.push({ markers: markers_filter }) if markers_filter.any?
|
|
203
|
+
|
|
147
204
|
@collection = Paginator::EventsCollection.new(
|
|
148
205
|
current_config,
|
|
149
206
|
starting_id: params[:starting_id]&.to_i,
|
|
@@ -152,14 +209,20 @@ module PgEventstore
|
|
|
152
209
|
options: {
|
|
153
210
|
filter: { event_types: events_filter, streams: streams_filter },
|
|
154
211
|
resolve_link_tos: resolve_link_tos?,
|
|
155
|
-
}
|
|
156
|
-
system_stream:
|
|
212
|
+
}
|
|
157
213
|
)
|
|
214
|
+
@gp_to_sp_map = EventSubscriptionPositionQueries.new(
|
|
215
|
+
PgEventstore.connection(current_config)
|
|
216
|
+
).subscription_positions_from_db(@collection.collection)
|
|
158
217
|
|
|
159
218
|
if request.xhr?
|
|
160
219
|
content_type 'application/json'
|
|
161
220
|
halt 200, {
|
|
162
|
-
events: erb(
|
|
221
|
+
events: erb(
|
|
222
|
+
:'home/partials/events',
|
|
223
|
+
{ layout: false },
|
|
224
|
+
{ events: @collection.collection, gp_to_sp_map: @gp_to_sp_map }
|
|
225
|
+
),
|
|
163
226
|
total_count: total_count(@collection.total_count),
|
|
164
227
|
pagination: erb(:'home/partials/pagination_links', { layout: false }, { collection: @collection }),
|
|
165
228
|
}.to_json
|
|
@@ -168,6 +231,16 @@ module PgEventstore
|
|
|
168
231
|
end
|
|
169
232
|
end
|
|
170
233
|
|
|
234
|
+
get '/streams' do
|
|
235
|
+
@collection = Paginator::StreamsCollection.new(
|
|
236
|
+
current_config,
|
|
237
|
+
starting_id: params[:starting_id]&.to_i,
|
|
238
|
+
per_page: Paginator::StreamsCollection::PER_PAGE[params[:per_page]],
|
|
239
|
+
order: Paginator::StreamsCollection::SQL_DIRECTIONS[params[:order]]
|
|
240
|
+
)
|
|
241
|
+
erb :'streams/index'
|
|
242
|
+
end
|
|
243
|
+
|
|
171
244
|
get '/subscriptions' do
|
|
172
245
|
@set_collection = Subscriptions::SetCollection.new(connection)
|
|
173
246
|
@current_set = params[:set_name] || @set_collection.names.first
|
|
@@ -247,6 +320,17 @@ module PgEventstore
|
|
|
247
320
|
paginated_json_response(collection)
|
|
248
321
|
end
|
|
249
322
|
|
|
323
|
+
get '/markers_filtering', provides: :json do
|
|
324
|
+
collection = Paginator::MarkersCollection.new(
|
|
325
|
+
current_config,
|
|
326
|
+
starting_id: unescape_empty_string(params[:starting_id]),
|
|
327
|
+
per_page: Paginator::MarkersCollection::PER_PAGE,
|
|
328
|
+
order: :asc,
|
|
329
|
+
options: { query: params[:term] }
|
|
330
|
+
)
|
|
331
|
+
paginated_json_response(collection)
|
|
332
|
+
end
|
|
333
|
+
|
|
250
334
|
post '/subscription_cmd/:set_id/:id/:cmd' do
|
|
251
335
|
validate_subscription_cmd(params[:cmd])
|
|
252
336
|
cmd_class = SubscriptionRunnerCommands.command_class(params[:cmd])
|
|
@@ -279,14 +363,14 @@ module PgEventstore
|
|
|
279
363
|
end
|
|
280
364
|
|
|
281
365
|
post '/delete_subscription/:id' do
|
|
282
|
-
SubscriptionQueries.new(connection).delete(Integer(params[:id]))
|
|
366
|
+
SubscriptionQueries.new(connection, QueryStrategy::Foreground.new(connection)).delete(Integer(params[:id]))
|
|
283
367
|
|
|
284
368
|
redirect redirect_back_url(fallback_url: url('/subscriptions'))
|
|
285
369
|
end
|
|
286
370
|
|
|
287
371
|
post '/delete_all_subscriptions' do
|
|
288
372
|
params[:ids].each do |id|
|
|
289
|
-
SubscriptionQueries.new(connection).delete(Integer(id))
|
|
373
|
+
SubscriptionQueries.new(connection, QueryStrategy::Foreground.new(connection)).delete(Integer(id))
|
|
290
374
|
end
|
|
291
375
|
|
|
292
376
|
redirect redirect_back_url(fallback_url: url('/subscriptions'))
|
|
@@ -4,6 +4,9 @@ module PgEventstore
|
|
|
4
4
|
module Web
|
|
5
5
|
module Paginator
|
|
6
6
|
class BaseCollection
|
|
7
|
+
# This is the minimum amount of symbols requeried to perform search using trigrams gin index
|
|
8
|
+
MIN_QUERY_SIZE_FOR_ADVANCE_SEARCH = 3
|
|
9
|
+
|
|
7
10
|
# @!attribute config_name
|
|
8
11
|
# @return [Symbol]
|
|
9
12
|
attr_reader :config_name
|
|
@@ -30,7 +33,8 @@ module PgEventstore
|
|
|
30
33
|
@starting_id = starting_id
|
|
31
34
|
@per_page = per_page
|
|
32
35
|
@order = order
|
|
33
|
-
@options =
|
|
36
|
+
@options =
|
|
37
|
+
options.is_a?(Sinatra::IndifferentHash) ? Utils.deep_transform_keys(Hash[options], &:to_sym) : options
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
# @return [Array]
|
|
@@ -12,9 +12,9 @@ module PgEventstore
|
|
|
12
12
|
@collection ||=
|
|
13
13
|
begin
|
|
14
14
|
sql_builder = SQLBuilder.new.select('event_type').from('partitions')
|
|
15
|
-
sql_builder.where('
|
|
15
|
+
sql_builder.where('event_type is not null')
|
|
16
16
|
sql_builder.where("event_type #{direction_operator} ?", starting_id) if starting_id
|
|
17
|
-
sql_builder
|
|
17
|
+
compute_event_type_filter(sql_builder)
|
|
18
18
|
sql_builder.group('event_type').order("event_type #{order}").limit(per_page)
|
|
19
19
|
connection.with do |conn|
|
|
20
20
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
@@ -28,9 +28,8 @@ module PgEventstore
|
|
|
28
28
|
|
|
29
29
|
starting_id = collection.first['event_type']
|
|
30
30
|
sql_builder = SQLBuilder.new.select('event_type').from('partitions')
|
|
31
|
-
sql_builder.where('context is not null and stream_name is not null')
|
|
32
31
|
sql_builder.where("event_type #{direction_operator} ?", starting_id)
|
|
33
|
-
sql_builder
|
|
32
|
+
compute_event_type_filter(sql_builder)
|
|
34
33
|
sql_builder.group('event_type').order("event_type #{order}").limit(1).offset(per_page)
|
|
35
34
|
|
|
36
35
|
connection.with do |conn|
|
|
@@ -44,6 +43,17 @@ module PgEventstore
|
|
|
44
43
|
def direction_operator
|
|
45
44
|
order == :asc ? '>=' : '<='
|
|
46
45
|
end
|
|
46
|
+
|
|
47
|
+
# @param builder [PgEventstore::SQLBuilder]
|
|
48
|
+
# @return [void]
|
|
49
|
+
def compute_event_type_filter(builder)
|
|
50
|
+
query = options[:query].to_s
|
|
51
|
+
if query.size < MIN_QUERY_SIZE_FOR_ADVANCE_SEARCH
|
|
52
|
+
builder.where('event_type like ?', "#{query}%")
|
|
53
|
+
else
|
|
54
|
+
builder.where('event_type ilike ?', "%#{query}%")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
47
57
|
end
|
|
48
58
|
end
|
|
49
59
|
end
|
|
@@ -19,57 +19,46 @@ module PgEventstore
|
|
|
19
19
|
# count instead because of the potential performance degradation.
|
|
20
20
|
MAX_NUMBER_TO_COUNT = 10_000
|
|
21
21
|
|
|
22
|
-
# @param config_name [Symbol]
|
|
23
|
-
# @param starting_id [String, Integer, nil]
|
|
24
|
-
# @param per_page [Integer]
|
|
25
|
-
# @param order [Symbol] :asc or :desc
|
|
26
|
-
# @param options [Hash] additional options to filter the collection
|
|
27
|
-
# @param system_stream [String, nil] a name of system stream
|
|
28
|
-
def initialize(config_name, starting_id:, per_page:, order:, options: {}, system_stream: nil)
|
|
29
|
-
super(config_name, starting_id:, per_page:, order:, options:)
|
|
30
|
-
@stream = system_stream ? PgEventstore::Stream.system_stream(system_stream) : PgEventstore::Stream.all_stream
|
|
31
|
-
end
|
|
32
|
-
|
|
33
22
|
# @return [Array<PgEventstore::Event>]
|
|
34
23
|
def collection
|
|
35
|
-
|
|
36
|
-
@stream,
|
|
37
|
-
options: options.merge(from_position: starting_id, max_count: per_page, direction: order)
|
|
38
|
-
)
|
|
24
|
+
_collection[0...per_page]
|
|
39
25
|
end
|
|
40
26
|
|
|
41
27
|
# @return [Integer, nil]
|
|
42
28
|
def next_page_starting_id
|
|
43
29
|
return unless collection.size == per_page
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
sql_builder = QueryBuilders::EventsFiltering.events_filtering(
|
|
47
|
-
@stream,
|
|
48
|
-
options.merge(from_position:, max_count: 1, direction: order)
|
|
49
|
-
).to_sql_builder.unselect.select('global_position').offset(per_page)
|
|
50
|
-
global_position(sql_builder)
|
|
31
|
+
event_global_position(_collection[per_page])
|
|
51
32
|
end
|
|
52
33
|
|
|
53
34
|
# @return [Integer, nil]
|
|
54
35
|
def prev_page_starting_id
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
36
|
+
starting_position = event_global_position(_collection.first) || starting_id
|
|
37
|
+
return unless starting_position
|
|
38
|
+
|
|
39
|
+
order = self.order == :asc ? :desc : :asc
|
|
40
|
+
starting_position = self.order == :asc ? starting_position - 1 : starting_position + 1
|
|
41
|
+
|
|
42
|
+
# Not ideal from the perspective of the amount of records we load here, but this is the only way to read from
|
|
43
|
+
# the db in the correct way. Direct usage of query builders may result in heavy, unoptimized queries.
|
|
44
|
+
options = self.options.merge(
|
|
45
|
+
from_position: starting_position, max_count: per_page, direction: order, resolve_link_tos: false
|
|
46
|
+
)
|
|
47
|
+
PgEventstore.client(config_name).read(
|
|
48
|
+
PgEventstore::Stream.all_stream,
|
|
49
|
+
options:,
|
|
50
|
+
middlewares: []
|
|
51
|
+
).last&.global_position
|
|
65
52
|
end
|
|
66
53
|
|
|
67
54
|
# @return [Integer]
|
|
68
55
|
def total_count
|
|
69
56
|
@total_count ||=
|
|
70
57
|
begin
|
|
71
|
-
|
|
72
|
-
sql_builder.
|
|
58
|
+
filters_collection = QueryBuilders::Filters::Collection.from_options(options)
|
|
59
|
+
sql_builder = QueryBuilders::IndexBasedEventsFiltering.sql_builder_for_estimate_count(
|
|
60
|
+
filters_collection.collection
|
|
61
|
+
)
|
|
73
62
|
count = estimate_count(sql_builder)
|
|
74
63
|
return count if count > MAX_NUMBER_TO_COUNT
|
|
75
64
|
|
|
@@ -79,6 +68,13 @@ module PgEventstore
|
|
|
79
68
|
|
|
80
69
|
private
|
|
81
70
|
|
|
71
|
+
def _collection
|
|
72
|
+
@_collection ||= PgEventstore.client(config_name).read(
|
|
73
|
+
PgEventstore::Stream.all_stream,
|
|
74
|
+
options: options.merge(from_position: starting_id, max_count: per_page + 1, direction: order)
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
82
78
|
# @param event [PgEventstore::Event, nil]
|
|
83
79
|
# @return [Integer, nil]
|
|
84
80
|
def event_global_position(event)
|
|
@@ -80,24 +80,31 @@ module PgEventstore
|
|
|
80
80
|
parts.join(delimiter)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
# @param
|
|
83
|
+
# @param stream [PgEventstore::Stream]
|
|
84
84
|
# @return [String]
|
|
85
|
-
def stream_path(
|
|
85
|
+
def stream_path(stream)
|
|
86
86
|
build_path(
|
|
87
87
|
{
|
|
88
88
|
filter: {
|
|
89
89
|
streams: [
|
|
90
90
|
{
|
|
91
|
-
context: escape_empty_string(
|
|
92
|
-
stream_name: escape_empty_string(
|
|
93
|
-
stream_id: escape_empty_string(
|
|
91
|
+
context: escape_empty_string(stream.context),
|
|
92
|
+
stream_name: escape_empty_string(stream.stream_name),
|
|
93
|
+
stream_id: escape_empty_string(stream.stream_id),
|
|
94
94
|
},
|
|
95
95
|
],
|
|
96
96
|
},
|
|
97
|
-
}
|
|
97
|
+
},
|
|
98
|
+
base_url: '/'
|
|
98
99
|
)
|
|
99
100
|
end
|
|
100
101
|
|
|
102
|
+
# @param marker [String]
|
|
103
|
+
# @return [String]
|
|
104
|
+
def event_marker_path(marker)
|
|
105
|
+
build_path({ filter: { markers: [escape_empty_string(marker)] } }, base_url: '/')
|
|
106
|
+
end
|
|
107
|
+
|
|
101
108
|
# @param str [String]
|
|
102
109
|
# @return [String]
|
|
103
110
|
def empty_characters_fallback(str)
|
|
@@ -118,11 +125,11 @@ module PgEventstore
|
|
|
118
125
|
|
|
119
126
|
# @param params [Hash, Array]
|
|
120
127
|
# @return [String]
|
|
121
|
-
def build_path(params)
|
|
128
|
+
def build_path(params, base_url: request.path)
|
|
122
129
|
encoded_params = Rack::Utils.build_nested_query(params)
|
|
123
|
-
return
|
|
130
|
+
return base_url if encoded_params.empty?
|
|
124
131
|
|
|
125
|
-
"#{
|
|
132
|
+
"#{base_url}?#{encoded_params}"
|
|
126
133
|
end
|
|
127
134
|
end
|
|
128
135
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgEventstore
|
|
4
|
+
module Web
|
|
5
|
+
module Paginator
|
|
6
|
+
class MarkersCollection < BaseCollection
|
|
7
|
+
# @return [Integer]
|
|
8
|
+
PER_PAGE = 10
|
|
9
|
+
|
|
10
|
+
# @return [Array<Hash<String => String>>]
|
|
11
|
+
def collection
|
|
12
|
+
@collection ||=
|
|
13
|
+
begin
|
|
14
|
+
sql_builder = SQLBuilder.new.select('name').from('event_markers')
|
|
15
|
+
sql_builder.where("name #{direction_operator} ?", starting_id) if starting_id
|
|
16
|
+
sql_builder.where('name like ?', "#{options[:query]}%")
|
|
17
|
+
sql_builder.order("name #{order}").limit(per_page)
|
|
18
|
+
connection.with do |conn|
|
|
19
|
+
conn.exec_params(*sql_builder.to_exec_params)
|
|
20
|
+
end.to_a
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [String, nil]
|
|
25
|
+
def next_page_starting_id
|
|
26
|
+
return unless collection.size == per_page
|
|
27
|
+
|
|
28
|
+
starting_id = collection.first['name']
|
|
29
|
+
sql_builder = SQLBuilder.new.select('name').from('event_markers')
|
|
30
|
+
sql_builder.where("name #{direction_operator} ?", starting_id)
|
|
31
|
+
sql_builder.where('name like ?', "#{options[:query]}%")
|
|
32
|
+
sql_builder.order("name #{order}").limit(1).offset(per_page)
|
|
33
|
+
|
|
34
|
+
connection.with do |conn|
|
|
35
|
+
conn.exec_params(*sql_builder.to_exec_params)
|
|
36
|
+
end.to_a.dig(0, 'name')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# @return [String]
|
|
42
|
+
def direction_operator
|
|
43
|
+
order == :asc ? '>=' : '<='
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -14,7 +14,7 @@ module PgEventstore
|
|
|
14
14
|
sql_builder = SQLBuilder.new.select('context').from('partitions')
|
|
15
15
|
sql_builder.where('stream_name is null and event_type is null')
|
|
16
16
|
sql_builder.where("context #{direction_operator} ?", starting_id) if starting_id
|
|
17
|
-
sql_builder
|
|
17
|
+
compute_context_filter(sql_builder)
|
|
18
18
|
sql_builder.limit(per_page).order("context #{order}")
|
|
19
19
|
connection.with do |conn|
|
|
20
20
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
@@ -30,7 +30,7 @@ module PgEventstore
|
|
|
30
30
|
sql_builder = SQLBuilder.new.select('context').from('partitions')
|
|
31
31
|
sql_builder.where('stream_name is null and event_type is null')
|
|
32
32
|
sql_builder.where("context #{direction_operator} ?", starting_id)
|
|
33
|
-
sql_builder
|
|
33
|
+
compute_context_filter(sql_builder)
|
|
34
34
|
sql_builder.limit(1).offset(per_page).order("context #{order}")
|
|
35
35
|
|
|
36
36
|
connection.with do |conn|
|
|
@@ -44,6 +44,17 @@ module PgEventstore
|
|
|
44
44
|
def direction_operator
|
|
45
45
|
order == :asc ? '>=' : '<='
|
|
46
46
|
end
|
|
47
|
+
|
|
48
|
+
# @param builder [PgEventstore::SQLBuilder]
|
|
49
|
+
# @return [void]
|
|
50
|
+
def compute_context_filter(builder)
|
|
51
|
+
query = options[:query].to_s
|
|
52
|
+
if query.size < MIN_QUERY_SIZE_FOR_ADVANCE_SEARCH
|
|
53
|
+
builder.where('context like ?', "#{query}%")
|
|
54
|
+
else
|
|
55
|
+
builder.where('context ilike ?', "%#{query}%")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
47
58
|
end
|
|
48
59
|
end
|
|
49
60
|
end
|
|
@@ -11,11 +11,9 @@ module PgEventstore
|
|
|
11
11
|
def collection
|
|
12
12
|
@collection ||=
|
|
13
13
|
begin
|
|
14
|
-
sql_builder =
|
|
15
|
-
sql_builder.
|
|
16
|
-
|
|
17
|
-
sql_builder.where("stream_id #{direction_operator} ?", starting_id) if starting_id
|
|
18
|
-
sql_builder.group('stream_id').limit(per_page).order("stream_id #{order}")
|
|
14
|
+
sql_builder = streams_index_sql_builder(from_id: starting_id)
|
|
15
|
+
sql_builder.limit(per_page)
|
|
16
|
+
|
|
19
17
|
connection.with do |conn|
|
|
20
18
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
21
19
|
end.to_a
|
|
@@ -26,12 +24,8 @@ module PgEventstore
|
|
|
26
24
|
def next_page_starting_id
|
|
27
25
|
return unless collection.size == per_page
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
sql_builder
|
|
31
|
-
sql_builder.where("stream_id #{direction_operator} ?", starting_id)
|
|
32
|
-
sql_builder.where('stream_id like ?', "#{options[:query]}%")
|
|
33
|
-
sql_builder.where('context = ? and stream_name = ?', options[:context], options[:stream_name])
|
|
34
|
-
sql_builder.group('stream_id').limit(1).offset(per_page).order("stream_id #{order}")
|
|
27
|
+
sql_builder = streams_index_sql_builder(from_id: collection.first['stream_id'])
|
|
28
|
+
sql_builder.limit(1).offset(per_page)
|
|
35
29
|
|
|
36
30
|
connection.with do |conn|
|
|
37
31
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
@@ -40,6 +34,22 @@ module PgEventstore
|
|
|
40
34
|
|
|
41
35
|
private
|
|
42
36
|
|
|
37
|
+
def partitions_sql_builder
|
|
38
|
+
filters = QueryBuilders::Filters::Collection.from_options(
|
|
39
|
+
{ filter: { streams: [{ context: options[:context], stream_name: options[:stream_name] }] } }
|
|
40
|
+
)
|
|
41
|
+
QueryBuilders::PartitionsFiltering.assemble_sql_builder(filters, scope: :stream_name).unselect.select('id')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def streams_index_sql_builder(from_id: nil)
|
|
45
|
+
sql_builder = SQLBuilder.new.select('stream_id')
|
|
46
|
+
sql_builder.from(QueryBuilders::StreamsGlobalIndexFiltering::PRIMARY_TABLE_NAME)
|
|
47
|
+
sql_builder.where('partition_id = ?', partitions_sql_builder)
|
|
48
|
+
sql_builder.where('stream_id like ?', "#{options[:query]}%")
|
|
49
|
+
sql_builder.where("stream_id #{direction_operator} ?", from_id) if from_id
|
|
50
|
+
sql_builder.order("stream_id #{order}")
|
|
51
|
+
end
|
|
52
|
+
|
|
43
53
|
# @return [String]
|
|
44
54
|
def direction_operator
|
|
45
55
|
order == :asc ? '>=' : '<='
|
|
@@ -12,9 +12,9 @@ module PgEventstore
|
|
|
12
12
|
@collection ||=
|
|
13
13
|
begin
|
|
14
14
|
sql_builder = SQLBuilder.new.select('stream_name').from('partitions')
|
|
15
|
-
sql_builder.where('event_type is null and context = ?', options[:context])
|
|
16
|
-
sql_builder.where('stream_name ilike ?', "%#{options[:query]}%")
|
|
15
|
+
sql_builder.where('event_type is null and stream_name is not null and context = ?', options[:context])
|
|
17
16
|
sql_builder.where("stream_name #{direction_operator} ?", starting_id) if starting_id
|
|
17
|
+
compute_stream_name_filter(sql_builder)
|
|
18
18
|
sql_builder.limit(per_page).order("stream_name #{order}")
|
|
19
19
|
connection.with do |conn|
|
|
20
20
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
@@ -29,10 +29,9 @@ module PgEventstore
|
|
|
29
29
|
starting_id = collection.first['stream_name']
|
|
30
30
|
sql_builder = SQLBuilder.new.select('stream_name').from('partitions')
|
|
31
31
|
sql_builder.where("stream_name #{direction_operator} ?", starting_id)
|
|
32
|
-
sql_builder
|
|
32
|
+
compute_stream_name_filter(sql_builder)
|
|
33
33
|
sql_builder.where('event_type is null and context = ?', options[:context])
|
|
34
34
|
sql_builder.limit(1).offset(per_page).order("stream_name #{order}")
|
|
35
|
-
|
|
36
35
|
connection.with do |conn|
|
|
37
36
|
conn.exec_params(*sql_builder.to_exec_params)
|
|
38
37
|
end.to_a.dig(0, 'stream_name')
|
|
@@ -44,6 +43,21 @@ module PgEventstore
|
|
|
44
43
|
def direction_operator
|
|
45
44
|
order == :asc ? '>=' : '<='
|
|
46
45
|
end
|
|
46
|
+
|
|
47
|
+
# @param builder [PgEventstore::SQLBuilder]
|
|
48
|
+
# @return [void]
|
|
49
|
+
def compute_stream_name_filter(builder)
|
|
50
|
+
query = options[:query].to_s
|
|
51
|
+
if query.size < MIN_QUERY_SIZE_FOR_ADVANCE_SEARCH
|
|
52
|
+
builder.where('stream_name like ?', "#{query}%")
|
|
53
|
+
else
|
|
54
|
+
builder.where(
|
|
55
|
+
'context || ? || stream_name ilike ?',
|
|
56
|
+
PgEventstore::Event::SYSTEM_SYMBOL,
|
|
57
|
+
"#{options[:context]}#{PgEventstore::Event::SYSTEM_SYMBOL}%#{query}%"
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
47
61
|
end
|
|
48
62
|
end
|
|
49
63
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgEventstore
|
|
4
|
+
module Web
|
|
5
|
+
module Paginator
|
|
6
|
+
class StreamsCollection < BaseCollection
|
|
7
|
+
# @return [Hash<String => Symbol>] SQL directions, string-to-symbol mapping
|
|
8
|
+
SQL_DIRECTIONS = {
|
|
9
|
+
'asc' => :asc,
|
|
10
|
+
'desc' => :desc,
|
|
11
|
+
}.tap do |directions|
|
|
12
|
+
directions.default = :desc
|
|
13
|
+
end.freeze
|
|
14
|
+
# @return [Hash<String => Integer>] per page limits, string to Integer mapping
|
|
15
|
+
PER_PAGE = %w[10 20 50 100 1000].to_h { [_1, _1.to_i] }.tap do |per_page|
|
|
16
|
+
per_page.default = 10
|
|
17
|
+
end.freeze
|
|
18
|
+
# @return [Integer] max number of events after which we don't perform the exact count and keep the estimate
|
|
19
|
+
# count instead because of the potential performance degradation.
|
|
20
|
+
MAX_NUMBER_TO_COUNT = 10_000
|
|
21
|
+
|
|
22
|
+
# @return [Array<PgEventstore::Stream>]
|
|
23
|
+
def collection
|
|
24
|
+
@collection ||= PgEventstore.client(config_name).read_streams(
|
|
25
|
+
options: { from_position: starting_id, max_count: per_page, direction: order }
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Integer, nil]
|
|
30
|
+
def next_page_starting_id
|
|
31
|
+
return unless collection.size == per_page
|
|
32
|
+
|
|
33
|
+
from_position = collection.first&.starting_position
|
|
34
|
+
sql_builder = QueryBuilders::StreamsGlobalIndexFiltering.sql_builder_for_basic_pagination(
|
|
35
|
+
from_position:, max_count: 1, direction: order
|
|
36
|
+
).unselect.select('starting_position').offset(per_page)
|
|
37
|
+
starting_position(sql_builder)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Integer, nil]
|
|
41
|
+
def prev_page_starting_id
|
|
42
|
+
from_position = collection.first&.starting_position || starting_id
|
|
43
|
+
sql_builder = QueryBuilders::StreamsGlobalIndexFiltering.sql_builder_for_basic_pagination(
|
|
44
|
+
from_position:, max_count: per_page, direction: order == :asc ? :desc : :asc
|
|
45
|
+
).unselect.select('starting_position').offset(1)
|
|
46
|
+
sql_builder = SQLBuilder.new.select('starting_position').from(sql_builder)
|
|
47
|
+
sql_builder.order("starting_position #{order}").limit(1)
|
|
48
|
+
starting_position(sql_builder)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Integer]
|
|
52
|
+
def total_count
|
|
53
|
+
@total_count ||=
|
|
54
|
+
begin
|
|
55
|
+
sql_builder = QueryBuilders::StreamsGlobalIndexFiltering.sql_builder_for_basic_pagination({})
|
|
56
|
+
sql_builder.remove_limit.remove_group.remove_order
|
|
57
|
+
count = estimate_count(sql_builder)
|
|
58
|
+
return count if count > MAX_NUMBER_TO_COUNT
|
|
59
|
+
|
|
60
|
+
regular_count(sql_builder)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# @param sql_builder [PgEventstore::SQLBuilder]
|
|
67
|
+
# @return [Integer]
|
|
68
|
+
def estimate_count(sql_builder)
|
|
69
|
+
sql, params = sql_builder.to_exec_params
|
|
70
|
+
connection.with do |conn|
|
|
71
|
+
conn.exec_params("EXPLAIN #{sql}", params)
|
|
72
|
+
end.to_a.first['QUERY PLAN'].match(/rows=(\d+)/)[1].to_i
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @param sql_builder [PgEventstore::SQLBuilder]
|
|
76
|
+
# @return [Integer]
|
|
77
|
+
def regular_count(sql_builder)
|
|
78
|
+
sql_builder.unselect.select('count(*) as count_all')
|
|
79
|
+
|
|
80
|
+
connection.with do |conn|
|
|
81
|
+
conn.exec_params(*sql_builder.to_exec_params)
|
|
82
|
+
end.to_a.first['count_all']
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param sql_builder [PgEventstore::SQLBuilder]
|
|
86
|
+
# @return [Integer, nil]
|
|
87
|
+
def starting_position(sql_builder)
|
|
88
|
+
connection.with do |conn|
|
|
89
|
+
conn.exec_params(*sql_builder.to_exec_params)
|
|
90
|
+
end.to_a.dig(0, 'starting_position')
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|