Skip to content

Commit 035d186

Browse files
Search enhacements:
- `SearchIndexerConfig` is respected both by natural and full-text search engine - Ability to disable incremental full-text search indexing via config option
1 parent 6f81d6c commit 035d186

18 files changed

Lines changed: 190 additions & 74 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ Recommendation: for ease of reading, use the following order:
1111
- Fixed
1212
-->
1313

14+
## [Unreleased]
15+
### Added
16+
- `SearchIndexerConfig` is respected both by natural and full-text search engine
17+
- Ability to disable incremental full-text search indexing via config option
18+
1419
## [0.256.0] - 2026-01-08
1520
### Added
1621
- Prototyped full-text search capabilities via `Elasticsearch`:

resources/di.puml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,6 @@ class "ModifyAccountPasswordUseCaseImpl"
245245
class "MqttSourceConfig" <<singleton>>
246246
class "NaturalLanguageSearchConfig" <<singleton>>
247247
class "NaturalLanguageSearchImplLazyInit" <<singleton>>
248-
class "NaturalLanguageSearchIndexerConfig" <<singleton>>
249248
class "NotebookServerFactory"
250249
class "OAuthDeviceCodeGeneratorDefault"
251250
class "OAuthDeviceCodeServiceImpl"
@@ -294,6 +293,7 @@ class "ResetPlannerImpl"
294293
class "ResourceLoaderImpl"
295294
class "RunInfoDir" <<singleton>>
296295
class "SchemaServiceImpl"
296+
class "SearchIndexerConfig" <<singleton>>
297297
class "SearchServiceRemoteImpl"
298298
class "SessionAuthAnonymous"
299299
class "SessionAuthConfig" <<singleton>>
@@ -652,13 +652,15 @@ class "WsSmartTransferProtocolClient"
652652
"DidGenerator" <|-- "DidGeneratorDefault"
653653
"SearchEntitySchemaProvider" <|-- "DatasetSearchSchemaProvider"
654654
"DatasetSearchSchemaProvider" --> "Catalog"
655+
"DatasetSearchSchemaProvider" --> "SearchIndexerConfig"
655656
"MessageConsumer" <|-- "DatasetSearchUpdater"
656657
"MessageConsumerT" <|-- "DatasetSearchUpdater"
657658
"MessageConsumerT" <|-- "DatasetSearchUpdater"
658659
"MessageConsumerT" <|-- "DatasetSearchUpdater"
659660
"DatasetSearchUpdater" --> "DatasetEntryService"
660661
"DatasetSearchUpdater" --> "DatasetRegistry"
661662
"DatasetSearchUpdater" --> "SearchService"
663+
"DatasetSearchUpdater" --> "SearchIndexerConfig"
662664
"InitOnStartup" <|-- "DatasetBlockIndexer"
663665
"DatasetBlockIndexer" --> "Catalog"
664666
"MessageConsumer" <|-- "DatasetBlockUpdateHandler"

src/app/cli/src/app.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use tracing::{Instrument, warn};
3939

4040
use crate::accounts::AccountService;
4141
use crate::cli::Command;
42+
use crate::config::CLIConfig;
4243
use crate::error::*;
4344
use crate::output::*;
4445
use crate::{
@@ -129,15 +130,15 @@ pub async fn run(workspace_layout: WorkspaceLayout, args: cli::Cli) -> Result<()
129130
(_, true) => WorkspaceStatus::Created(tenancy_config),
130131
};
131132

132-
let config = load_config(&workspace_layout);
133+
let cli_config = load_config(&workspace_layout);
133134
let current_account =
134135
AccountService::current_account_indication(args.account.clone(), tenancy_config)
135136
.map_err(CLIError::usage_error_from)?;
136137

137138
prepare_run_dir(&workspace_layout.run_info_dir);
138139

