Skip to content

Commit

Permalink
feat: Added ModelSearch.
Browse files Browse the repository at this point in the history
  • Loading branch information
mathrunet committed Jun 7, 2023
1 parent 80c3cf5 commit 1cf9f55
Show file tree
Hide file tree
Showing 9 changed files with 416 additions and 25 deletions.
6 changes: 3 additions & 3 deletions packages/katana_model/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -342,21 +342,21 @@ packages:
path: "../../katana"
relative: true
source: path
version: "2.0.1"
version: "2.1.0"
katana_listenables_annotation:
dependency: "direct overridden"
description:
path: "../../katana_listenables_annotation"
relative: true
source: path
version: "2.0.2"
version: "2.0.3"
katana_model:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "2.0.2"
version: "2.1.0"
lints:
dependency: transitive
description:
Expand Down
214 changes: 214 additions & 0 deletions packages/katana_model/lib/src/model_field_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ abstract class ModelFieldValue<T> {
const ModelTimestampConverter(),
const ModelGeoValueConverter(),
const ModelUriConverter(),
const ModelSearchConverter(),
const ModelRefConverter(),
};

Expand All @@ -151,6 +152,7 @@ abstract class ModelFieldValue<T> {
const ModelTimestampFilter(),
const ModelGeoValueFilter(),
const ModelUriFilter(),
const ModelSearchFilter(),
const ModelRefFilter(),
};

Expand Down Expand Up @@ -1232,3 +1234,215 @@ class ModelUriFilter extends ModelFieldValueFilter<ModelUri> {
return null;
}
}

/// Define searchable fields.
///
/// You can store values as searchable values and search for elements that contain all of those defined in [ModelQueryFilter.equal].
///
/// Available for category search, etc.
///
/// 検索可能なフィールドを定義します。
///
/// 値を検索可能な値として保存し、[ModelQueryFilter.equal]で定義されたものがすべて含まれる要素を検索することができます。
///
/// カテゴリー検索等に利用可能です。
@immutable
class ModelSearch extends ModelFieldValue<List<String>> {
/// Define searchable fields.
///
/// You can store values as searchable values and search for elements that contain all of those defined in [ModelQueryFilter.equal].
///
/// Available for category search, etc.
///
/// 検索可能なフィールドを定義します。
///
/// 値を検索可能な値として保存し、[ModelQueryFilter.equal]で定義されたものがすべて含まれる要素を検索することができます。
///
/// カテゴリー検索等に利用可能です。
const factory ModelSearch(List<String> searchList) = _ModelSearch;

/// Used to disguise the retrieval of data from the server.
///
/// Use for testing purposes.
///
/// サーバーからのデータの取得に偽装するために利用します。
///
/// テスト用途で用いてください。
const factory ModelSearch.fromServer(List<String> searchList) =
_ModelSearch.fromServer;

/// Convert from [json] map to [ModelSearch].
///
/// [json]のマップから[ModelSearch]に変換します。
factory ModelSearch.fromJson(DynamicMap json) {
final list = json.getAsList<String>(kListKey);
return ModelSearch.fromServer(list);
}

const ModelSearch._(
List<String> value, [
ModelFieldValueSource source = ModelFieldValueSource.user,
]) : _value = value,
_source = source;

/// Key to save the list.
///
/// リストを保存しておくキー。
static const kListKey = "@list";

/// Key to store the data source.
///
/// データソースを保存しておくキー。
static const kSourceKey = "@source";

@override
List<String> get value => _value ?? [];
final List<String>? _value;

final ModelFieldValueSource _source;

@override
String toString() {
return value.toString();
}

@override
DynamicMap toJson() => {
kTypeFieldKey: (ModelSearch).toString(),
kListKey: value,
kSourceKey: _source.name,
};

@override
bool operator ==(Object other) => hashCode == other.hashCode;

@override
int get hashCode {
if (_value == null) {
return null.hashCode;
}
return Object.hashAll(_value!);
}
}

