Skip to content

Commit 2fd72c9

Browse files
committed
feat(ht_data_mongodb): add configurable text search with regex
- Add searchableFields parameter to HtDataMongodb constructor - Implement configurable text search using MongoDB regex - Log warning when search term is provided but no searchableFields are configured - Add ignore_for_file directive for cascade_invocations
1 parent a1b819f commit 2fd72c9

File tree

1 file changed

+37
-5
lines changed

1 file changed

+37
-5
lines changed

lib/src/ht_data_mongodb.dart

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore_for_file: cascade_invocations
2+
13
import 'package:ht_data_client/ht_data_client.dart';
24
import 'package:ht_data_mongodb/src/mongo_db_connection_manager.dart';
35
import 'package:ht_shared/ht_shared.dart';
@@ -66,6 +68,7 @@ class HtDataMongodb<T> implements HtDataClient<T> {
6668
required String modelName,
6769
required FromJson<T> fromJson,
6870
required ToJson<T> toJson,
71+
this.searchableFields,
6972
Logger? logger,
7073
}) : _connectionManager = connectionManager,
7174
_modelName = modelName,
@@ -74,6 +77,7 @@ class HtDataMongodb<T> implements HtDataClient<T> {
7477
_logger = logger ?? Logger('HtDataMongodb<$T>');
7578

7679
final MongoDbConnectionManager _connectionManager;
80+
final List<String>? searchableFields;
7781
final String _modelName;
7882
final FromJson<T> _fromJson;
7983
final ToJson<T> _toJson;
@@ -147,11 +151,39 @@ class HtDataMongodb<T> implements HtDataClient<T> {
147151
final processedFilter = Map<String, dynamic>.from(filter);
148152

149153
// Check for the special 'q' parameter for text search.
150-
if (processedFilter.containsKey('q')) {
151-
final searchTerm = processedFilter.remove('q');
152-
// Add the MongoDB text search operator.
153-
// This assumes a text index exists on the collection.
154-
processedFilter[r'$text'] = {r'$search': searchTerm};
154+
if (processedFilter.containsKey('q') &&
155+
searchableFields != null &&
156+
searchableFields!.isNotEmpty) {
157+
final searchTerm = processedFilter.remove('q') as String;
158+
final searchConditions = <Map<String, dynamic>>[];
159+
160+
for (final field in searchableFields!) {
161+
searchConditions.add({
162+
field: {r'$regex': searchTerm, r'$options': 'i'},
163+
});
164+
}
165+
166+
// If there's already an '$and' operator, add the '$or' to it.
167+
// Otherwise, create a new '$and' that includes the '$or'.
168+
if (processedFilter.containsKey(r'$and')) {
169+
final existingAnd = processedFilter[r'$and'] as List;
170+
existingAnd.add({r'$or': searchConditions});
171+
} else {
172+
// If there are other filters, they need to be combined with the search.
173+
final otherFilters = Map<String, dynamic>.from(processedFilter);
174+
processedFilter.clear();
175+
processedFilter[r'$and'] = [
176+
otherFilters,
177+
{r'$or': searchConditions}
178+
];
179+
}
180+
} else if (processedFilter.containsKey('q')) {
181+
// If 'q' is present but no searchable fields are configured,
182+
// remove it to prevent errors.
183+
processedFilter.remove('q');
184+
_logger.warning(
185+
'Search term "q" was provided, but no searchableFields are configured for $_modelName. Ignoring search term.',
186+
);
155187
}
156188

157189
// If there's only one filter condition after processing, return it directly.

0 commit comments

Comments
 (0)