embulk-output-cassandra 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3a5a33db3984bac71c043a99cf1306c12aed2fa
|
4
|
+
data.tar.gz: 6351267b4e17e6d75617fa4ddda29f4c5b2551e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45f9a322c118f1b4786d730f1db68eefbdccfb1f63fcc9c14967319c9a441bb6b003f0f06a075228c042fdd217f82064f215fb807812cd9e6152c2e76a557a02
|
7
|
+
data.tar.gz: 6a29cfbd03d3ecb2f070563af73265b711c093c0b88ffd68b57d27f98b541964f8002f9f9d59aa3dcba8c33afb8ea680fab6ce77560c44653bd390a87b48cef6
|
data/README.md
CHANGED
@@ -67,7 +67,9 @@ For example, If input data = {id: 1, count: 5}, Executed Statement is `UPDATE ta
|
|
67
67
|
- **cluster_name**: cluster name (string, default: `null`)
|
68
68
|
- **keyspace**: target keyspace name (string, required)
|
69
69
|
- **table**: target table name (string, required)
|
70
|
+
- **mode**: insert or update (string, default: `"insert"`)
|
70
71
|
- **if_not_exists**: Add "IF NOT EXISTS" to INSERT query (boolean, default: `false`)
|
72
|
+
- **if_exists**: Add "IF EXISTS" to UPDATE query (boolean, default: `false`)
|
71
73
|
- **ttl**: Add "TTL" to INSERT query (integer, default: `null`)
|
72
74
|
- **idempotent**: Treat INSERT query as idempotent (boolean, default: `false`)
|
73
75
|
- **connect_timeout**: Set connect timeout millisecond (integer, default: `5000`)
|
data/build.gradle
CHANGED
data/embulk-output-cassandra.iml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<module external.linked.project.id="embulk-output-cassandra" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.
|
2
|
+
<module external.linked.project.id="embulk-output-cassandra" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.2.0" type="JAVA_MODULE" version="4">
|
3
3
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
4
4
|
<exclude-output />
|
5
5
|
<content url="file://$MODULE_DIR$">
|
@@ -13,6 +13,8 @@ import com.datastax.driver.core.querybuilder.BuiltStatement;
|
|
13
13
|
import com.datastax.driver.core.querybuilder.Insert;
|
14
14
|
import com.datastax.driver.core.querybuilder.QueryBuilder;
|
15
15
|
import com.datastax.driver.core.querybuilder.Update;
|
16
|
+
import com.fasterxml.jackson.annotation.JsonCreator;
|
17
|
+
import com.fasterxml.jackson.annotation.JsonValue;
|
16
18
|
import com.google.common.base.Optional;
|
17
19
|
import com.google.common.collect.ImmutableList;
|
18
20
|
import com.google.common.collect.ImmutableMap;
|
@@ -20,6 +22,7 @@ import com.google.common.collect.Lists;
|
|
20
22
|
import org.embulk.config.Config;
|
21
23
|
import org.embulk.config.ConfigDefault;
|
22
24
|
import org.embulk.config.ConfigDiff;
|
25
|
+
import org.embulk.config.ConfigException;
|
23
26
|
import org.embulk.config.ConfigSource;
|
24
27
|
import org.embulk.config.Task;
|
25
28
|
import org.embulk.config.TaskReport;
|
@@ -27,6 +30,7 @@ import org.embulk.config.TaskSource;
|
|
27
30
|
import org.embulk.output.cassandra.setter.CassandraColumnSetter;
|
28
31
|
import org.embulk.output.cassandra.setter.CassandraColumnSetterFactory;
|
29
32
|
import org.embulk.output.cassandra.setter.ColumnSetterVisitor;
|
33
|
+
import org.embulk.spi.Column;
|
30
34
|
import org.embulk.spi.Exec;
|
31
35
|
import org.embulk.spi.OutputPlugin;
|
32
36
|
import org.embulk.spi.Page;
|
@@ -37,7 +41,9 @@ import org.slf4j.Logger;
|
|
37
41
|
|
38
42
|
import java.net.InetSocketAddress;
|
39
43
|
import java.util.List;
|
44
|
+
import java.util.Locale;
|
40
45
|
import java.util.Map;
|
46
|
+
import java.util.stream.Collectors;
|
41
47
|
|
42
48
|
public class CassandraOutputPlugin
|
43
49
|
implements OutputPlugin
|
@@ -46,7 +52,7 @@ public class CassandraOutputPlugin
|
|
46
52
|
extends Task
|
47
53
|
{
|
48
54
|
@Config("hosts")
|
49
|
-
public List<String>
|
55
|
+
public List<String> getHosts();
|
50
56
|
|
51
57
|
@Config("port")
|
52
58
|
@ConfigDefault("9042")
|
@@ -70,10 +76,18 @@ public class CassandraOutputPlugin
|
|
70
76
|
@Config("table")
|
71
77
|
public String getTable();
|
72
78
|
|
79
|
+
@Config("mode")
|
80
|
+
@ConfigDefault("\"insert\"")
|
81
|
+
public Mode getMode();
|
82
|
+
|
73
83
|
@Config("if_not_exists")
|
74
84
|
@ConfigDefault("false")
|
75
85
|
public Boolean getIfNotExists();
|
76
86
|
|
87
|
+
@Config("if_exists")
|
88
|
+
@ConfigDefault("false")
|
89
|
+
public Boolean getIfExists();
|
90
|
+
|
77
91
|
@Config("ttl")
|
78
92
|
@ConfigDefault("null")
|
79
93
|
public Optional<Integer> getTtl();
|
@@ -89,10 +103,6 @@ public class CassandraOutputPlugin
|
|
89
103
|
@Config("request_timeout")
|
90
104
|
@ConfigDefault("12000")
|
91
105
|
public int getRequestTimeout();
|
92
|
-
|
93
|
-
@Config("counter_columnName")
|
94
|
-
@ConfigDefault("null")
|
95
|
-
public Optional<String> getCounterColumnName();
|
96
106
|
}
|
97
107
|
|
98
108
|
private final Logger logger = Exec.getLogger(CassandraOutputPlugin.class);
|
@@ -155,12 +165,36 @@ public class CassandraOutputPlugin
|
|
155
165
|
if (column.getType().getName() == DataType.Name.COUNTER) {
|
156
166
|
update.with(QueryBuilder.incr(column.getName(), QueryBuilder.bindMarker(column.getName())));
|
157
167
|
}
|
158
|
-
else{
|
168
|
+
else {
|
159
169
|
update.where(QueryBuilder.eq(column.getName(), QueryBuilder.bindMarker(column.getName())));
|
160
170
|
}
|
161
171
|
}
|
162
172
|
query = update;
|
163
173
|
}
|
174
|
+
else if (task.getMode() == Mode.UPDATE) {
|
175
|
+
Update update = QueryBuilder.update(task.getKeyspace(), task.getTable());
|
176
|
+
List<String> primaryKeys = tableMetadata.getPrimaryKey().stream().map(ColumnMetadata::getName).collect(Collectors.toList());
|
177
|
+
List<String> columnNames = tableMetadata.getColumns().stream().map(ColumnMetadata::getName).collect(Collectors.toList());
|
178
|
+
|
179
|
+
if (task.getIfExists()) {
|
180
|
+
update.where().ifExists();
|
181
|
+
}
|
182
|
+
if (task.getTtl().isPresent()) {
|
183
|
+
update.using(QueryBuilder.ttl(task.getTtl().get()));
|
184
|
+
}
|
185
|
+
for (String pkey : primaryKeys) {
|
186
|
+
update.where(QueryBuilder.eq(pkey, QueryBuilder.bindMarker(pkey)));
|
187
|
+
}
|
188
|
+
for (Column col : schema.getColumns()) {
|
189
|
+
if (primaryKeys.contains(col.getName())) {
|
190
|
+
continue;
|
191
|
+
}
|
192
|
+
if (columnNames.contains(col.getName())) {
|
193
|
+
update.with(QueryBuilder.set(col.getName(), QueryBuilder.bindMarker(col.getName())));
|
194
|
+
}
|
195
|
+
}
|
196
|
+
query = update;
|
197
|
+
}
|
164
198
|
else {
|
165
199
|
Insert insert = QueryBuilder.insertInto(task.getKeyspace(), task.getTable());
|
166
200
|
if (task.getIfNotExists()) {
|
@@ -190,7 +224,7 @@ public class CassandraOutputPlugin
|
|
190
224
|
List<ColumnSetterVisitor> columnVisitors = Lists.transform(schema.getColumns(), (column) ->
|
191
225
|
new ColumnSetterVisitor(pageReader, columnSetters.get(column.getName())));
|
192
226
|
|
193
|
-
logger.info("
|
227
|
+
logger.info("Query: {}", query.getQueryString());
|
194
228
|
|
195
229
|
PreparedStatement prepared = session.prepare(query);
|
196
230
|
if (task.getIdempotent()) {
|
@@ -203,7 +237,7 @@ public class CassandraOutputPlugin
|
|
203
237
|
private Cluster getCluster(PluginTask task)
|
204
238
|
{
|
205
239
|
Cluster.Builder builder = Cluster.builder();
|
206
|
-
for (String host : task.
|
240
|
+
for (String host : task.getHosts()) {
|
207
241
|
builder.addContactPointsWithPorts(new InetSocketAddress(host, task.getPort()));
|
208
242
|
}
|
209
243
|
|
@@ -311,4 +345,29 @@ public class CassandraOutputPlugin
|
|
311
345
|
return report;
|
312
346
|
}
|
313
347
|
}
|
348
|
+
|
349
|
+
public enum Mode {
|
350
|
+
INSERT,
|
351
|
+
UPDATE;
|
352
|
+
|
353
|
+
@JsonValue
|
354
|
+
@Override
|
355
|
+
public String toString()
|
356
|
+
{
|
357
|
+
return name().toLowerCase(Locale.ENGLISH);
|
358
|
+
}
|
359
|
+
|
360
|
+
@JsonCreator
|
361
|
+
public static Mode fromString(String value)
|
362
|
+
{
|
363
|
+
switch(value) {
|
364
|
+
case "insert":
|
365
|
+
return INSERT;
|
366
|
+
case "update":
|
367
|
+
return UPDATE;
|
368
|
+
default:
|
369
|
+
throw new ConfigException(String.format("Unknown mode '%s'", value));
|
370
|
+
}
|
371
|
+
}
|
372
|
+
}
|
314
373
|
}
|
@@ -3,7 +3,6 @@ package org.embulk.output.cassandra;
|
|
3
3
|
import com.datastax.driver.core.Cluster;
|
4
4
|
import com.datastax.driver.core.DataType;
|
5
5
|
import com.datastax.driver.core.LocalDate;
|
6
|
-
import com.datastax.driver.core.ResultSet;
|
7
6
|
import com.datastax.driver.core.Row;
|
8
7
|
import com.datastax.driver.core.Session;
|
9
8
|
import com.datastax.driver.core.TupleType;
|
@@ -33,7 +32,12 @@ import java.util.List;
|
|
33
32
|
import java.util.Map;
|
34
33
|
import java.util.Set;
|
35
34
|
|
36
|
-
import static org.junit.Assert
|
35
|
+
import static org.junit.Assert.assertArrayEquals;
|
36
|
+
import static org.junit.Assert.assertEquals;
|
37
|
+
import static org.junit.Assert.assertFalse;
|
38
|
+
import static org.junit.Assert.assertNotNull;
|
39
|
+
import static org.junit.Assert.assertNull;
|
40
|
+
import static org.junit.Assert.assertTrue;
|
37
41
|
|
38
42
|
public class TestCassandraOutputPlugin
|
39
43
|
{
|
@@ -249,6 +253,56 @@ public class TestCassandraOutputPlugin
|
|
249
253
|
assertTrue(UUIDs.unixTimestamp(row1.getUUID("timeuuid_item")) < ZonedDateTime.now().toInstant().toEpochMilli());
|
250
254
|
}
|
251
255
|
|
256
|
+
@Test
|
257
|
+
public void testBasicWithUpdateMode() throws IOException
|
258
|
+
{
|
259
|
+
Path input = getInputPath("test1.csv");
|
260
|
+
ConfigSource config = loadYamlResource("test_basic.yaml");
|
261
|
+
config.set("hosts", getCassandraHostAsList());
|
262
|
+
config.set("mode", "update");
|
263
|
+
|
264
|
+
assertEquals(0, session.execute("SELECT * FROM embulk_test.test_basic").all().size());
|
265
|
+
|
266
|
+
embulk.runOutput(config, input);
|
267
|
+
|
268
|
+
Row row1 = session.execute("SELECT * FROM embulk_test.test_basic WHERE id = 'A001'").one();
|
269
|
+
Row row2 = session.execute("SELECT * FROM embulk_test.test_basic WHERE id = 'A002'").one();
|
270
|
+
Row row3 = session.execute("SELECT * FROM embulk_test.test_basic WHERE id = 'A003'").one();
|
271
|
+
assertEquals("A001", row1.getString("id"));
|
272
|
+
assertEquals(9, row1.getLong("int_item"));
|
273
|
+
assertEquals(createDate(2018, 7, 1, 10, 0, 0, 0), row1.getTimestamp("timestamp_item"));
|
274
|
+
assertEquals("A002", row2.getString("id"));
|
275
|
+
assertEquals(0, row2.getLong("int_item"));
|
276
|
+
assertEquals(createDate(2018, 7, 1, 10, 0, 1, 0), row2.getTimestamp("timestamp_item"));
|
277
|
+
assertEquals("A003", row3.getString("id"));
|
278
|
+
assertEquals(9, row3.getLong("int_item"));
|
279
|
+
assertEquals(createDate(2018, 7, 1, 10, 0, 2, 0), row3.getTimestamp("timestamp_item"));
|
280
|
+
}
|
281
|
+
|
282
|
+
@Test
|
283
|
+
public void testBasicWithIfExists() throws IOException
|
284
|
+
{
|
285
|
+
Path input = getInputPath("test1.csv");
|
286
|
+
ConfigSource config = loadYamlResource("test_basic.yaml");
|
287
|
+
config.set("hosts", getCassandraHostAsList());
|
288
|
+
config.set("mode", "update");
|
289
|
+
config.set("if_exists", true);
|
290
|
+
|
291
|
+
session.execute("INSERT INTO embulk_test.test_basic (id) VALUES ('A001')");
|
292
|
+
assertEquals(1, session.execute("SELECT * FROM embulk_test.test_basic").all().size());
|
293
|
+
|
294
|
+
embulk.runOutput(config, input);
|
295
|
+
|
296
|
+
Row row1 = session.execute("SELECT * FROM embulk_test.test_basic WHERE id = 'A001'").one();
|
297
|
+
Row row2 = session.execute("SELECT * FROM embulk_test.test_basic WHERE id = 'A002'").one();
|
298
|
+
Row row3 = session.execute("SELECT * FROM embulk_test.test_basic WHERE id = 'A003'").one();
|
299
|
+
assertEquals("A001", row1.getString("id"));
|
300
|
+
assertEquals(9, row1.getLong("int_item"));
|
301
|
+
assertEquals(createDate(2018, 7, 1, 10, 0, 0, 0), row1.getTimestamp("timestamp_item"));
|
302
|
+
assertNull(row2);
|
303
|
+
assertNull(row3);
|
304
|
+
}
|
305
|
+
|
252
306
|
@Test
|
253
307
|
public void testComplex() throws IOException
|
254
308
|
{
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-output-cassandra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- joker1007
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,7 +55,7 @@ files:
|
|
55
55
|
- classpath/asm-tree-5.0.3.jar
|
56
56
|
- classpath/asm-util-5.0.3.jar
|
57
57
|
- classpath/cassandra-driver-core-3.5.0.jar
|
58
|
-
- classpath/embulk-output-cassandra-0.
|
58
|
+
- classpath/embulk-output-cassandra-0.3.0.jar
|
59
59
|
- classpath/guava-19.0.jar
|
60
60
|
- classpath/jffi-1.2.16-native.jar
|
61
61
|
- classpath/jffi-1.2.16.jar
|