0

There is A Mongo Document named Product one product has many ProductOption and option has type from 1 to 5. the properties type is differ by its option type(example below).

What I want to do is defining QuestionThisType below. can you recommend for me?

@Document("Product")
data class Product(
    val id: Int,
    val options: List<ProductOption>,
) {
    data class ProductOption(
        val type: Int,
        val properties: QuestionThisType
    )
}

if type == 1 then

data class Property1(
   val one: String
)

if type == 2 then

data class Property2(
   val a: String,
   val b: String,
)
...
Tenfour04
  • 83,111
  • 11
  • 94
  • 154

1 Answers1

0

This may work for you - create your own Spring Data Converter

First, imagine a general way to store the data of ProductOption in a data type that Mongo can represent, e.g. Map<String,Any> that contains nested Map<String,Any>. Then remove the inner class:

@Document("Product")
data class Product(
    val id: Int,
    val options: List<ProductOption>,
)

Now you need an interface to link the real ProductOptions together for types 1,2,3,4,5 and then concrete classes for each of them

interface ProductOption {
    val type: Int
    val properties: Any
}

data class ProductOption1(
    override val type: Int = 1,
    override val properties: Property1,
) : ProductOption {
    data class Property1(
        val one: String,
    )
}

data class ProductOption2(
    override val type: Int = 2,
    override val properties: Property2,
) : ProductOption {
    data class Property2(
        val a: String,
        val b: String,
    )
}

etc...

Then you need to create a converter that takes these concrete classes that implement ProductOption and converts them to/from what get stored in Mongo: ProductOptionGeneral

import org.springframework.core.convert.converter.Converter
import org.springframework.data.convert.ReadingConverter
import org.springframework.data.convert.WritingConverter

class MapToProductOptionConverter : Converter<Map<String,Any>, ProductOption> {
    override fun convert(source: Map<String,Any>): ProductOption? {
        val properties = source["properties"] as Map<String, Any>
        when (source["type"] as Int) {
            1 -> {
                ProductOption1(
                    properties = ProductOption1.Property1(
                        one = properties["one"] as String
                    )
                )
            }
            else -> TODO()
        }
    }
}

@WritingConverter
class ProductOptionToMapConverter : Converter<ProductOption, Map<String,Any>> {
    override fun convert(source: ProductOption): Map<String,Any>? {
        TODO()
    }
}

Lastly register your converters

@Configuration
@EnableMongoRepositories
class MongoConfig {

    @Bean
    fun customConversions(): MongoCustomConversions {
        return MongoCustomConversions(
            listOf(
                MapToProductOptionConverter(),
                ProductOptionToMapConverter(),
            )
        )
    }

(If you had asked about handling JSON deserialization into a polymorphic class structure, this is possible in a much simpler way. This could be wholly different approach for you. You'd retrieve the data from Mongo as a BSON object, convert to JSON and use Jackson's ObjectMapper in the way I describe in this Stackoverflow Answer: https://stackoverflow.com/a/72106352/1847378)

AndrewL
  • 2,034
  • 18
  • 18