@immutable
class _ModelSearch extends ModelSearch
with ModelFieldValueAsMapMixin<List<String>> {
const _ModelSearch(
List<String> value, [
ModelFieldValueSource source = ModelFieldValueSource.user,
]) : super._(value, source);
const _ModelSearch.fromServer(List<String> value)
: super._(value, ModelFieldValueSource.server);
}

/// [ModelFieldValueConverter] to enable automatic conversion of [ModelSearch] as [ModelFieldValue].
///
/// [ModelSearch][ModelFieldValue]として自動変換できるようにするための[ModelFieldValueConverter]
@immutable
class ModelSearchConverter extends ModelFieldValueConverter<ModelSearch> {
/// [ModelFieldValueConverter] to enable automatic conversion of [ModelSearch] as [ModelFieldValue].
///
/// [ModelSearch][ModelFieldValue]として自動変換できるようにするための[ModelFieldValueConverter]
const ModelSearchConverter();

@override
ModelSearch fromJson(Map<String, Object?> map) {
return ModelSearch.fromJson(map);
}

@override
Map<String, Object?> toJson(ModelSearch value) {
return value.toJson();
}
}

/// Filter class to make [ModelSearch] available to [ModelQuery.filters].
///
/// [ModelSearch][ModelQuery.filters]で利用できるようにするためのフィルタークラス。
@immutable
class ModelSearchFilter extends ModelFieldValueFilter<ModelSearch> {
/// Filter class to make [ModelSearch] available to [ModelQuery.filters].
///
/// [ModelSearch][ModelQuery.filters]で利用できるようにするためのフィルタークラス。
const ModelSearchFilter();

@override
int? compare(dynamic a, dynamic b) {
return _hasMatch(a, b, (a, b) => a.toString().compareTo(b.toString()));
}

@override
bool? hasMatch(ModelQueryFilter filter, dynamic source) {
final target = filter.value;
switch (filter.type) {
case ModelQueryFilterType.equalTo:
return _hasMatch(
source,
target,
(source, target) => target.every((e) => source.contains(e)),
);
case ModelQueryFilterType.notEqualTo:
return _hasMatch(
source,
target,
(source, target) => target.every((e) => !source.contains(e)),
);
case ModelQueryFilterType.lessThan:
case ModelQueryFilterType.greaterThan:
case ModelQueryFilterType.lessThanOrEqualTo:
case ModelQueryFilterType.greaterThanOrEqualTo:
return null;
case ModelQueryFilterType.arrayContains:
return _hasMatch(
source,
target,
(source, target) => target.every((e) => source.contains(e)),
);
case ModelQueryFilterType.arrayContainsAny:
return _hasMatch(
source,
target,
(source, target) => target.every((e) => source.contains(e)),
);
case ModelQueryFilterType.whereIn:
case ModelQueryFilterType.whereNotIn:
return null;
default:
return null;
}
}

T? _hasMatch<T>(
dynamic source,
dynamic target,
T Function(List<String> source, List<String> target) filter,
) {
if (source is ModelSearch && target is ModelSearch) {
return filter(source.value, target.value);
} else if (source is ModelSearch && target is List) {
return filter(source.value, target.map((e) => e.toString()).toList());
} else if (source is List && target is ModelSearch) {
return filter(source.map((e) => e.toString()).toList(), target.value);
} else if (source is ModelSearch && target is String) {
return filter(source.value, [target]);
} else if (source is String && target is ModelSearch) {
return filter([source], target.value);
} else if (source is ModelSearch &&
target is DynamicMap &&
target.get(kTypeFieldKey, "") == (ModelSearch).toString()) {
return filter(source.value, ModelSearch.fromJson(target).value);
} else if (source is DynamicMap &&
target is ModelSearch &&
source.get(kTypeFieldKey, "") == (ModelSearch).toString()) {
return filter(ModelSearch.fromJson(source).value, target.value);
} else if (source is DynamicMap &&
target is DynamicMap &&
source.get(kTypeFieldKey, "") == (ModelSearch).toString() &&
target.get(kTypeFieldKey, "") == (ModelSearch).toString()) {
return filter(ModelSearch.fromJson(source).value,
ModelSearch.fromJson(target).value);
}
return null;
}
}
6 changes: 3 additions & 3 deletions packages/katana_model/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ packages:
path: "../katana"
relative: true
source: path
version: "2.0.1"
version: "2.1.1"
lints:
dependency: transitive
description:
Expand Down Expand Up @@ -779,10 +779,10 @@ packages:
dependency: transitive
description:
name: uuid
sha256: "2dd495c04ea91ba961459cd7b4588ce67f7cb4a236edc79031a69667067bd8db"
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "4.0.0-beta3-1"
version: "3.0.7"
vector_math:
dependency: transitive
description:
Expand Down
15 changes: 14 additions & 1 deletion packages/katana_model/test/model_field_value_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class TestValue with _$TestValue {
@Default(ModelCounter(0)) ModelCounter counter,
@Default(ModelUri()) ModelUri uri,
@Default(ModelGeoValue()) ModelGeoValue geo,
@Default(ModelSearch([])) ModelSearch search,
}) = _TestValue;

