schema_core/config/
projection.rs1use crate::common::{ColumnName, GenericValue, IndexName};
13
14use super::{
15 Aggregate, AggregateOp, Column, ContentHash, Field, FieldSource, IndexMapping, IndexSchema,
16 Mapping, MappingType, Relation, ResolvedField,
17};
18
19impl IndexSchema {
20 pub fn resolve(&self, index: IndexName) -> IndexMapping {
22 resolve_index(index, self)
23 }
24}
25
26fn resolve_index(index: IndexName, schema: &IndexSchema) -> IndexMapping {
27 IndexMapping {
28 index,
29 hash: ContentHash::of(schema),
32 fields: resolve_fields(&schema.fields, schema.primary_key.as_ref()),
33 }
34}
35
36fn resolve_fields(fields: &[Field], primary_key: Option<&ColumnName>) -> Vec<ResolvedField> {
40 fields
41 .iter()
42 .map(|field| resolve_field(field, primary_key))
43 .collect()
44}
45
46fn resolve_field(field: &Field, primary_key: Option<&ColumnName>) -> ResolvedField {
47 let (child_fields, child_pk): (&[Field], Option<&ColumnName>) = match &field.source {
52 FieldSource::Relation(Relation::Join(join)) => (&join.fields, Some(&join.primary_key)),
53 FieldSource::Group(fields) => (fields, primary_key),
54 _ => (&[], primary_key),
55 };
56 let children = resolve_fields(child_fields, child_pk);
57
58 let (mapping_type, nullable) = type_and_nullability(field, primary_key);
59 let mapping = Mapping {
60 mapping_type,
61 extra: field.options.clone(),
62 };
63
64 ResolvedField {
65 name: field.field.clone(),
66 mapping,
67 nullable,
68 children,
69 }
70}
71
72fn type_and_nullability(field: &Field, primary_key: Option<&ColumnName>) -> (MappingType, bool) {
74 match &field.source {
75 FieldSource::Column(Column {
78 column,
79 ty,
80 nullable,
81 default,
82 ..
83 }) => {
84 let forced_non_null = primary_key == Some(column) || default.is_some();
85 (ty.opensearch(), *nullable && !forced_non_null)
86 }
87 FieldSource::Group(_) => (MappingType::Object, false),
89 FieldSource::Geo(geo) => (MappingType::Other("geo_point".to_owned()), geo.nullable),
92 FieldSource::Constant(value) => (
94 constant_mapping_type(value),
95 matches!(value, GenericValue::Null),
96 ),
97 FieldSource::Relation(Relation::Join(join)) => {
101 if join.kind.is_to_many() {
102 (MappingType::Nested, false)
103 } else {
104 (MappingType::Object, true)
105 }
106 }
107 FieldSource::Relation(Relation::Aggregate(aggregate)) => aggregate_type(aggregate),
110 }
111}
112
113fn aggregate_type(aggregate: &Aggregate) -> (MappingType, bool) {
114 match &aggregate.op {
115 AggregateOp::Count => (MappingType::Long, false),
116 AggregateOp::Avg(_) => (MappingType::Double, true),
117 AggregateOp::Sum(_) | AggregateOp::Min(_) | AggregateOp::Max(_) => {
118 let mapping_type = aggregate
119 .value_type
120 .as_ref()
121 .map(|ty| ty.opensearch())
122 .unwrap_or(MappingType::Double);
125 (mapping_type, true)
126 }
127 }
128}
129
130fn constant_mapping_type(value: &GenericValue) -> MappingType {
132 match value {
133 GenericValue::Bool(_) => MappingType::Boolean,
134 GenericValue::Int(_) => MappingType::Long,
135 GenericValue::Decimal(_) => MappingType::Double,
136 GenericValue::Array(items) => items
137 .first()
138 .map(constant_mapping_type)
139 .unwrap_or(MappingType::Keyword),
140 GenericValue::Map(_) => MappingType::Object,
141 GenericValue::String(_) | GenericValue::Null => MappingType::Keyword,
142 }
143}