22
33import java .util .List ;
44import java .util .UUID ;
5- import java .util .function .Function ;
65import java .util .stream .Collectors ;
76import java .util .stream .Stream ;
87
98import de .bwaldvogel .mongo .MongoBackend ;
109import de .bwaldvogel .mongo .MongoCollection ;
11- import de .bwaldvogel .mongo .backend .Cursor ;
1210import de .bwaldvogel .mongo .backend .CursorRegistry ;
11+ import de .bwaldvogel .mongo .backend .TailableCursor ;
1312import de .bwaldvogel .mongo .backend .Utils ;
1413import de .bwaldvogel .mongo .backend .aggregation .Aggregation ;
1514import de .bwaldvogel .mongo .bson .BsonTimestamp ;
1615import de .bwaldvogel .mongo .bson .Document ;
17- import de .bwaldvogel .mongo .exception .MongoServerException ;
1816
1917public class CollectionBackedOplog implements Oplog {
2018
@@ -78,23 +76,13 @@ public void handleDropCollection(String namespace) {
7876 collection .addDocument (toOplogDropCollection (databaseName , collectionName ));
7977 }
8078
81- private Stream <Document > streamOplog (Document changeStreamDocument , OplogPosition position , Aggregation aggregation ) {
82- return aggregation .runStagesAsStream (collection .queryAllAsStream ()
83- .filter (document -> {
84- BsonTimestamp timestamp = getOplogTimestamp (document );
85- OplogPosition documentOplogPosition = new OplogPosition (timestamp );
86- return documentOplogPosition .isAfter (position );
87- })
88- .sorted ((o1 , o2 ) -> {
89- BsonTimestamp timestamp1 = getOplogTimestamp (o1 );
90- BsonTimestamp timestamp2 = getOplogTimestamp (o2 );
91- return timestamp1 .compareTo (timestamp2 );
92- })
93- .map (document -> toChangeStreamResponseDocument (document , changeStreamDocument )));
79+ @ Override
80+ public OplogCursor createCursor (String namespace , OplogPosition initialOplogPosition ) {
81+ return new OplogCursor (cursorRegistry .generateCursorId (), this ::streamOplog , initialOplogPosition );
9482 }
9583
9684 @ Override
97- public Cursor createCursor (Document changeStreamDocument , String namespace , Aggregation aggregation ) {
85+ public TailableCursor createChangeStreamCursor (Document changeStreamDocument , String namespace , Aggregation aggregation ) {
9886 Document startAfter = (Document ) changeStreamDocument .get ("startAfter" );
9987 Document resumeAfter = (Document ) changeStreamDocument .get ("resumeAfter" );
10088 BsonTimestamp startAtOperationTime = (BsonTimestamp ) changeStreamDocument .get (START_AT_OPERATION_TIME );
@@ -107,7 +95,7 @@ public Cursor createCursor(Document changeStreamDocument, String namespace, Aggr
10795 String collectionName = Utils .getCollectionNameFromFullName (namespace );
10896 boolean resumeAfterTerminalEvent = collection .queryAllAsStream ()
10997 .filter (document -> {
110- BsonTimestamp timestamp = getOplogTimestamp (document );
98+ BsonTimestamp timestamp = OplogUtils . getOplogTimestamp (document );
11199 OplogPosition documentOplogPosition = new OplogPosition (timestamp );
112100 return initialOplogPosition .isAfter (documentOplogPosition .inclusive ());
113101 })
@@ -125,12 +113,27 @@ public Cursor createCursor(Document changeStreamDocument, String namespace, Aggr
125113 initialOplogPosition = new OplogPosition (oplogClock .now ());
126114 }
127115
128- Function <OplogPosition , Stream <Document >> streamSupplier = position -> streamOplog (changeStreamDocument , position , aggregation );
129- OplogCursor cursor = new OplogCursor (cursorRegistry .generateCursorId (), streamSupplier , initialOplogPosition );
116+ OplogCursor oplogCursor = createCursor (namespace , initialOplogPosition );
117+ ChangeStreamCursor cursor
118+ = new ChangeStreamCursor (backend , changeStreamDocument , aggregation , oplogCursor );
130119 cursorRegistry .add (cursor );
131120 return cursor ;
132121 }
133122
123+ private Stream <Document > streamOplog (OplogPosition position ) {
124+ return collection .queryAllAsStream ()
125+ .filter (document -> {
126+ BsonTimestamp timestamp = OplogUtils .getOplogTimestamp (document );
127+ OplogPosition documentOplogPosition = new OplogPosition (timestamp );
128+ return documentOplogPosition .isAfter (position );
129+ })
130+ .sorted ((o1 , o2 ) -> {
131+ BsonTimestamp timestamp1 = OplogUtils .getOplogTimestamp (o1 );
132+ BsonTimestamp timestamp2 = OplogUtils .getOplogTimestamp (o2 );
133+ return timestamp1 .compareTo (timestamp2 );
134+ });
135+ }
136+
134137 private Document toOplogDocument (OperationType operationType , String namespace ) {
135138 return new Document ()
136139 .append (OplogDocumentFields .TIMESTAMP , oplogClock .incrementAndGet ())
@@ -168,91 +171,4 @@ private boolean isOplogCollection(String namespace) {
168171 return collection .getFullName ().equals (namespace );
169172 }
170173
171- private Document getFullDocument (Document changeStreamDocument , Document document , OperationType operationType ) {
172- switch (operationType ) {
173- case INSERT :
174- return getUpdateDocument (document );
175- case DELETE :
176- return null ;
177- case UPDATE :
178- return lookUpUpdateDocument (changeStreamDocument , document );
179- }
180- throw new IllegalArgumentException ("Invalid operation type" );
181- }
182-
183- private Document lookUpUpdateDocument (Document changeStreamDocument , Document document ) {
184- Document deltaUpdate = getDeltaUpdate (getUpdateDocument (document ));
185- if (changeStreamDocument .containsKey (FULL_DOCUMENT ) && changeStreamDocument .get (FULL_DOCUMENT ).equals ("updateLookup" )) {
186- String namespace = (String ) document .get (OplogDocumentFields .NAMESPACE );
187- String databaseName = namespace .split ("\\ ." )[0 ];
188- String collectionName = namespace .split ("\\ ." )[1 ];
189- return backend .resolveDatabase (databaseName )
190- .resolveCollection (collectionName , true )
191- .queryAllAsStream ()
192- .filter (d -> d .get (OplogDocumentFields .ID ).equals (((Document ) document .get (OplogDocumentFields .O2 )).get (OplogDocumentFields .ID )))
193- .findFirst ()
194- .orElse (deltaUpdate );
195- }
196- return deltaUpdate ;
197- }
198-
199- private Document getDeltaUpdate (Document updateDocument ) {
200- Document delta = new Document ();
201- if (updateDocument .containsKey ("$set" )) {
202- delta .appendAll ((Document ) updateDocument .get ("$set" ));
203- }
204- if (updateDocument .containsKey ("$unset" )) {
205- delta .appendAll ((Document ) updateDocument .get ("$unset" ));
206- }
207- return delta ;
208- }
209-
210- private Document toChangeStreamResponseDocument (Document oplogDocument , Document changeStreamDocument ) {
211- OperationType operationType = OperationType .fromCode (oplogDocument .get ("op" ).toString ());
212- Document documentKey = new Document ();
213- Document document = getUpdateDocument (oplogDocument );
214- BsonTimestamp timestamp = getOplogTimestamp (oplogDocument );
215- OplogPosition oplogPosition = new OplogPosition (timestamp );
216- switch (operationType ) {
217- case UPDATE :
218- case DELETE :
219- documentKey = document ;
220- break ;
221- case INSERT :
222- documentKey .append (OplogDocumentFields .ID , document .get (OplogDocumentFields .ID ));
223- break ;
224- case COMMAND :
225- return toChangeStreamCommandResponseDocument (oplogDocument , oplogPosition , timestamp );
226- default :
227- throw new IllegalArgumentException ("Unexpected operation type: " + operationType );
228- }
229-
230- return new Document ()
231- .append (OplogDocumentFields .ID , new Document (OplogDocumentFields .ID_DATA_KEY , oplogPosition .toHexString ()))
232- .append ("operationType" , operationType .getDescription ())
233- .append (FULL_DOCUMENT , getFullDocument (changeStreamDocument , oplogDocument , operationType ))
234- .append ("documentKey" , documentKey )
235- .append ("clusterTime" , timestamp );
236- }
237-
238- private Document toChangeStreamCommandResponseDocument (Document oplogDocument , OplogPosition oplogPosition , BsonTimestamp timestamp ) {
239- Document document = getUpdateDocument (oplogDocument );
240- String operationType = document .keySet ().stream ().findFirst ().orElseThrow (
241- () -> new MongoServerException ("Unspecified command operation type" )
242- );
243-
244- return new Document ()
245- .append (OplogDocumentFields .ID , new Document (OplogDocumentFields .ID_DATA_KEY , oplogPosition .toHexString ()))
246- .append ("operationType" , operationType )
247- .append ("clusterTime" , timestamp );
248- }
249-
250- private static BsonTimestamp getOplogTimestamp (Document document ) {
251- return (BsonTimestamp ) document .get (OplogDocumentFields .TIMESTAMP );
252- }
253-
254- private static Document getUpdateDocument (Document document ) {
255- return (Document ) document .get (OplogDocumentFields .O );
256- }
257-
258174}
0 commit comments