3

I am trying to store a Scala Map (that I'm trying to convert to a java.util.Map) into cassandra 2.1.8.

The data structure looks like this:

Map[String -> Set[Tuple[String, String, String]]]

I created the table as follows:

CREATE TABLE mailing (emailaddr text PRIMARY KEY, totalmails bigint, emails map<text, frozen<set<tuple<text, text, text>>>>);

I first try to convert the Set's to java Set's:

def emailsToCassandra(addr: emailAddress, mail: MailContent, number: Int) = {
println("Inserting emails into cassandra")

mail.emails.foreach(result =>

  setAsJavaSet(result._2)
)

I then build the query and attempt to convert the Map to a java Map:

val query = QueryBuilder.insertInto("emails", "mailing")
                        .value("emailAddr", addr.toString())
                        .value("totalmails", number)
                        .value("emails", mapAsJavaMap(mail.emails))
session.executeAsync(query)

I get back:

java.lang.IllegalArgumentException: Value 1 of type class scala.collection.convert.Wrappers$MapWrapper does not correspond to any CQL3 type

I also tried to do this:

val lol = mail.emails.asInstanceOf[java.util.Map[String, java.util.Set[Tuple3[String, String, String]]]]

Which didn't work

Thank you in advance

3 Answers 3

5

There are a few things you are going to need to overcome here:

  1. Converting the map into the java.util.Map type. (You've already covered this by using mapAsJavaMap)
  2. Converting the Set[Tuple3] into a java.util.Set type.
  3. Converting Tuple3 into a TupleValue.

Unfortunately the error returned by the driver (java.lang.IllegalArgumentException: Value 1 of type class scala.collection.convert.Wrappers$MapWrapper does not correspond to any CQL3 type) is misleading, as the Map type is converting correctly in your code, but the Tuple3 type is ultimately what it is having problems with. I opened up JAVA-833 to track this.

I'm making assumptions about what MailContent is, but here is some code that should make things work. The main logic that does the heavy lifting is emailsToCql which maps Tuple3[String, String, String] into TupleValue, Set to java.util.Set and Map to java.util.Map.

import com.datastax.driver.core.{DataType, TupleType, Cluster}
import com.datastax.driver.core.querybuilder.QueryBuilder

import scala.collection.JavaConverters._

object Scratch extends App {

  val cluster = Cluster.builder().addContactPoint("127.0.0.1").build()
  val session = cluster.connect()

  session.execute("create keyspace if not exists emails WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };")
  session.execute("create table if not exists emails.mailing (emailaddr text PRIMARY KEY, totalmails bigint, emails map<text, frozen<set<tuple<text, text, text>>>>);")

  val emailType = TupleType.of(DataType.text(), DataType.text(), DataType.text())

  case class MailContent(addr: String, emails: Map[String, Set[Tuple3[String, String, String]]]) {
    lazy val emailsToCql = emails.mapValues {
      _.map(v => emailType.newValue(v._1, v._2, v._3)).asJava
    }.asJava
  }

  val mailContent = MailContent("[email protected]", Map(
    "[email protected]" -> Set(("field1", "field2", "field3")),
    "[email protected]" -> Set(("2field1", "2field2", "2field3"))))

  val query = QueryBuilder.insertInto("emails", "mailing")
                .value("emailAddr", mailContent.addr)
                .value("totalmails", mailContent.emails.size)
                .value("emails", mailContent.emailsToCql)

  session.execute(query)

  cluster.close()
}

This yields a record that looks like the following in cqlsh:

 emailaddr      | emails                                                                                                       | totalmails
----------------+--------------------------------------------------------------------------------------------------------------+------------
 [email protected] | {'[email protected]': {('2field1', '2field2', '2field3')}, '[email protected]': {('field1', 'field2', 'field3')}} |          2
Sign up to request clarification or add additional context in comments.

Comments

3

I am far from being a Scala expert, but the error looks like Scala "convertion" to Java Map means to encapsulate it into a scala.collection.convert.Wrappers$MapWrapper that extends java.util.AbstractMap which, in turn, implements java.util.Map. When Cassandra tries to map from Java's types to its own it doesn't encounter MapWrapper as a valid type.

I would recommend you to write your own method to convert the whole thing (MailContent, right?) from a Map[String -> Set[Tuple[String, String, String]]] to a similar structure using Java's equivalents: HashMap and HashSet (shrug). Also, you should have problems with the TupleType so you should use Cassandra's TupleType during this conversion.

Comments

2

user2244255 was right, separating it out into it's own method helped me understand what was going on. Here is the final code that worked:

def resultToJavaMap(mail: MailContent) ={

 mapAsJavaMap(mail.emails.mapValues{result =>

  setAsJavaSet(result.map {lol =>
    val theType = TupleType.of(DataType.text(), DataType.text(), DataType.text())
    val theValue = theType.newValue()
    theValue.setString(0, lol.to.toString())
    theValue.setString(1, lol.subject)
    theValue.setString(2, lol.message)
  })

})

Essentially you access the tuples in the Set, change them to the datastax type, then change the entire set to a java.util.Set then change the entire thing to a java.util.Map

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.