scala-commons icon indicating copy to clipboard operation
scala-commons copied to clipboard

Referencing nested array fields in typed Mongo

Open AvaPL opened this issue 3 years ago • 6 comments

Assuming a hierarchy like this:

case class InnerRecord(
  int: Int,
  str: String,
)
object InnerRecord extends MongoDataCompanion[InnerRecord]

case class RecordTestEntity(
  id: String,
  innerList: List[InnerRecord]
) extends MongoEntity[String]
object RecordTestEntity extends MongoEntityCompanion[RecordTestEntity]

How can I construct a projection so that only innerList.int fields are returned? I know I can reference the fields at particular index of the list, eg. RecordTestEntity.ref(_.innerList.head.int), but I would like to achieve something like this: RecordTestEntity.ref(_.innerList.map(_.int)). Is it possible?

AvaPL avatar Jul 20 '22 09:07 AvaPL

Hi, I'm not aware of any MongoDB projection operator that would do this (please let me know if you find one). Assuming that there is no such operator in MongoDB API itself, you have two options:

  • Map the projection itself in runtime, i.e. RecordTestEntity.ref(_.innerList).map(_.map(_.int)) - the mapping will happen on the client side, MongoDB will not be aware of it and will be sending full objects on the wire. Note that this is largely equivalent to mapping the Observable returned by TypedMongoCollection.find().
  • Use the Aggregation Framework - you will need to use the native API of the ReactiveStreams driver as it is not covered by AVSystem's Typed Mongo API

ghik avatar Jul 20 '22 10:07 ghik

Just specifying the path to the field inside array (innerList.int) works for me using Compass/MongoDB shell. https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/#projection-on-embedded-documents-in-an-array - the syntax is described here.

AvaPL avatar Jul 20 '22 11:07 AvaPL

Oh, nice, I didn't know about that. We will add support for it.

ghik avatar Jul 20 '22 11:07 ghik

@AvaPL FYI: I hoped to do this quickly but it turned out to be somewhat annoying, mostly because of binary compatibility issues. We're still planning to do this one way or another.

ghik avatar Aug 24 '22 12:08 ghik

No pressure, thank you for the information.

AvaPL avatar Aug 24 '22 15:08 AvaPL

There's another use-case for innerList.int path, i.e. it can be used to filter documents by inner field value as described here: https://www.mongodb.com/docs/manual/tutorial/query-array-of-documents/#specify-a-query-condition-on-a-field-in-an-array-of-documents. It would be nice if it was somehow supported by the driver.

Currently we do this kind of workaround, which works for queries and indexes, but it probably fails for updates.

final val InnerIntRef: Ref[Int] = FieldRef(ref(_.innerList), "int", implicitly, Opt.Empty)

enthyp avatar Jan 02 '23 11:01 enthyp