9
9
import static io .airbyte .integrations .source .mongodb .internal .MongoConstants .ID_FIELD ;
10
10
11
11
import com .fasterxml .jackson .databind .JsonNode ;
12
- import com .google .common .annotations .VisibleForTesting ;
13
12
import com .mongodb .client .MongoClient ;
14
13
import com .mongodb .client .MongoDatabase ;
15
14
import com .mongodb .client .model .Filters ;
16
15
import com .mongodb .client .model .Projections ;
17
16
import com .mongodb .client .model .Sorts ;
18
17
import com .mongodb .connection .ClusterType ;
19
- import io .airbyte .commons .json .Jsons ;
20
18
import io .airbyte .commons .util .AutoCloseableIterator ;
21
19
import io .airbyte .commons .util .AutoCloseableIterators ;
22
20
import io .airbyte .integrations .BaseConnector ;
23
21
import io .airbyte .integrations .base .AirbyteTraceMessageUtility ;
24
22
import io .airbyte .integrations .base .IntegrationRunner ;
25
23
import io .airbyte .integrations .base .Source ;
24
+ import io .airbyte .integrations .source .mongodb .internal .state .MongoDbStateManager ;
25
+ import io .airbyte .integrations .source .mongodb .internal .state .MongoDbStreamState ;
26
26
import io .airbyte .protocol .models .v0 .AirbyteCatalog ;
27
27
import io .airbyte .protocol .models .v0 .AirbyteConnectionStatus ;
28
28
import io .airbyte .protocol .models .v0 .AirbyteMessage ;
29
- import io .airbyte .protocol .models .v0 .AirbyteStateMessage ;
30
- import io .airbyte .protocol .models .v0 .AirbyteStateMessage .AirbyteStateType ;
31
29
import io .airbyte .protocol .models .v0 .AirbyteStream ;
32
30
import io .airbyte .protocol .models .v0 .CatalogHelpers ;
33
31
import io .airbyte .protocol .models .v0 .ConfiguredAirbyteCatalog ;
34
- import io .airbyte .protocol .models .v0 .StreamDescriptor ;
35
32
import io .airbyte .protocol .models .v0 .SyncMode ;
36
33
import java .time .Instant ;
37
- import java .util .Arrays ;
38
34
import java .util .List ;
39
- import java .util .Map ;
40
- import java .util .Map .Entry ;
41
35
import java .util .Optional ;
42
- import java .util .stream .Collectors ;
43
36
import org .bson .BsonDocument ;
44
37
import org .bson .conversions .Bson ;
45
38
import org .bson .types .ObjectId ;
@@ -50,9 +43,6 @@ public class MongoDbSource extends BaseConnector implements Source {
50
43
51
44
private static final Logger LOGGER = LoggerFactory .getLogger (MongoDbSource .class );
52
45
53
- /** Helper class for holding a collection-name and stream state together */
54
- private record CollectionNameState (Optional <String > name , Optional <MongodbStreamState > state ) {}
55
-
56
46
public static void main (final String [] args ) throws Exception {
57
47
final Source source = new MongoDbSource ();
58
48
LOGGER .info ("starting source: {}" , MongoDbSource .class );
@@ -106,15 +96,14 @@ public AutoCloseableIterator<AirbyteMessage> read(final JsonNode config,
106
96
final JsonNode state ) {
107
97
final var databaseName = config .get (DATABASE_CONFIGURATION_KEY ).asText ();
108
98
final var emittedAt = Instant .now ();
109
-
110
- final var states = convertState (state );
99
+ final var stateManager = MongoDbStateManager .createStateManager (state );
111
100
final MongoClient mongoClient = MongoConnectionUtils .createMongoClient (config );
112
101
113
102
try {
114
103
final var database = mongoClient .getDatabase (databaseName );
115
104
// TODO treat INCREMENTAL and FULL_REFRESH differently?
116
105
return AutoCloseableIterators .appendOnClose (AutoCloseableIterators .concatWithEagerClose (
117
- convertCatalogToIterators (catalog , states , database , emittedAt ),
106
+ convertCatalogToIterators (catalog , stateManager , database , emittedAt ),
118
107
AirbyteTraceMessageUtility ::emitStreamStatusTrace ),
119
108
mongoClient ::close );
120
109
} catch (final Exception e ) {
@@ -123,41 +112,12 @@ public AutoCloseableIterator<AirbyteMessage> read(final JsonNode config,
123
112
}
124
113
}
125
114
126
- /**
127
- * Converts the JsonNode into a map of mongodb collection names to stream states.
128
- */
129
- @ VisibleForTesting
130
- protected Map <String , MongodbStreamState > convertState (final JsonNode state ) {
131
- // I'm unsure if the JsonNode data is going to be a singular AirbyteStateMessage or an array of
132
- // AirbyteStateMessages.
133
- // So this currently handles both cases, converting the singular message into a list of messages,
134
- // leaving the list of messages
135
- // as a list of messages, or returning an empty list.
136
- final List <AirbyteStateMessage > states = Jsons .tryObject (state , AirbyteStateMessage .class )
137
- .map (List ::of )
138
- .orElseGet (() -> Jsons .tryObject (state , AirbyteStateMessage [].class )
139
- .map (Arrays ::asList )
140
- .orElse (List .of ()));
141
-
142
- // TODO add namespace support?
143
- return states .stream ()
144
- .filter (s -> s .getType () == AirbyteStateType .STREAM )
145
- .map (s -> new CollectionNameState (
146
- Optional .ofNullable (s .getStream ().getStreamDescriptor ()).map (StreamDescriptor ::getName ),
147
- Jsons .tryObject (s .getStream ().getStreamState (), MongodbStreamState .class )))
148
- // only keep states that could be parsed
149
- .filter (p -> p .name .isPresent () && p .state .isPresent ())
150
- .collect (Collectors .toMap (
151
- p -> p .name .orElseThrow (),
152
- p -> p .state .orElseThrow ()));
153
- }
154
-
155
115
/**
156
116
* Converts the streams in the catalog into a list of AutoCloseableIterators.
157
117
*/
158
118
private List <AutoCloseableIterator <AirbyteMessage >> convertCatalogToIterators (
159
119
final ConfiguredAirbyteCatalog catalog ,
160
- final Map < String , MongodbStreamState > states ,
120
+ final MongoDbStateManager stateManager ,
161
121
final MongoDatabase database ,
162
122
final Instant emittedAt ) {
163
123
return catalog .getStreams ()
@@ -175,12 +135,8 @@ private List<AutoCloseableIterator<AirbyteMessage>> convertCatalogToIterators(
175
135
final var fields = Projections .fields (Projections .include (CatalogHelpers .getTopLevelFieldNames (airbyteStream ).stream ().toList ()));
176
136
177
137
// find the existing state, if there is one, for this steam
178
- final Optional <MongodbStreamState > existingState = states .entrySet ().stream ()
179
- // look only for states that match this stream's name
180
- // TODO add namespace support
181
- .filter (state -> state .getKey ().equals (airbyteStream .getStream ().getName ()))
182
- .map (Entry ::getValue )
183
- .findFirst ();
138
+ final Optional <MongoDbStreamState > existingState =
139
+ stateManager .getStreamState (airbyteStream .getStream ().getName (), airbyteStream .getStream ().getNamespace ());
184
140
185
141
// The filter determines the starting point of this iterator based on the state of this collection.
186
142
// If a state exists, it will use that state to create a query akin to
@@ -198,7 +154,7 @@ private List<AutoCloseableIterator<AirbyteMessage>> convertCatalogToIterators(
198
154
.sort (Sorts .ascending (ID_FIELD ))
199
155
.cursor ();
200
156
201
- final var stateIterator = new MongoDbStateIterator (cursor , airbyteStream , existingState , emittedAt , CHECKPOINT_INTERVAL );
157
+ final var stateIterator = new MongoDbStateIterator (cursor , stateManager , airbyteStream , emittedAt , CHECKPOINT_INTERVAL );
202
158
return AutoCloseableIterators .fromIterator (stateIterator , cursor ::close , null );
203
159
})
204
160
.toList ();
0 commit comments