139140
let app_database_config =
140-
get_app_database_config(&workspace_layout, &config, workspace_status);
141+
get_app_database_config(&workspace_layout, &cli_config, workspace_status);
141142
let (database_config, maybe_temp_database_path) = app_database_config.into_inner();
142143
let maybe_db_connection_settings =
143144
database_config.as_ref().map(build_db_connection_settings);
@@ -149,6 +150,7 @@ pub async fn run(workspace_layout: WorkspaceLayout, args: cli::Cli) -> Result<()
149150
let mut base_catalog_builder = configure_base_catalog(
150151
&workspace_layout,
151152
workspace_status,
153+
&cli_config,
152154
tenancy_config,
153155
args.system_time.map(Into::into),
154156
is_e2e_testing,
@@ -171,7 +173,7 @@ pub async fn run(workspace_layout: WorkspaceLayout, args: cli::Cli) -> Result<()
171173
base_catalog_builder.add_value(Interact::new(args.yes, output_config.is_tty));
172174

173175
register_config_in_catalog(
174-
&config,
176+
&cli_config,
175177
&mut base_catalog_builder,
176178
workspace_status,
177179
args.password_hashing_mode,
@@ -417,6 +419,7 @@ where
417419
pub fn configure_base_catalog(
418420
workspace_layout: &WorkspaceLayout,
419421
workspace_status: WorkspaceStatus,
422+
cli_config: &CLIConfig,
420423
tenancy_config: TenancyConfig,
421424
system_time: Option<DateTime<Utc>>,
422425
is_e2e_testing: bool,
@@ -545,16 +548,36 @@ pub fn configure_base_catalog(
545548
kamu_adapter_oauth::register_dependencies(&mut b, !is_e2e_testing);
546549
}
547550

551+
let incremental_search_indexing = cli_config
552+
.search
553+
.as_ref()
554+
.unwrap()
555+
.indexer
556+
.as_ref()
557+
.unwrap()
558+
.incremental_indexing;
559+
548560
kamu_accounts_services::register_dependencies(
549561
&mut b,
550-
workspace_status.is_indexing_needed(),
551-
!is_e2e_testing,
562+
kamu_accounts_services::AccountDomainDependenciesOptions {
563+
needs_indexing: workspace_status.is_indexing_needed(),
564+
production: !is_e2e_testing,
565+
incremental_search_indexing,
566+
},
552567
);
553568

554569
odf_server::register_dependencies(&mut b, is_e2e_testing);
555570

556571
kamu_adapter_auth_oso_rebac::register_dependencies(&mut b);
557-
kamu_datasets_services::register_dependencies(&mut b, workspace_status.is_indexing_needed());
572+
573+
kamu_datasets_services::register_dependencies(
574+
&mut b,
575+
kamu_datasets_services::DatasetDomainDependenciesOptions {
576+
needs_indexing: workspace_status.is_indexing_needed(),
577+
incremental_search_indexing,
578+
},
579+
);
580+
558581
kamu_auth_rebac_services::register_dependencies(&mut b, workspace_status.is_indexing_needed());
559582

560583
b.add::<DatabaseTransactionRunner>();
@@ -1093,7 +1116,7 @@ pub fn register_config_in_catalog(
10931116
});
10941117

10951118
let indexer = indexer.unwrap_or_default();
1096-
catalog_builder.add_value(kamu_search_services::NaturalLanguageSearchIndexerConfig {
1119+
catalog_builder.add_value(kamu_search::SearchIndexerConfig {
10971120
clear_on_start: indexer.clear_on_start,
10981121
skip_datasets_with_no_description: indexer.skip_datasets_with_no_description,
10991122
skip_datasets_with_no_data: indexer.skip_datasets_with_no_data,

src/app/cli/src/services/config/models.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,10 +1140,13 @@ impl Default for SearchConfig {
11401140
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11411141

11421142
#[skip_serializing_none]
1143-
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1143+
#[derive(Debug, Clone, Serialize, Deserialize)]
11441144
#[serde(deny_unknown_fields)]
11451145
#[serde(rename_all = "camelCase")]
11461146
pub struct SearchIndexerConfig {
1147+
/// Whether incremental indexing is enabled
1148+
pub incremental_indexing: bool,
1149+
11471150
/// Whether to clear and re-index on start or use existing vectors if any
11481151
pub clear_on_start: bool,
11491152

@@ -1159,6 +1162,18 @@ pub struct SearchIndexerConfig {
11591162
pub payload_include_content: bool,
11601163
}
11611164

1165+
impl Default for SearchIndexerConfig {
1166+
fn default() -> Self {
1167+
Self {
1168+
incremental_indexing: true,
1169+
clear_on_start: false,
1170+
skip_datasets_with_no_description: true,
1171+
skip_datasets_with_no_data: true,
1172+
payload_include_content: false,
1173+
}
1174+
}
1175+
}
1176+
11621177
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11631178

11641179
#[skip_serializing_none]

src/app/cli/tests/tests/test_di_graph.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use kamu::domain::{KamuBackgroundCatalog, ServerUrlConfig, TenancyConfig};
1515
use kamu_accounts::{CurrentAccountSubject, JwtAuthenticationConfig};
1616
use kamu_adapter_http::AccessToken;
1717
use kamu_adapter_oauth::GithubAuthenticationConfig;
18+
use kamu_cli::config::CLIConfig;
1819
use kamu_cli::services::config::DatabaseConfig;
1920
use kamu_cli::{self, Interact, OutputConfig, WorkspaceLayout, WorkspaceStatus};
2021

@@ -75,6 +76,7 @@ fn test_di_cli_graph_validates(
7576
let mut base_catalog_builder = kamu_cli::configure_base_catalog(
7677
&workspace_layout,
7778
WorkspaceStatus::Created(tenancy_config),
79+
&CLIConfig::default(),
7880
tenancy_config,
7981
None,
8082
false,
@@ -118,6 +120,7 @@ fn test_di_server_graph_validates(
118120
let mut base_catalog_builder = kamu_cli::configure_base_catalog(
119121
&workspace_layout,
120122
WorkspaceStatus::Created(tenancy_config),
123+
&CLIConfig::default(),
121124
tenancy_config,
122125
None,
123126
false,
@@ -181,6 +184,7 @@ async fn update_di_graph() {
181184
let mut base_catalog_builder = kamu_cli::configure_base_catalog(
182185
&workspace_layout,
183186
WorkspaceStatus::Created(tenancy_config),
187+
&CLIConfig::default(),
184188
tenancy_config,
185189
None,
186190
false,

src/app/cli/tests/tests/test_system_info_command.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::sync::Arc;
1313
use container_runtime::ContainerRuntimeConfig;
1414
use dill::TypedBuilder;
1515
use kamu::domain::TenancyConfig;
16+
use kamu_cli::config::CLIConfig;
1617
use kamu_cli::*;
1718
use observability::build_info::BuildInfo;
1819

@@ -24,6 +25,7 @@ async fn test_system_info_command() {
2425
let cat = kamu_cli::configure_base_catalog(
2526
&workspace_layout,
2627
WorkspaceStatus::NoWorkspace,
28+
&CLIConfig::default(),
2729
TenancyConfig::SingleTenant,
2830
None,
2931
false,

src/domain/accounts/services/src/dependencies.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,20 @@ use crate::*;
1313

1414
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1515

16-
pub fn register_dependencies(b: &mut CatalogBuilder, needs_indexing: bool, production: bool) {
16+
#[derive(Clone, Copy)]
17+
pub struct AccountDomainDependenciesOptions {
18+
pub needs_indexing: bool,
19+
pub production: bool,
20+
pub incremental_search_indexing: bool,
21+
}
22+
23+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
24+
25+
pub fn register_dependencies(b: &mut CatalogBuilder, options: AccountDomainDependenciesOptions) {
1726
b.add::<AccountSearchSchemaProvider>();
18-
b.add::<AccountSearchUpdater>();
27+
if options.incremental_search_indexing {
28+
b.add::<AccountSearchUpdater>();
29+
}
1930

2031
b.add::<AccessTokenServiceImpl>();
2132
b.add::<AccountServiceImpl>();
@@ -32,10 +43,10 @@ pub fn register_dependencies(b: &mut CatalogBuilder, needs_indexing: bool, produ
3243

3344
b.add::<DidSecretService>();
3445

35-
if needs_indexing {
46+
if options.needs_indexing {
3647
b.add::<OAuthDeviceCodeServiceImpl>();
3748

38-
if production {
49+
if options.production {
3950
b.add::<OAuthDeviceCodeGeneratorDefault>();
4051
} else {
4152
b.add::<PredefinedOAuthDeviceCodeGenerator>();

src/domain/datasets/services/src/dependencies.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,29 @@
77
// the Business Source License, use of this software will be governed
88
// by the Apache License, Version 2.0.
99

10-
use dill::CatalogBuilder;
11-
1210
use crate::utils::CreateDatasetUseCaseHelper;
1311
use crate::*;
1412

1513
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1614

17-
pub fn register_dependencies(b: &mut CatalogBuilder, needs_indexing: bool) {
15+
#[derive(Clone, Copy)]
16+
pub struct DatasetDomainDependenciesOptions {
17+
pub needs_indexing: bool,
18+
pub incremental_search_indexing: bool,
19+
}
20+
21+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
22+
23+
pub fn register_dependencies(
24+
b: &mut dill::CatalogBuilder,
25+
options: DatasetDomainDependenciesOptions,
26+
) {
1827
b.add::<DatasetSearchSchemaProvider>();
19-
b.add::<DatasetSearchUpdater>();
28+
if options.incremental_search_indexing {
29+
b.add::<DatasetSearchUpdater>();
30+
}
2031

21-
if needs_indexing {
32+
if options.needs_indexing {
2233
b.add::<DatasetEntryIndexer>();
2334
b.add::<DatasetReferenceIndexer>();
2435
b.add::<DatasetStatisticsIndexer>();

src/domain/datasets/services/src/search/dataset_search_indexer.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ use kamu_search::*;
1717
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1818

1919
pub(crate) async fn index_dataset_from_scratch(
20+
indexer_config: &SearchIndexerConfig,
2021
dataset: ResolvedDataset,
2122
owner_id: &odf::AccountID,
22-
) -> Result<serde_json::Value, InternalError> {
23+
) -> Result<Option<serde_json::Value>, InternalError> {
2324
// Find key blocks that contribute to search
2425
let mut attachments_visitor = odf::dataset::SearchSetAttachmentsVisitor::new();
2526
let mut info_visitor = odf::dataset::SearchSetInfoVisitor::new();
@@ -46,6 +47,17 @@ pub(crate) async fn index_dataset_from_scratch(
4647
let (description, keywords) = extract_description_and_keywords(info_visitor);
4748
let attachments = extract_attachment_contents(attachments_visitor);
4849

50+
// Apply indexing configuration filters
51+
if indexer_config.skip_datasets_with_no_data && !schema_fields.is_present() {
52+
return Ok(None);
53+
}
54+
if indexer_config.skip_datasets_with_no_description
55+
&& !description.is_present()
56+
&& !attachments.is_present()
57+
{
58+
return Ok(None);
59+
}
60+
4961
// Seed event: created_at
5062
let seed_event_time = seed_visitor.into_block().unwrap().system_time;
5163

@@ -91,7 +103,7 @@ pub(crate) async fn index_dataset_from_scratch(
91103
index_doc_mut.insert(fields::ATTACHMENTS.to_string(), serde_json::json!(v));
92104
}
93105

94-
Ok(index_doc)
106+
Ok(Some(index_doc))
95107
}
96108

97109
Err(e) => Err(e.int_err()),
@@ -101,11 +113,12 @@ pub(crate) async fn index_dataset_from_scratch(
101113
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102114

103115
pub(crate) async fn partial_update_for_new_interval(
116+
indexer_config: &SearchIndexerConfig,
104117
dataset: ResolvedDataset,
105118
owner_id: &odf::AccountID,
106119
new_head: &odf::Multihash,
107120
maybe_prev_head: Option<&odf::Multihash>,
108-
) -> Result<serde_json::Value, InternalError> {
121+
) -> Result<Option<serde_json::Value>, InternalError> {
109122
// Extract key blocks that contribute to search
110123
let mut attachments_visitor = odf::dataset::SearchSetAttachmentsVisitor::new();
111124
let mut info_visitor = odf::dataset::SearchSetInfoVisitor::new();
@@ -136,7 +149,7 @@ pub(crate) async fn partial_update_for_new_interval(
136149
match result {
137150
Ok(_) => {}
138151
Err(AcceptVisitorError::Traversal(IterBlocksError::InvalidInterval(_))) => {
139-
return index_dataset_from_scratch(dataset, owner_id).await;
152+
return index_dataset_from_scratch(indexer_config, dataset, owner_id).await;
140153
}
141154
Err(e) => return Err(e.int_err()),
142155
}
@@ -170,7 +183,7 @@ pub(crate) async fn partial_update_for_new_interval(
170183
insert_search_incremental_update_field(&mut update, fields::KEYWORDS, keywords);
171184
insert_search_incremental_update_field(&mut update, fields::ATTACHMENTS, attachments);
172185

173-
Ok(serde_json::Value::Object(update))
186+
Ok(Some(serde_json::Value::Object(update)))
174187
}
175188

176189
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -270,6 +283,7 @@ pub(crate) async fn index_datasets(
270283
dataset_entry_service: &dyn DatasetEntryService,
271284
dataset_registry: &dyn DatasetRegistry,
272285
search_repo: &dyn SearchRepository,
286+
indexer_config: &SearchIndexerConfig,
273287
) -> Result<usize, InternalError> {
274288
const BULK_SIZE: usize = 500;
275289

@@ -288,7 +302,8 @@ pub(crate) async fn index_datasets(
288302
.int_err()?;
289303

290304
// Index dataset
291-
let dataset_document = index_dataset_from_scratch(dataset, &entry.owner_id).await?;
305+
let dataset_document =
306+
index_dataset_from_scratch(indexer_config, dataset, &entry.owner_id).await?;
292307
let dataset_document_json = serde_json::to_value(dataset_document).int_err()?;
293308

294309
tracing::debug!(

0 commit comments

Comments
 (0)