Reputation: 21239
I have a Spring Data MongoDB type containing a Set
of Comparable
types (DayOfWeek
in my specific case). This is logically a set, an it is therefore represented as such in the object. Spring Data MongoDB saves the set to the database as a MongoDB array.
public class MyType {
private Set<DayOfWeek> daysOfWeek;
public Set<DayOfWeek> getDaysOfWeek() { return daysOfWeek; }
public void setDaysOfWeek(Set<DayOfWeek> d) { this.daysOfWeek = d; }
}
An array in MongoDB is an ordered data structure, but Java's Set
is not. Therefore, when the Set
is translated to the MongoDB type by Spring, an ordering is by necessity applied to the elements. The resulting array is in the Set
's (generally arbitrary) iteration order, and not in a logical sorted order (or even in a guaranteed consistent order).
Using an arbitrary, inconsistent iteration order causes a number of (minor) issues. It makes the data more annoying to work with: it's harder to see what elements are present at a visual glance, and any tests on the database structure itself need to make sure they're ignoring the ordering of the resulting MongoDB array. It also means that the MongoDB field value could change over time despite it logically being the same value (and being the same value in the Java model), since the iteration order could vary each time the object is saved. Depending on how differences are determined, it could result in unnecessary updates to the database, since the array ordering may have changed even though nothing of any import has actually changed.
What I would like is the ability to use the natural order of the Set
elements when saving the object to a MongoDB document. Ideally, I do not want to force the Java type to be a sorted set, since there is no logical reason it needs to be one, beyond wanting them saved in a predictable sorted order for convenience and consistency.
How can I ensure that my object containing an unordered Set
is saved to MongoDB in a sorted order?
Upvotes: 1
Views: 742
Reputation: 339342
A Set
by definition does not have an iteration order. The name “set” was chosen from the mathematical term meaning a collection of distinct unordered elements.
If you need the elements to iterate in a certain order, use an implementation of SortedSet
or its successor NavigableSet
.
Note that you can use a sorted set internally within your class while using the more general Set
interface when interacting with the world outside your class. For your setter method, accept a Set
object, then feed that to a constructor of your chosen NavigableSet
such as TreeSet
. For your getter method, return a defensive copy of your internal NavigableSet
as a Set
.
Your case is special in that you are working with enum DayOfWeek
. If you want those sorted in the order defined by ISO 8601, Monday-Sunday, use an EnumSet
as your internal set object. While not a SortedSet
/NavigableSet
, the EnumSet
class promises to maintain an iteration order per the order in which the enum objects are declared.
public class MyType {
private EnumSet< DayOfWeek > dows ;
public Set< DayOfWeek > getDaysOfWeek() { return EnumSet.copyOf( this.dows) ; }
public void setDaysOfWeek( Set< DayOfWeek > d ) { this. dows = EnumSet.copyOf( d ) ; }
}
You said:
since there is no logical reason it needs to be one, beyond wanting them saved in a sorted order for convenience.
Actually, wanting them saved in sorted order is indeed a logical reason the specify a SortedSet
/NavigableSet
. I would recommend exporting the sorted variety of set rather than Set
. I wrote my Answer to use Set
only because you asked for it.
Upvotes: 2