factory TestValue.fromJson(Map<String, Object?> map) =>
Expand Down Expand Up @@ -75,7 +76,8 @@ void main() {
"geo": ModelGeoValue.fromDouble(
latitude: 35.68177834908552,
longitude: 139.75310000426765,
)
),
"search": const ModelSearch(["aaaa", "bbbb", "cccc"])
});
expect(
model.value,
Expand All @@ -87,6 +89,7 @@ void main() {
latitude: 35.68177834908552,
longitude: 139.75310000426765,
),
"search": const ModelSearch(["aaaa", "bbbb", "cccc"])
},
);
await model2.load();
Expand All @@ -100,12 +103,14 @@ void main() {
latitude: 35.68177834908552,
longitude: 139.75310000426765,
),
"search": const ModelSearch(["aaaa", "bbbb", "cccc"])
},
);
print((model.value!["counter"] as ModelCounter).value);
print((model.value!["time"] as ModelTimestamp).value);
print((model.value!["uri"] as ModelUri).value);
print((model.value!["geo"] as ModelGeoValue).value);
print((model.value!["search"] as ModelSearch).value);
await model.save({
"counter": model.value?.getAsModelCounter("counter").increment(1),
"time": ModelTimestamp(DateTime(2022, 1, 2)),
Expand All @@ -114,11 +119,13 @@ void main() {
latitude: 35.67389581850969,
longitude: 139.75049296820384,
),
"search": const ModelSearch(["ddd", "eee"]),
});
print((model.value!["counter"] as ModelCounter).value);
print((model.value!["time"] as ModelTimestamp).value);
print((model.value!["uri"] as ModelUri).value);
print((model.value!["geo"] as ModelGeoValue).value);
print((model.value!["search"] as ModelSearch).value);
expect(
model.value,
{
Expand All @@ -129,6 +136,7 @@ void main() {
latitude: 35.67389581850969,
longitude: 139.75049296820384,
),
"search": const ModelSearch(["ddd", "eee"]),
},
);
});
Expand All @@ -146,6 +154,7 @@ void main() {
latitude: 35.68177834908552,
longitude: 139.75310000426765,
),
search: const ModelSearch(["aaaa", "bbbb", "cccc"]),
),
);
expect(
Expand All @@ -158,6 +167,7 @@ void main() {
latitude: 35.68177834908552,
longitude: 139.75310000426765,
),
search: const ModelSearch(["aaaa", "bbbb", "cccc"]),
),
);
await model2.load();
Expand All @@ -171,6 +181,7 @@ void main() {
latitude: 35.68177834908552,
longitude: 139.75310000426765,
),
search: const ModelSearch(["aaaa", "bbbb", "cccc"]),
),
);
await model.save(
Expand All @@ -184,6 +195,7 @@ void main() {
latitude: 35.67389581850969,
longitude: 139.75049296820384,
),
search: const ModelSearch(["ddd", "eee"]),
),
);
expect(
Expand All @@ -196,6 +208,7 @@ void main() {
latitude: 35.67389581850969,
longitude: 139.75049296820384,
),
search: const ModelSearch(["ddd", "eee"]),
),
);
});
Expand Down
Loading

0 comments on commit 1cf9f55

Please sign in to comment.