Skip to content

Commit 3fab26e

Browse files
committed
fix(mongodb): work around reserved language field in text search
- Implement `_transformMapForDb` to rename 'language' to 'modelLanguage' before database insertion - Implement `_transformMapFromDb` to reverse the transformation after database retrieval - Apply transformations in create, update, and find operations - Update documentation to explain the purpose and functionality of the transformations
1 parent 48626cf commit 3fab26e

File tree

1 file changed

+45
-4
lines changed

1 file changed

+45
-4
lines changed

lib/src/data_mongodb.dart

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,39 @@ class DataMongodb<T> implements DataClient<T> {
8484
final Logger _logger;
8585
final _uuid = const Uuid();
8686

87+
/// Transforms a data map before it's sent to the database.
88+
///
89+
/// This method addresses a critical issue where the field name 'language'
90+
/// conflicts with a reserved keyword in MongoDB's `text` search options.
91+
/// To prevent this, it transparently renames any 'language' field to
92+
/// 'modelLanguage' before the document is written.
93+
Map<String, dynamic> _transformMapForDb(Map<String, dynamic> map) {
94+
if (map.containsKey('language')) {
95+
final newMap = Map<String, dynamic>.from(map);
96+
final languageValue = newMap.remove('language');
97+
newMap['modelLanguage'] = languageValue;
98+
return newMap;
99+
}
100+
return map;
101+
}
102+
103+
/// Transforms a data map after it's retrieved from the database.
104+
///
105+
/// This is the counterpart to [_transformMapForDb]. It checks for the
106+
/// 'modelLanguage' field (which was renamed for storage) and transforms it
107+
/// back to the original 'language' field that the application's data models
108+
/// expect. This ensures the database-level workaround is invisible to the
109+
/// rest of the application.
110+
Map<String, dynamic> _transformMapFromDb(Map<String, dynamic> map) {
111+
if (map.containsKey('modelLanguage')) {
112+
final newMap = Map<String, dynamic>.from(map);
113+
final languageValue = newMap.remove('modelLanguage');
114+
newMap['language'] = languageValue;
115+
return newMap;
116+
}
117+
return map;
118+
}
119+
87120
/// A getter for the MongoDB collection for the given model type [T].
88121
DbCollection get _collection => _connectionManager.db.collection(_modelName);
89122

@@ -96,7 +129,9 @@ class DataMongodb<T> implements DataClient<T> {
96129
// We create a copy to avoid modifying the original map.
97130
final newDoc = Map<String, dynamic>.from(doc);
98131
newDoc['id'] = (newDoc['_id'] as ObjectId).oid;
99-
return _fromJson(newDoc);
132+
// Apply the reverse transformation for the 'language' field.
133+
final transformedDoc = _transformMapFromDb(newDoc);
134+
return _fromJson(transformedDoc);
100135
}
101136

102137
/// Prepares a model of type [T] for insertion or update in MongoDB.
@@ -285,7 +320,8 @@ class DataMongodb<T> implements DataClient<T> {
285320
}) async {
286321
_logger.fine('Attempting to create item in collection: $_modelName...');
287322
try {
288-
final doc = _prepareDocumentForInsertionOrUpdate(item);
323+
final preparedDoc = _prepareDocumentForInsertionOrUpdate(item);
324+
final doc = _transformMapForDb(preparedDoc);
289325
_logger.finer('Prepared document for insertion with _id: ${doc['_id']}');
290326

291327
// DIAGNOSTIC: Log the exact document before insertion.
@@ -488,10 +524,11 @@ class DataMongodb<T> implements DataClient<T> {
488524

489525
// Prepare the document for update. This ensures the item's ID is
490526
// validated before proceeding.
491-
final docToUpdate = _prepareDocumentForInsertionOrUpdate(item)
527+
final preparedDoc = _prepareDocumentForInsertionOrUpdate(item)
492528
// The `_id` field must be removed from the update payload itself,
493529
// as it's illegal to modify the `_id` of an existing document.
494530
..remove('_id');
531+
final docToUpdate = _transformMapForDb(preparedDoc);
495532
_logger.finer('Update payload: $docToUpdate');
496533

497534
// Use findAndModify for an atomic update and return operation.
@@ -575,8 +612,12 @@ class DataMongodb<T> implements DataClient<T> {
575612
.aggregateToStream(finalPipeline)
576613
.toList();
577614

615+
// Apply the reverse transformation for the 'language' field to each
616+
// document in the result set.
617+
final transformedResults = results.map(_transformMapFromDb).toList();
618+
578619
return SuccessApiResponse(
579-
data: results,
620+
data: transformedResults,
580621
metadata: ResponseMetadata(
581622
requestId: _uuid.v4(),
582623
timestamp: DateTime.now(),

0 commit comments

Comments
 (0)