use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize};
use crate::common;
use super::{
Aggregate, AggregateKey, ColumnPair, Filter, FlussoType, Join, JoinKind, Through, Transform,
ViaHop,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Field {
pub field: common::FieldName,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub options: BTreeMap<String, common::GenericValue>,
#[serde(default = "enabled", skip_serializing_if = "is_enabled")]
pub emit: bool,
pub source: FieldSource,
}
impl Hash for Field {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self {
field,
options,
emit,
source,
} = self;
field.hash(state);
options.hash(state);
source.hash(state);
if !emit {
"emit".hash(state);
}
}
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FieldSource {
Column(Column),
Group(Vec<Field>),
Geo(Geo),
Relation(Relation),
Constant(common::GenericValue),
Script(Script),
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct Script {
pub source: String,
pub ty: FlussoType,
pub nullable: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub array: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub enum_order: Vec<String>,
#[serde(default = "enabled", skip_serializing_if = "is_enabled")]
pub subfields: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Column {
pub column: common::ColumnName,
pub ty: FlussoType,
pub nullable: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub transforms: Vec<Transform>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<common::GenericValue>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub enum_order: Vec<String>,
#[serde(default = "enabled", skip_serializing_if = "is_enabled")]
pub subfields: bool,
#[serde(default = "enabled", skip_serializing_if = "is_enabled")]
pub indexed: bool,
}
pub(crate) fn enabled() -> bool {
true
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn is_enabled(flag: &bool) -> bool {
*flag
}
const MAP_KEY_MAPPING_REVISION: u8 = 2;
impl Hash for Column {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self {
column,
ty,
nullable,
transforms,
default,
enum_order,
subfields,
indexed,
} = self;
column.hash(state);
ty.hash(state);
nullable.hash(state);
transforms.hash(state);
default.hash(state);
enum_order.hash(state);
if matches!(ty, FlussoType::Map { .. }) {
MAP_KEY_MAPPING_REVISION.hash(state);
}
if !subfields {
"subfields".hash(state);
}
if !indexed {
"indexed".hash(state);
}
}
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub struct Geo {
pub lat: common::ColumnName,
pub lon: common::ColumnName,
pub nullable: bool,
}
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Relation {
Join(Join),
Aggregate(Aggregate),
}
impl Field {
pub fn children(&self) -> &[Field] {
match &self.source {
FieldSource::Group(fields) => fields,
FieldSource::Relation(Relation::Join(join)) => &join.fields,
FieldSource::Column(_)
| FieldSource::Geo(_)
| FieldSource::Relation(Relation::Aggregate(_))
| FieldSource::Constant(_)
| FieldSource::Script(_) => &[],
}
}
pub fn relation(&self) -> Option<&Relation> {
match &self.source {
FieldSource::Relation(relation) => Some(relation),
_ => None,
}
}
pub fn column(&self) -> Option<&common::ColumnName> {
match &self.source {
FieldSource::Column(column) => Some(&column.column),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum RelationKey<'a> {
Local(&'a common::ColumnName),
Direct(&'a common::ColumnName),
Through(&'a Through),
}
impl Relation {
pub fn table(&self) -> &common::TableName {
match self {
Relation::Join(join) => &join.table,
Relation::Aggregate(aggregate) => &aggregate.table,
}
}
pub fn key(&self) -> RelationKey<'_> {
match self {
Relation::Join(join) => match &join.kind {
JoinKind::BelongsTo { column } => RelationKey::Local(column),
JoinKind::HasOne { foreign_key } | JoinKind::HasMany { foreign_key } => {
RelationKey::Direct(foreign_key)
}
JoinKind::ManyToMany { through } => RelationKey::Through(through),
},
Relation::Aggregate(aggregate) => match &aggregate.key {
AggregateKey::Direct(foreign_key) => RelationKey::Direct(foreign_key),
AggregateKey::Through(through) => RelationKey::Through(through),
},
}
}
pub fn filters(&self) -> Option<&[Filter]> {
match self {
Relation::Join(join) => join.filters.as_deref(),
Relation::Aggregate(aggregate) => aggregate.filters.as_deref(),
}
}
pub fn via(&self) -> &[ViaHop] {
match self {
Relation::Join(_) => &[],
Relation::Aggregate(aggregate) => &aggregate.via,
}
}
pub fn on(&self) -> &[ColumnPair] {
match self {
Relation::Join(join) => &join.on,
Relation::Aggregate(aggregate) => &aggregate.on,
}
}
pub fn parent_on(&self) -> &[ColumnPair] {
match self.via().first() {
Some(hop) => &hop.on,
None => self.on(),
}
}
}
#[derive(Debug, Clone, Hash)]
pub struct Mapping {
pub mapping_type: MappingType,
pub extra: BTreeMap<String, common::GenericValue>,
pub map_values: Option<Box<Mapping>>,
pub decimal: bool,
pub enum_order: Option<Vec<String>>,
pub subfields: bool,
pub indexed: bool,
}
impl Mapping {
pub fn of_type(ty: &FlussoType) -> Self {
Self {
mapping_type: ty.opensearch(),
extra: ty.default_options(),
map_values: None,
decimal: matches!(ty, FlussoType::Decimal),
enum_order: None,
subfields: true,
indexed: true,
}
}
}
impl Serialize for Mapping {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let len = 1
+ self.extra.len()
+ usize::from(self.map_values.is_some())
+ usize::from(!self.subfields)
+ usize::from(!self.indexed);
let mut map = serializer.serialize_map(Some(len))?;
map.serialize_entry("type", &self.mapping_type)?;
for (key, value) in &self.extra {
map.serialize_entry(key, value)?;
}
if let Some(values) = &self.map_values {
map.serialize_entry("values", values)?;
}
if !self.subfields {
map.serialize_entry("subfields", &false)?;
}
if !self.indexed {
map.serialize_entry("indexed", &false)?;
}
map.end()
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum MappingType {
Text,
Keyword,
Boolean,
Byte,
Short,
Integer,
Long,
Float,
Double,
HalfFloat,
ScaledFloat,
Date,
Object,
Nested,
Other(String),
}
impl MappingType {
pub fn name(&self) -> &str {
match self {
MappingType::Text => "text",
MappingType::Keyword => "keyword",
MappingType::Boolean => "boolean",
MappingType::Byte => "byte",
MappingType::Short => "short",
MappingType::Integer => "integer",
MappingType::Long => "long",
MappingType::Float => "float",
MappingType::Double => "double",
MappingType::HalfFloat => "half_float",
MappingType::ScaledFloat => "scaled_float",
MappingType::Date => "date",
MappingType::Object => "object",
MappingType::Nested => "nested",
MappingType::Other(name) => name,
}
}
pub fn from_name(name: &str) -> MappingType {
match name {
"text" => MappingType::Text,
"keyword" => MappingType::Keyword,
"boolean" => MappingType::Boolean,
"byte" => MappingType::Byte,
"short" => MappingType::Short,
"integer" => MappingType::Integer,
"long" => MappingType::Long,
"float" => MappingType::Float,
"double" => MappingType::Double,
"half_float" => MappingType::HalfFloat,
"scaled_float" => MappingType::ScaledFloat,
"date" => MappingType::Date,
"object" => MappingType::Object,
"nested" => MappingType::Nested,
other => MappingType::Other(other.to_owned()),
}
}
}
impl Serialize for MappingType {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.name())
}
}