11package de .bwaldvogel .mongo .oplog ;
22
33import java .util .List ;
4+ import java .util .Objects ;
45import java .util .UUID ;
56import java .util .function .Function ;
7+ import java .util .stream .Collectors ;
68import java .util .stream .Stream ;
79
810import de .bwaldvogel .mongo .MongoBackend ;
911import de .bwaldvogel .mongo .MongoCollection ;
10- import de .bwaldvogel .mongo .backend .Cursor ;
1112import de .bwaldvogel .mongo .backend .CursorRegistry ;
13+ import de .bwaldvogel .mongo .backend .TailableCursor ;
1214import de .bwaldvogel .mongo .backend .Utils ;
1315import de .bwaldvogel .mongo .backend .aggregation .Aggregation ;
1416import de .bwaldvogel .mongo .bson .BsonTimestamp ;
1517import de .bwaldvogel .mongo .bson .Document ;
16- import de .bwaldvogel .mongo .exception .MongoServerException ;
1718
1819public class CollectionBackedOplog implements Oplog {
1920
2021 private static final long ELECTION_TERM = 1L ;
2122 private static final String START_AT_OPERATION_TIME = "startAtOperationTime" ;
22- private static final String FULL_DOCUMENT = "fullDocument" ;
2323 private static final String START_AFTER = "startAfter" ;
2424 private static final String RESUME_AFTER = "resumeAfter" ;
25- private static final String OPERATION_TYPE = "operationType" ;
26- private static final String CLUSTER_TIME = "clusterTime" ;
27- private static final String DOCUMENT_KEY = "documentKey" ;
2825
2926 private final OplogClock oplogClock ;
3027 private final MongoCollection <Document > collection ;
@@ -83,21 +80,19 @@ public void handleDropCollection(String namespace) {
8380 collection .addDocument (toOplogDropCollection (databaseName , collectionName ));
8481 }
8582
86- private Stream <Document > streamOplog (Document changeStreamDocument , OplogPosition position , Aggregation aggregation ,
87- String namespace ) {
88- return aggregation .runStagesAsStream (collection .queryAllAsStream ()
83+ private Stream <Document > streamOplog (OplogPosition position , String namespace ) {
84+ return collection .queryAllAsStream ()
8985 .filter (document -> filterNamespace (document , namespace ))
9086 .filter (document -> {
91- BsonTimestamp timestamp = getOplogTimestamp (document );
87+ BsonTimestamp timestamp = OplogUtils . getOplogTimestamp (document );
9288 OplogPosition documentOplogPosition = new OplogPosition (timestamp );
9389 return documentOplogPosition .isAfter (position );
9490 })
9591 .sorted ((o1 , o2 ) -> {
96- BsonTimestamp timestamp1 = getOplogTimestamp (o1 );
97- BsonTimestamp timestamp2 = getOplogTimestamp (o2 );
92+ BsonTimestamp timestamp1 = OplogUtils . getOplogTimestamp (o1 );
93+ BsonTimestamp timestamp2 = OplogUtils . getOplogTimestamp (o2 );
9894 return timestamp1 .compareTo (timestamp2 );
99- })
100- .map (document -> toChangeStreamResponseDocument (document , changeStreamDocument )));
95+ });
10196 }
10297
10398 private static boolean filterNamespace (Document document , String namespace ) {
@@ -110,7 +105,16 @@ private static boolean filterNamespace(Document document, String namespace) {
110105 }
111106
112107 @ Override
113- public Cursor createCursor (Document changeStreamDocument , String namespace , Aggregation aggregation ) {
108+ public OplogCursor createCursor (String namespace , OplogPosition initialOplogPosition ) {
109+ return new OplogCursor (
110+ cursorRegistry .generateCursorId (),
111+ position -> streamOplog (position , namespace ),
112+ initialOplogPosition
113+ );
114+ }
115+
116+ @ Override
117+ public TailableCursor createChangeStreamCursor (Document changeStreamDocument , String namespace , Aggregation aggregation ) {
114118 Document startAfter = (Document ) changeStreamDocument .get (START_AFTER );
115119 Document resumeAfter = (Document ) changeStreamDocument .get (RESUME_AFTER );
116120 BsonTimestamp startAtOperationTime = (BsonTimestamp ) changeStreamDocument .get (START_AT_OPERATION_TIME );
@@ -123,7 +127,7 @@ public Cursor createCursor(Document changeStreamDocument, String namespace, Aggr
123127 String collectionName = Utils .getCollectionNameFromFullName (namespace );
124128 boolean resumeAfterTerminalEvent = collection .queryAllAsStream ()
125129 .filter (document -> {
126- BsonTimestamp timestamp = getOplogTimestamp (document );
130+ BsonTimestamp timestamp = OplogUtils . getOplogTimestamp (document );
127131 OplogPosition documentOplogPosition = new OplogPosition (timestamp );
128132 return initialOplogPosition .isAfter (documentOplogPosition .inclusive ());
129133 })
@@ -141,9 +145,9 @@ public Cursor createCursor(Document changeStreamDocument, String namespace, Aggr
141145 initialOplogPosition = new OplogPosition (oplogClock .now ());
142146 }
143147
144- Function < OplogPosition , Stream < Document >> streamSupplier =
145- position -> streamOplog ( changeStreamDocument , position , aggregation , namespace );
146- OplogCursor cursor = new OplogCursor ( cursorRegistry . generateCursorId (), streamSupplier , initialOplogPosition );
148+ OplogCursor oplogCursor = createCursor ( namespace , initialOplogPosition );
149+ ChangeStreamCursor cursor
150+ = new ChangeStreamCursor ( backend , changeStreamDocument , aggregation , oplogCursor );
147151 cursorRegistry .add (cursor );
148152 return cursor ;
149153 }
@@ -185,91 +189,4 @@ private boolean isOplogCollection(String namespace) {
185189 return collection .getFullName ().equals (namespace );
186190 }
187191
188- private Document getFullDocument (Document changeStreamDocument , Document document , OperationType operationType ) {
189- switch (operationType ) {
190- case INSERT :
191- return getUpdateDocument (document );
192- case DELETE :
193- return null ;
194- case UPDATE :
195- return lookUpUpdateDocument (changeStreamDocument , document );
196- }
197- throw new IllegalArgumentException ("Invalid operation type" );
198- }
199-
200- private Document lookUpUpdateDocument (Document changeStreamDocument , Document document ) {
201- Document deltaUpdate = getDeltaUpdate (getUpdateDocument (document ));
202- if (changeStreamDocument .containsKey (FULL_DOCUMENT ) && changeStreamDocument .get (FULL_DOCUMENT ).equals ("updateLookup" )) {
203- String namespace = (String ) document .get (OplogDocumentFields .NAMESPACE );
204- String databaseName = namespace .split ("\\ ." )[0 ];
205- String collectionName = namespace .split ("\\ ." )[1 ];
206- return backend .resolveDatabase (databaseName )
207- .resolveCollection (collectionName , true )
208- .queryAllAsStream ()
209- .filter (d -> d .get (OplogDocumentFields .ID ).equals (((Document ) document .get (OplogDocumentFields .O2 )).get (OplogDocumentFields .ID )))
210- .findFirst ()
211- .orElse (deltaUpdate );
212- }
213- return deltaUpdate ;
214- }
215-
216- private Document getDeltaUpdate (Document updateDocument ) {
217- Document delta = new Document ();
218- if (updateDocument .containsKey ("$set" )) {
219- delta .appendAll ((Document ) updateDocument .get ("$set" ));
220- }
221- if (updateDocument .containsKey ("$unset" )) {
222- delta .appendAll ((Document ) updateDocument .get ("$unset" ));
223- }
224- return delta ;
225- }
226-
227- private Document toChangeStreamResponseDocument (Document oplogDocument , Document changeStreamDocument ) {
228- OperationType operationType = OperationType .fromCode (oplogDocument .get (OplogDocumentFields .OPERATION_TYPE ).toString ());
229- Document documentKey = new Document ();
230- Document document = getUpdateDocument (oplogDocument );
231- BsonTimestamp timestamp = getOplogTimestamp (oplogDocument );
232- OplogPosition oplogPosition = new OplogPosition (timestamp );
233- switch (operationType ) {
234- case UPDATE :
235- case DELETE :
236- documentKey = document ;
237- break ;
238- case INSERT :
239- documentKey .append (OplogDocumentFields .ID , document .get (OplogDocumentFields .ID ));
240- break ;
241- case COMMAND :
242- return toChangeStreamCommandResponseDocument (oplogDocument , oplogPosition , timestamp );
243- default :
244- throw new IllegalArgumentException ("Unexpected operation type: " + operationType );
245- }
246-
247- return new Document ()
248- .append (OplogDocumentFields .ID , new Document (OplogDocumentFields .ID_DATA_KEY , oplogPosition .toHexString ()))
249- .append (OPERATION_TYPE , operationType .getDescription ())
250- .append (FULL_DOCUMENT , getFullDocument (changeStreamDocument , oplogDocument , operationType ))
251- .append (DOCUMENT_KEY , documentKey )
252- .append (CLUSTER_TIME , timestamp );
253- }
254-
255- private Document toChangeStreamCommandResponseDocument (Document oplogDocument , OplogPosition oplogPosition , BsonTimestamp timestamp ) {
256- Document document = getUpdateDocument (oplogDocument );
257- String operationType = document .keySet ().stream ().findFirst ().orElseThrow (
258- () -> new MongoServerException ("Unspecified command operation type" )
259- );
260-
261- return new Document ()
262- .append (OplogDocumentFields .ID , new Document (OplogDocumentFields .ID_DATA_KEY , oplogPosition .toHexString ()))
263- .append (OPERATION_TYPE , operationType )
264- .append (CLUSTER_TIME , timestamp );
265- }
266-
267- private static BsonTimestamp getOplogTimestamp (Document document ) {
268- return (BsonTimestamp ) document .get (OplogDocumentFields .TIMESTAMP );
269- }
270-
271- private static Document getUpdateDocument (Document document ) {
272- return (Document ) document .get (OplogDocumentFields .O );
273- }
274-
275192}
0 commit comments