Skip to content

Commit a929686

Browse files
author
vroyer
committed
Add support for Cassandra decimal as Elasticsearch #139
1 parent a28a6a7 commit a929686

File tree

7 files changed

+28
-15
lines changed

7 files changed

+28
-15
lines changed

docs/elassandra/source/mapping.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Below is the mapping from Elasticsearch field basic types to CQL3 types :
3030
+--------------------+--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
3131
| long | bigint | |
3232
+--------------------+--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
33+
| keyword | decimal | Existing Cassandra *decimal* columns are mapped to an Elasticsearch keyword. |
34+
+--------------------+--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
3335
| long | time | Existing Cassandra *time* columns (64-bit signed integer representing |
3436
| | | the number of nanoseconds since midnight) stored as long in Elasticsearch. |
3537
+--------------------+--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

server/src/main/java/org/elassandra/cluster/ColumnDescriptor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public void validate(KeyspaceMetadata ksm , CFMetaData cfm) throws Configuration
7575
if (!existingCql3.equals(inferedCql) &&
7676
!(existingCql3.endsWith("uuid") && inferedCql.equals("text")) && // #74 uuid is mapped as keyword
7777
!(existingCql3.equals("timeuuid") && (inferedCql.equals("timestamp") || inferedCql.equals("text"))) &&
78+
!(existingCql3.equals("decimal") && inferedCql.equals("text")) &&
7879
!(existingCql3.equals("date") && inferedCql.equals("timestamp")) &&
7980
!(existingCql3.equals("time") && inferedCql.equals("bigint"))
8081
) // timeuuid can be mapped to date

