Student c ID

@Serializable
data class Student(
    val name: String,
    val group: String,
    @Contextual val id: Id<Student> = newId()
)

val json = Json { 
  serializersModule = IdKotlinXSerializationModule 
}

Ссылки в Mongo

@Serializable
data class Grade(
    val studentId: @Contextual Id<Student>,
    val studentName: String,
    val value: Int? = null,
    @Serializable(with = DateAsLongSerializer::class)
    val date: Date? = null,
)
@Serializable
data class Course(
    val name: String,
    val grades: List<Grade> = emptyList()
)

Тестовые данные

fun fillStudentsAndCourse(
  fillCourse: Boolean = true): List<Student> {
  val students = 
    listOf("Penny", "Amy").map {Student(it, "Girls")} +
    listOf("Sheldon", "Leonard", "Howard", "Raj")
      .map { Student(it, "Boys") }
  mStudents.insertMany(students)
  if(fillCourse) {
    val courses = listOf("Math", "Phys", "History").map {
      Course(it, students.map { Grade(it.id, it.name) })}
    mCourses.insertMany(courses)
  }
return students }

Тестовые данные

Выставление оценки. Фильтр

fun setGrade(courseName: String, studentName: String, value: Int) =
  mCourses.updateOne(
    and(
      Course::name eq courseName,
      Course::grades / Grade::studentName eq studentName
    ),
    ...
)
operator fun <T0, T1, T2> KProperty1<T0, Iterable<T1>?>
  .div(p2: KProperty1<out T1, T2?>): KProperty1<T0, T2?> =
    KPropertyPath(this, p2)

Выставление оценки. Операторы set и $

fun setGrade(courseName: String, studentName: String, value: Int) =
  mCourses.updateOne(
    and(
      Course::name eq courseName,
      Course::grades / Grade::studentName eq studentName
    ),
    setValue(Course::grades.posOp / Grade::value, value)
)

Выставление оценки. Операторы set и $

setGrade("Math", "Penny", 5)

{"update": "course", "ordered": true, "$db": "test", 
"lsid": {...}, 
"updates": [{
  "q": {"$and": [
    {"name": "Math"}, 
    {"grades.studentName": "Penny"}]}, 
  "u": {"$set": {"grades.$.value": 5}}}]}

Обновленные данные