5353import org .springframework .expression .ParserContext ;
5454import org .springframework .expression .spel .standard .SpelExpressionParser ;
5555import org .springframework .expression .spel .support .StandardEvaluationContext ;
56+ import org .springframework .lang .Nullable ;
57+ import org .springframework .util .ReflectionUtils ;
5658
5759import java .util .*;
5860import java .util .Map .Entry ;
@@ -178,73 +180,115 @@ private ArangoCollection _collection(final String name, final ArangoPersistentEn
178180 @ SuppressWarnings ("deprecation" )
179181 private static void ensureCollectionIndexes (final CollectionOperations collection ,
180182 final ArangoPersistentEntity <?> persistentEntity ) {
181- persistentEntity . getPersistentIndexes (). forEach ( index -> ensurePersistentIndex ( collection , index ) );
182- persistentEntity .getPersistentIndexedProperties ().forEach (p -> ensurePersistentIndex (collection , p ));
183- persistentEntity .getGeoIndexes ().forEach (index -> ensureGeoIndex (collection , index ));
184- persistentEntity .getGeoIndexedProperties ().forEach (p -> ensureGeoIndex (collection , p ));
185- persistentEntity .getFulltextIndexes ().forEach (index -> ensureFulltextIndex (collection , index ));
186- persistentEntity .getFulltextIndexedProperties ().forEach (p -> ensureFulltextIndex (collection , p ));
187- persistentEntity .getTtlIndex ().ifPresent ( index -> ensureTtlIndex (collection , index ));
188- persistentEntity .getTtlIndexedProperty ().ifPresent (p -> ensureTtlIndex (collection , p ));
189- persistentEntity .getMDIndexes ().forEach ( index -> ensureMDIndex (collection , index ));
190- persistentEntity .getMDPrefixedIndexes ().forEach (index -> ensureMDPrefixedIndex (collection , index ));
191- }
192-
193- private static void ensurePersistentIndex ( final CollectionOperations collection , final PersistentIndex annotation ) {
194- collection . ensurePersistentIndex ( Arrays . asList ( annotation . fields ()),
195- new PersistentIndexOptions ()
183+ Collection < IndexEntity > existing = collection . getIndexes ( );
184+ persistentEntity .getPersistentIndexes ().forEach (index -> ensurePersistentIndex (collection , index , existing ));
185+ persistentEntity .getPersistentIndexedProperties ().forEach (p -> ensurePersistentIndex (collection , p , existing ));
186+ persistentEntity .getGeoIndexes ().forEach (index -> ensureGeoIndex (collection , index , existing ));
187+ persistentEntity .getGeoIndexedProperties ().forEach (p -> ensureGeoIndex (collection , p , existing ));
188+ persistentEntity .getFulltextIndexes ().forEach (index -> ensureFulltextIndex (collection , index , existing ));
189+ persistentEntity .getFulltextIndexedProperties ().forEach ( p -> ensureFulltextIndex (collection , p , existing ));
190+ persistentEntity .getTtlIndex ().ifPresent (index -> ensureTtlIndex (collection , index , existing ));
191+ persistentEntity .getTtlIndexedProperty ().ifPresent ( p -> ensureTtlIndex (collection , p , existing ));
192+ persistentEntity .getMDIndexes ().forEach (index -> ensureMDIndex (collection , index , existing ));
193+ persistentEntity . getMDPrefixedIndexes (). forEach ( index -> ensureMDPrefixedIndex ( collection , index , existing ));
194+ }
195+
196+ private static void ensurePersistentIndex ( final CollectionOperations collection , final PersistentIndex annotation , Collection < IndexEntity > existing ) {
197+ PersistentIndexOptions options = new PersistentIndexOptions ()
196198 .unique (annotation .unique ())
197199 .sparse (annotation .sparse ())
198- .deduplicate (annotation .deduplicate ()));
200+ .deduplicate (annotation .deduplicate ());
201+ Collection <String > fields = Arrays .asList (annotation .fields ());
202+ if (existing .stream ().noneMatch (index -> equalPersistentIndex (index , options , fields ))) {
203+ collection .ensurePersistentIndex (fields , options );
204+ }
199205 }
200206
201207 private static void ensurePersistentIndex (final CollectionOperations collection ,
202- final ArangoPersistentProperty value ) {
208+ final ArangoPersistentProperty value , Collection < IndexEntity > existing ) {
203209 final PersistentIndexOptions options = new PersistentIndexOptions ();
204210 value .getPersistentIndexed ().ifPresent (i -> options
205211 .unique (i .unique ())
206212 .sparse (i .sparse ())
207213 .deduplicate (i .deduplicate ()));
208- collection .ensurePersistentIndex (Collections .singleton (value .getFieldName ()), options );
214+ Collection <String > fields = Collections .singleton (value .getFieldName ());
215+ if (existing .stream ().noneMatch (index -> equalPersistentIndex (index , options , fields ))) {
216+ collection .ensurePersistentIndex (fields , options );
217+ }
218+ }
219+
220+ private static boolean equalPersistentIndex (IndexEntity index , PersistentIndexOptions options , Collection <String > fields ) {
221+ return isIndexWithTypeAndFields (index , IndexType .persistent , fields )
222+ && isEqualOption (index .getUnique (), options .getUnique (), false )
223+ && isEqualOption (index .getSparse (), options .getSparse (), false )
224+ && isEqualOption (index .getDeduplicate (), options .getDeduplicate (), false );
209225 }
210226
211- private static void ensureGeoIndex (final CollectionOperations collection , final GeoIndex annotation ) {
212- collection .ensureGeoIndex (Arrays .asList (annotation .fields ()),
213- new GeoIndexOptions ().geoJson (annotation .geoJson ()));
227+ private static void ensureGeoIndex (final CollectionOperations collection , final GeoIndex annotation , Collection <IndexEntity > existing ) {
228+ GeoIndexOptions options = new GeoIndexOptions ().geoJson (annotation .geoJson ());
229+ Collection <String > fields = Arrays .asList (annotation .fields ());
230+ if (existing .stream ().noneMatch (index -> equalGeoIndex (index , options , fields ))) {
231+ collection .ensureGeoIndex (fields , options );
232+ }
214233 }
215234
216- private static void ensureGeoIndex (final CollectionOperations collection , final ArangoPersistentProperty value ) {
235+ private static void ensureGeoIndex (final CollectionOperations collection , final ArangoPersistentProperty value , Collection < IndexEntity > existing ) {
217236 final GeoIndexOptions options = new GeoIndexOptions ();
218237 value .getGeoIndexed ().ifPresent (i -> options .geoJson (i .geoJson ()));
219- collection .ensureGeoIndex (Collections .singleton (value .getFieldName ()), options );
238+ Collection <String > fields = Collections .singleton (value .getFieldName ());
239+ if (existing .stream ().noneMatch (index -> equalGeoIndex (index , options , fields ))) {
240+ collection .ensureGeoIndex (fields , options );
241+ }
242+ }
243+
244+ private static boolean equalGeoIndex (IndexEntity index , GeoIndexOptions options , Collection <String > fields ) {
245+ return isIndexWithTypeAndFields (index , IndexType .geo , fields )
246+ && isEqualOption (index .getGeoJson (), options .getGeoJson (), false );
220247 }
221248
222249 @ SuppressWarnings ("deprecation" )
223- private static void ensureFulltextIndex (final CollectionOperations collection , final FulltextIndex annotation ) {
224- collection .ensureFulltextIndex (Collections .singleton (annotation .field ()),
225- new FulltextIndexOptions ().minLength (annotation .minLength () > -1 ? annotation .minLength () : null ));
250+ private static void ensureFulltextIndex (final CollectionOperations collection , final FulltextIndex annotation , Collection <IndexEntity > existing ) {
251+ Collection <String > fields = Collections .singleton (annotation .field ());
252+ FulltextIndexOptions options = new FulltextIndexOptions ().minLength (annotation .minLength () > -1 ? annotation .minLength () : null );
253+ if (existing .stream ().noneMatch (index -> equalFulltextIndex (index , options , fields ))) {
254+ collection .ensureFulltextIndex (fields , options );
255+ }
226256 }
227257
228258 @ SuppressWarnings ("deprecation" )
229259 private static void ensureFulltextIndex (final CollectionOperations collection ,
230- final ArangoPersistentProperty value ) {
260+ final ArangoPersistentProperty value , Collection < IndexEntity > existing ) {
231261 final FulltextIndexOptions options = new FulltextIndexOptions ();
232262 value .getFulltextIndexed ().ifPresent (i -> options .minLength (i .minLength () > -1 ? i .minLength () : null ));
233- collection .ensureFulltextIndex (Collections .singleton (value .getFieldName ()), options );
263+ Collection <String > fields = Collections .singleton (value .getFieldName ());
264+ if (existing .stream ().noneMatch (index -> equalFulltextIndex (index , options , fields ))) {
265+ collection .ensureFulltextIndex (fields , options );
266+ }
267+ }
268+
269+ private static boolean equalFulltextIndex (IndexEntity index , FulltextIndexOptions options , Collection <String > fields ) {
270+ return isIndexWithTypeAndFields (index , IndexType .fulltext , fields )
271+ && isEqualOption (index .getMinLength (), options .getMinLength (), 0 );
234272 }
235273
236- private static void ensureTtlIndex (final CollectionOperations collection , final TtlIndex annotation ) {
237- collection .ensureTtlIndex (Collections .singleton (annotation .field ()),
238- new TtlIndexOptions ().expireAfter (annotation .expireAfter ()));
274+ private static void ensureTtlIndex (final CollectionOperations collection , final TtlIndex annotation , Collection <IndexEntity > existing ) {
275+ TtlIndexOptions options = new TtlIndexOptions ().expireAfter (annotation .expireAfter ());
276+ Collection <String > fields = Collections .singleton (annotation .field ());
277+ if (existing .stream ().noneMatch (index -> equalTtlIndex (index , options , fields ))) {
278+ collection .ensureTtlIndex (fields , options );
279+ }
239280 }
240281
241- private static void ensureTtlIndex (final CollectionOperations collection , final ArangoPersistentProperty value ) {
282+ private static void ensureTtlIndex (final CollectionOperations collection , final ArangoPersistentProperty value , Collection < IndexEntity > existing ) {
242283 final TtlIndexOptions options = new TtlIndexOptions ();
243284 value .getTtlIndexed ().ifPresent (i -> options .expireAfter (i .expireAfter ()));
244- collection .ensureTtlIndex (Collections .singleton (value .getFieldName ()), options );
245- }
285+ Collection <String > fields = Collections .singleton (value .getFieldName ());
286+ if (existing .stream ().noneMatch (index -> equalTtlIndex (index , options , fields ))) {
287+ collection .ensureTtlIndex (fields , options );
288+ }
289+ }
246290
247- private static void ensureMDIndex (final CollectionOperations collection , final MDIndex annotation ) {
291+ private static void ensureMDIndex (final CollectionOperations collection , final MDIndex annotation , Collection < IndexEntity > existing ) {
248292 collection .ensureMDIndex (Arrays .asList (annotation .fields ()),
249293 new MDIndexOptions ()
250294 .unique (annotation .unique ())
@@ -253,7 +297,7 @@ private static void ensureMDIndex(final CollectionOperations collection, final M
253297 );
254298 }
255299
256- private static void ensureMDPrefixedIndex (final CollectionOperations collection , final MDPrefixedIndex annotation ) {
300+ private static void ensureMDPrefixedIndex (final CollectionOperations collection , final MDPrefixedIndex annotation , Collection < IndexEntity > existing ) {
257301 collection .ensureMDPrefixedIndex (Arrays .asList (annotation .fields ()),
258302 new MDPrefixedIndexOptions ()
259303 .prefixFields (Arrays .asList (annotation .prefixFields ()))
@@ -263,6 +307,28 @@ private static void ensureMDPrefixedIndex(final CollectionOperations collection,
263307 );
264308 }
265309
310+ private static boolean equalTtlIndex (IndexEntity index , TtlIndexOptions options , Collection <String > fields ) {
311+ return isIndexWithTypeAndFields (index , IndexType .ttl , fields )
312+ && isEqualOption (index .getExpireAfter (), getProtected (options , "getExpireAfter" ), 0 );
313+ }
314+
315+ private static boolean isIndexWithTypeAndFields (IndexEntity index , IndexType type , Collection <String > fields ) {
316+ return index .getType () == type && index .getFields ().size () == fields .size () && index .getFields ().containsAll (fields );
317+ }
318+
319+ private static <T > boolean isEqualOption (T value , @ Nullable T optionalValue , T defaultValue ) {
320+ return value .equals (optionalValue == null ? defaultValue : optionalValue );
321+ }
322+
323+ @ SuppressWarnings ("unchecked" )
324+ @ Deprecated
325+ // can be removed for driver 7 as all option getters are visible
326+ private static <T > T getProtected (Object options , String getterName ) {
327+ Method getter = ReflectionUtils .findMethod (options .getClass (), getterName );
328+ ReflectionUtils .makeAccessible (getter );
329+ return (T ) ReflectionUtils .invokeMethod (getter , options );
330+ }
331+
266332 private Optional <String > determineCollectionFromId (final Object id ) {
267333 return id != null ? Optional .ofNullable (MetadataUtils .determineCollectionFromId (converter .convertId (id )))
268334 : Optional .empty ();
0 commit comments