server/src/main/java/org/elassandra/cluster/QueryManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ public Object[] rowAsArray(final IndexService indexService, final String type, U
561561
case FLOAT:
562562
values[i] = value(fieldMapper, row.getFloat(columnName), valueForSearch);
563563
break;
564+
case DECIMAL:
565+
values[i] = value(fieldMapper, row.getDecimal(columnName), valueForSearch);
566+
break;
564567
case BLOB:
565568
values[i] = value(fieldMapper,
566569
row.getBlob(columnName),

server/src/main/java/org/elassandra/cluster/SchemaManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public class SchemaManager extends AbstractComponent {
176176
.put("inet", "ip" )
177177
.put("uuid", "keyword" )
178178
.put("timeuuid", "keyword" )
179+
.put("decimal", "keyword" )
179180
.build();
180181

181182
public SchemaManager(Settings settings, ClusterService clusterService) {

server/src/main/java/org/elassandra/index/ElasticSecondaryIndex.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,7 @@ private Query buildQuery(ColumnDefinition cd, FieldMapper mapper, Object start,
10041004
case ASCII:
10051005
case TEXT:
10061006
case VARCHAR:
1007+
case DECIMAL:
10071008
query = start != null && end != null && ((Comparable) start).compareTo(end) == 0 ?
10081009
new TermQuery(new Term(mapper.name(), BytesRefs.toBytesRef(start))) :
10091010
new TermRangeQuery(mapper.name(), BytesRefs.toBytesRef(start), BytesRefs.toBytesRef(end), includeLower, includeUpper);
@@ -1119,8 +1120,6 @@ private Query buildQuery(ColumnDefinition cd, FieldMapper mapper, Object start,
11191120
case BOOLEAN:
11201121
query = ((BooleanFieldMapper) mapper).fieldType().rangeQuery(start, end, includeLower, includeUpper, null);
11211122
break;
1122-
case DECIMAL:
1123-
throw new UnsupportedOperationException("Unsupported type [decimal] in primary key");
11241123
case BLOB:
11251124
throw new UnsupportedOperationException("Unsupported type [blob] in primary key");
11261125
}

server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
package org.elasticsearch.index.mapper;
2121

22-
import org.apache.cassandra.cql3.CQL3Type;
2322
import org.apache.cassandra.db.marshal.AbstractType;
23+
import org.apache.cassandra.db.marshal.DecimalType;
2424
import org.apache.cassandra.db.marshal.TimeUUIDType;
2525
import org.apache.cassandra.db.marshal.UUIDType;
2626
import org.apache.lucene.analysis.TokenStream;
@@ -45,6 +45,7 @@
4545
import org.elasticsearch.index.query.QueryShardContext;
4646

4747
import java.io.IOException;
48+
import java.math.BigDecimal;
4849
import java.util.Iterator;
4950
import java.util.List;
5051
import java.util.Map;
@@ -264,6 +265,9 @@ public Object cqlValue(Object value, AbstractType atype) {
264265
// #74 workaround
265266
return UUID.fromString(value.toString());
266267
}
268+
if (atype instanceof DecimalType) {
269+
return new BigDecimal(value.toString());
270+
}
267271
return value.toString();
268272
}
269273

@@ -389,7 +393,7 @@ protected void parseCreateField(ParseContext context, List<IndexableField> field
389393

390394
@Override
391395
public void createField(ParseContext context, Object object) throws IOException {
392-
String value = (object instanceof UUID) ? object.toString() : (String) object; // #74 uuid stored as string
396+
String value = (object == null || object instanceof String) ? (String) object : object.toString(); // #74 uuid stored as string
393397
if (value == null)
394398
value = fieldType().nullValueAsString();
395399

server/src/test/java/org/elassandra/CqlTypesTests.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.elasticsearch.test.ESSingleNodeTestCase;
4545
import org.junit.Test;
4646

47+
import java.math.BigDecimal;
4748
import java.nio.ByteBuffer;
4849
import java.time.Instant;
4950
import java.time.LocalDate;
@@ -107,7 +108,7 @@ public void testAllTypesTest() throws Exception {
107108
ensureGreen("ks1");
108109

109110
process(ConsistencyLevel.ONE,
110-
"CREATE TABLE ks1.natives (c1 text primary key, c2 text, c3 timestamp, c4 int, c5 bigint, c6 double, c7 float, c8 boolean, c9 blob, c10 uuid, c11 timeuuid, c12 smallint, c13 tinyint)");
111+
"CREATE TABLE ks1.natives (c1 text primary key, c2 text, c3 timestamp, c4 int, c5 bigint, c6 double, c7 float, c8 boolean, c9 blob, c10 uuid, c11 timeuuid, c12 smallint, c13 tinyint, c14 decimal)");
111112
assertAcked(client().admin().indices()
112113
.preparePutMapping("ks1")
113114
.setType("natives")
@@ -116,7 +117,7 @@ public void testAllTypesTest() throws Exception {
116117

117118
// {"c2": "toto", "c3" : "2016-10-10", "c4": 1, "c5":44, "c6":1.0, "c7":2.22, "c8": true, "c9":"U29tZSBiaW5hcnkgYmxvYg==" }
118119
assertThat(client().prepareIndex("ks1", "natives", "1")
119-
.setSource("{\"c2\": \"toto\", \"c3\" : \"2016-10-10\", \"c4\": 1, \"c5\":44, \"c6\":1.0, \"c7\":2.22, \"c8\": true, \"c9\":\"U29tZSBiaW5hcnkgYmxvYg==\", \"c10\":\"ae8c9260-dd02-11e6-b9d5-bbfb41c263ba\",\"c11\":\"ae8c9260-dd02-11e6-b9d5-bbfb41c263ba\", \"c12\":1, \"c13\":1 }", XContentType.JSON)
120+
.setSource("{\"c2\": \"toto\", \"c3\" : \"2016-10-10T00:00:00.000Z\", \"c4\": 1, \"c5\":44, \"c6\":1.0, \"c7\":2.22, \"c8\": true, \"c9\":\"U29tZSBiaW5hcnkgYmxvYg==\", \"c10\":\"ae8c9260-dd02-11e6-b9d5-bbfb41c263ba\",\"c11\":\"ae8c9260-dd02-11e6-b9d5-bbfb41c263ba\", \"c12\":1, \"c13\":1, \"c14\":\"3.1416\" }", XContentType.JSON)
120121
.get().getResult(), equalTo(DocWriteResponse.Result.CREATED));
121122
Map<String,Object> fields = client().prepareSearch("ks1").setTypes("natives").setQuery(QueryBuilders.queryStringQuery("c2:toto"))
122123
.get().getHits().getHits()[0]
@@ -131,30 +132,31 @@ public void testAllTypesTest() throws Exception {
131132
assertThat(fields.get("c9"),equalTo("U29tZSBiaW5hcnkgYmxvYg=="));
132133
assertThat(fields.get("c12"),equalTo(1));
133134
assertThat(fields.get("c13"),equalTo(1));
135+
assertThat(fields.get("c14"),equalTo("3.1416"));
134136

135-
process(ConsistencyLevel.ONE,"insert into ks1.natives (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13) VALUES ('tutu', 'titi', '2016-11-11', 1, 45, 1.0, 2.23, false,textAsBlob('bdb14fbe076f6b94444c660e36a400151f26fc6f'),ae8c9260-dd02-11e6-b9d5-bbfb41c263ba,ae8c9260-dd02-11e6-b9d5-bbfb41c263ba, 1, 1)");
137+
process(ConsistencyLevel.ONE,"insert into ks1.natives (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14) VALUES ('tutu', 'titi', '2016-11-11T00:00:00.000Z', 1, 45, 1.0, 2.23, false,textAsBlob('bdb14fbe076f6b94444c660e36a400151f26fc6f'),ae8c9260-dd02-11e6-b9d5-bbfb41c263ba,ae8c9260-dd02-11e6-b9d5-bbfb41c263ba, 1, 1, 3.1416)");
136138
assertThat(client().prepareSearch().setIndices("ks1").setTypes("natives").setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits(), equalTo(2L));
137139

138140
fields = client().prepareSearch().setIndices("ks1").setTypes("natives").setQuery(QueryBuilders.queryStringQuery("c5:45")).get().getHits().getHits()[0].getSourceAsMap();
139-
140141
assertThat(fields.get("c2"), equalTo("titi"));
141-
//assertThat(fields.get("c3"), equalTo(new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-11").toLocaleString()));
142+
assertThat(fields.get("c3"), equalTo("2016-11-11T00:00:00.000Z"));
142143
assertThat(fields.get("c4"),equalTo(1));
143144
assertThat(fields.get("c5"),equalTo(45));
144145
assertThat(fields.get("c6"),equalTo(1.0));
145146
assertThat(fields.get("c7"),equalTo(2.23));
146147
assertThat(fields.get("c8"),equalTo(false));
147148
assertThat(fields.get("c12"),equalTo(1));
148149
assertThat(fields.get("c13"),equalTo(1));
150+
assertThat(fields.get("c14"),equalTo("3.1416"));
149151
}
150152

151153
@Test
152154
public void testSinglePkTypesTest() throws Exception {
153155
createIndex("ks");
154156
ensureGreen("ks");
155157

156-
String[] types = new String[] { "text","int","smallint","tinyint","bigint","double","float","boolean","blob","timestamp","date","inet","uuid" };
157-
Object[] values = new Object[] { "foo", 1, (short)1, (byte)1, 2L, new Double(3.14), new Float(3.14), true, ByteBuffer.wrap("toto".getBytes("UTF-8")), new Date(), (int)LocalDate.now().toEpochDay(), InetAddresses.forString("127.0.0.1"), UUID.randomUUID() };
158+
String[] types = new String[] { "text","int","smallint","tinyint","bigint","double","float","boolean","blob","timestamp","date","inet","uuid","decimal" };
159+
Object[] values = new Object[] { "foo", 1, (short)1, (byte)1, 2L, new Double(3.14), new Float(3.14), true, ByteBuffer.wrap("toto".getBytes("UTF-8")), new Date(), (int)LocalDate.now().toEpochDay(), InetAddresses.forString("127.0.0.1"), UUID.randomUUID(), new BigDecimal("3.1416") };
158160
for(int i=0; i < types.length; i++) {
159161
String type = types[i];
160162
Object value = values[i];
@@ -197,9 +199,9 @@ public void testCompoundPkTypesTest() throws Exception {
197199
ensureGreen("ks");
198200

199201
Date now = new Date();
200-
String[] types = new String[] { "text", "int","smallint","tinyint", "bigint","double","float","boolean","blob","timestamp","date", "inet","uuid","timeuuid","timeuuid" };
201-
String[] names = new String[] { "text", "int","smallint","tinyint", "bigint","double","float","boolean","blob","timestamp","date2", "inet","uuid","timeuuid","timeuuid2" };
202-
Object[] values = new Object[] { "foo", 1, (short)1, (byte)1, 2L, new Double(3.14), new Float(3.14), true, ByteBuffer.wrap("toto".getBytes("UTF-8")), new Date(), (int)LocalDate.now().toEpochDay(), InetAddresses.forString("127.0.0.1"), UUID.randomUUID(), UUIDGen.getTimeUUID(now.getTime()), UUIDGen.getTimeUUID(now.getTime()) };
202+
String[] types = new String[] { "text", "int","smallint","tinyint", "bigint","double","float","boolean","blob","timestamp","date", "inet","uuid","timeuuid","timeuuid","decimal" };
203+
String[] names = new String[] { "text", "int","smallint","tinyint", "bigint","double","float","boolean","blob","timestamp","date2", "inet","uuid","timeuuid","timeuuid2","decimal"};
204+
Object[] values = new Object[] { "foo", 1, (short)1, (byte)1, 2L, new Double(3.14), new Float(3.14), true, ByteBuffer.wrap("toto".getBytes("UTF-8")), new Date(), (int)LocalDate.now().toEpochDay(), InetAddresses.forString("127.0.0.1"), UUID.randomUUID(), UUIDGen.getTimeUUID(now.getTime()), UUIDGen.getTimeUUID(now.getTime()), new BigDecimal("3.1416") };
203205
int randomCk = randomInt(types.length-1);
204206
int randomVal= randomInt(types.length-1);
205207
for(int i=0; i < types.length; i++) {
@@ -249,7 +251,8 @@ public void testCompoundPkTypesTest() throws Exception {
249251
System.out.println("delete pk name="+name+" type="+type+" value="+values[i]+" ck type="+types[randomCk]+" value="+values[randomCk]);
250252
process(ConsistencyLevel.ONE,String.format(Locale.ROOT,"DELETE FROM ks.t%s WHERE pk%s = ? AND ck >= ?", name, name), values[i], values[randomCk]);
251253
// blob not supported for delete by query
252-
assertThat(client().prepareSearch()
254+
assertThat("search in ks"+i+" type="+String.format(Locale.ROOT,"t%s", name)+" cql_type="+type,
255+
client().prepareSearch()
253256
.setIndices("ks"+i)
254257
.setTypes(String.format(Locale.ROOT,"t%s", name))
255258
.setQuery(QueryBuilders.matchAllQuery())

0 commit comments

Comments
 (0)