|
20 | 20 | import lombok.AccessLevel;
|
21 | 21 | import lombok.NonNull;
|
22 | 22 | import lombok.RequiredArgsConstructor;
|
| 23 | +import reactor.core.CoreSubscriber; |
23 | 24 | import reactor.core.publisher.Flux;
|
24 | 25 | import reactor.core.publisher.Mono;
|
| 26 | +import reactor.util.context.Context; |
25 | 27 | import reactor.util.function.Tuple2;
|
26 | 28 | import reactor.util.function.Tuples;
|
27 | 29 |
|
| 30 | +import java.lang.reflect.Field; |
28 | 31 | import java.util.*;
|
29 | 32 | import java.util.concurrent.TimeUnit;
|
30 | 33 | import java.util.function.Consumer;
|
|
70 | 73 | import org.springframework.data.mongodb.core.aggregation.PrefixingDelegatingAggregationOperationContext;
|
71 | 74 | import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
|
72 | 75 | import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
73 |
| -import org.springframework.data.mongodb.core.convert.*; |
| 76 | +import org.springframework.data.mongodb.core.convert.DbRefResolver; |
| 77 | +import org.springframework.data.mongodb.core.convert.JsonSchemaMapper; |
| 78 | +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; |
| 79 | +import org.springframework.data.mongodb.core.convert.MongoConverter; |
| 80 | +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; |
| 81 | +import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper; |
| 82 | +import org.springframework.data.mongodb.core.convert.MongoWriter; |
| 83 | +import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; |
| 84 | +import org.springframework.data.mongodb.core.convert.QueryMapper; |
| 85 | +import org.springframework.data.mongodb.core.convert.UpdateMapper; |
74 | 86 | import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
|
75 | 87 | import org.springframework.data.mongodb.core.index.ReactiveIndexOperations;
|
76 | 88 | import org.springframework.data.mongodb.core.index.ReactiveMongoPersistentEntityIndexCreator;
|
|
102 | 114 | import org.springframework.util.ClassUtils;
|
103 | 115 | import org.springframework.util.CollectionUtils;
|
104 | 116 | import org.springframework.util.ObjectUtils;
|
| 117 | +import org.springframework.util.ReflectionUtils; |
105 | 118 | import org.springframework.util.ResourceUtils;
|
106 | 119 | import org.springframework.util.StringUtils;
|
107 | 120 |
|
|
113 | 126 | import com.mongodb.MongoException;
|
114 | 127 | import com.mongodb.ReadPreference;
|
115 | 128 | import com.mongodb.WriteConcern;
|
116 |
| -import com.mongodb.client.model.*; |
| 129 | +import com.mongodb.client.model.CountOptions; |
| 130 | +import com.mongodb.client.model.CreateCollectionOptions; |
| 131 | +import com.mongodb.client.model.DeleteOptions; |
| 132 | +import com.mongodb.client.model.FindOneAndDeleteOptions; |
| 133 | +import com.mongodb.client.model.FindOneAndReplaceOptions; |
| 134 | +import com.mongodb.client.model.FindOneAndUpdateOptions; |
| 135 | +import com.mongodb.client.model.ReplaceOptions; |
| 136 | +import com.mongodb.client.model.ReturnDocument; |
| 137 | +import com.mongodb.client.model.UpdateOptions; |
| 138 | +import com.mongodb.client.model.ValidationOptions; |
117 | 139 | import com.mongodb.client.model.changestream.FullDocument;
|
118 | 140 | import com.mongodb.client.result.DeleteResult;
|
119 | 141 | import com.mongodb.client.result.UpdateResult;
|
120 |
| -import com.mongodb.reactivestreams.client.*; |
| 142 | +import com.mongodb.reactivestreams.client.AggregatePublisher; |
| 143 | +import com.mongodb.reactivestreams.client.ChangeStreamPublisher; |
| 144 | +import com.mongodb.reactivestreams.client.ClientSession; |
| 145 | +import com.mongodb.reactivestreams.client.DistinctPublisher; |
| 146 | +import com.mongodb.reactivestreams.client.FindPublisher; |
| 147 | +import com.mongodb.reactivestreams.client.MapReducePublisher; |
| 148 | +import com.mongodb.reactivestreams.client.MongoClient; |
| 149 | +import com.mongodb.reactivestreams.client.MongoCollection; |
| 150 | +import com.mongodb.reactivestreams.client.MongoDatabase; |
| 151 | +import com.mongodb.reactivestreams.client.Success; |
121 | 152 |
|
122 | 153 | /**
|
123 | 154 | * Primary implementation of {@link ReactiveMongoOperations}. It simplifies the use of Reactive MongoDB usage and helps
|
@@ -581,9 +612,98 @@ public <T> Flux<T> createFlux(String collectionName, ReactiveCollectionCallback<
|
581 | 612 | Mono<MongoCollection<Document>> collectionPublisher = Mono
|
582 | 613 | .fromCallable(() -> getAndPrepareCollection(doGetDatabase(), collectionName));
|
583 | 614 |
|
584 |
| - return collectionPublisher.flatMapMany(callback::doInCollection).onErrorMap(translateException()); |
| 615 | + Flux<T> source = collectionPublisher.flatMapMany(callback::doInCollection).onErrorMap(translateException()); |
| 616 | + |
| 617 | + |
| 618 | + return new Flux<T>() { |
| 619 | + |
| 620 | + @Override |
| 621 | + public void subscribe(CoreSubscriber actual) { |
| 622 | + |
| 623 | + Long skip = extractSkip(actual); |
| 624 | + Long take = extractLimit(actual); |
| 625 | + |
| 626 | + System.out.println(String.format("Setting offset %s and limit: %s", skip, take)); |
| 627 | + |
| 628 | + Context context = Context.empty(); |
| 629 | + |
| 630 | + // and here we use the original Flux and evaluate skip / take in the template |
| 631 | + if (skip != null && skip > 0L) { |
| 632 | + context = context.put("skip", skip); |
| 633 | + } |
| 634 | + if (take != null && take > 0L) { |
| 635 | + context = context.put("take", take); |
| 636 | + } |
| 637 | + |
| 638 | + |
| 639 | + source.subscriberContext(context).subscribe(actual); |
| 640 | + } |
| 641 | + }; |
| 642 | + } |
| 643 | + |
| 644 | + // --> HACKING |
| 645 | + |
| 646 | + @Nullable |
| 647 | + static Long extractSkip(Subscriber subscriber) { |
| 648 | + |
| 649 | + if (subscriber == null || !ClassUtils.getShortName(subscriber.getClass()).endsWith("SkipSubscriber")) { |
| 650 | + return null; |
| 651 | + } |
| 652 | + |
| 653 | + java.lang.reflect.Field field = ReflectionUtils.findField(subscriber.getClass(), "remaining"); |
| 654 | + if (field == null) { |
| 655 | + return null; |
| 656 | + } |
| 657 | + |
| 658 | + ReflectionUtils.makeAccessible(field); |
| 659 | + Long skip = (Long) ReflectionUtils.getField(field, subscriber); |
| 660 | + if (skip != null && skip > 0L) { |
| 661 | + |
| 662 | + // reset the field, otherwise we'd skip stuff in the code. |
| 663 | + ReflectionUtils.setField(field, subscriber, 0L); |
| 664 | + } |
| 665 | + |
| 666 | + return skip; |
| 667 | + } |
| 668 | + |
| 669 | + @Nullable |
| 670 | + static Long extractLimit(Subscriber subscriber) { |
| 671 | + |
| 672 | + if (subscriber == null) { |
| 673 | + return null; |
| 674 | + } |
| 675 | + |
| 676 | + if (!ClassUtils.getShortName(subscriber.getClass()).endsWith("TakeSubscriber")) { |
| 677 | + return extractLimit(extractPotentialTakeSubscriber(subscriber)); |
| 678 | + } |
| 679 | + |
| 680 | + java.lang.reflect.Field field = ReflectionUtils.findField(subscriber.getClass(), "n"); |
| 681 | + if (field == null) { |
| 682 | + return null; |
| 683 | + } |
| 684 | + |
| 685 | + ReflectionUtils.makeAccessible(field); |
| 686 | + return (Long) ReflectionUtils.getField(field, subscriber); |
| 687 | + } |
| 688 | + |
| 689 | + @Nullable |
| 690 | + static Subscriber extractPotentialTakeSubscriber(Subscriber subscriber) { |
| 691 | + |
| 692 | + if (!ClassUtils.getShortName(subscriber.getClass()).endsWith("SkipSubscriber")) { |
| 693 | + return null; |
| 694 | + } |
| 695 | + |
| 696 | + Field field = ReflectionUtils.findField(subscriber.getClass(), "actual"); |
| 697 | + if (field == null) { |
| 698 | + return null; |
| 699 | + } |
| 700 | + |
| 701 | + ReflectionUtils.makeAccessible(field); |
| 702 | + return (Subscriber) ReflectionUtils.getField(field, subscriber); |
585 | 703 | }
|
586 | 704 |
|
| 705 | + // <--- HACKING |
| 706 | + |
587 | 707 | /**
|
588 | 708 | * Create a reusable {@link Mono} for the {@code collectionName} and {@link ReactiveCollectionCallback}.
|
589 | 709 | *
|
@@ -2539,12 +2659,33 @@ private <T> Flux<T> executeFindMultiInternal(ReactiveCollectionQueryCallback<Doc
|
2539 | 2659 |
|
2540 | 2660 | return createFlux(collectionName, collection -> {
|
2541 | 2661 |
|
2542 |
| - FindPublisher<Document> findPublisher = collectionCallback.doInCollection(collection); |
| 2662 | + return Mono.subscriberContext().flatMapMany(context -> { |
| 2663 | + |
| 2664 | + FindPublisher<Document> findPublisher = collectionCallback.doInCollection(collection); |
| 2665 | + |
| 2666 | + if (preparer != null) { |
| 2667 | + findPublisher = preparer.prepare(findPublisher); |
| 2668 | + } |
| 2669 | + |
| 2670 | + Long skip = context.getOrDefault("skip", null); |
| 2671 | + Long take = context.getOrDefault("take", null); |
| 2672 | + |
| 2673 | + System.out.println(String.format("Using offset: %s and limit: %s", skip, take)); |
| 2674 | + |
| 2675 | + if(skip != null && skip > 0L) { |
| 2676 | + findPublisher = findPublisher.skip(skip.intValue()); |
| 2677 | + } |
| 2678 | + |
| 2679 | + if(take != null && take > 0L) { |
| 2680 | + findPublisher = findPublisher.limit(take.intValue()); |
| 2681 | + } |
| 2682 | + |
| 2683 | + return Flux.from(findPublisher).doOnNext(System.out::println).map(objectCallback::doWith); |
| 2684 | + |
| 2685 | + }); |
| 2686 | + |
| 2687 | + |
2543 | 2688 |
|
2544 |
| - if (preparer != null) { |
2545 |
| - findPublisher = preparer.prepare(findPublisher); |
2546 |
| - } |
2547 |
| - return Flux.from(findPublisher).map(objectCallback::doWith); |
2548 | 2689 | });
|
2549 | 2690 | }
|
2550 | 2691 |
|
|
0 commit comments