Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relational data using hash tables

Tags:

scala

I am trying to keep in memory a set of data structures identified by the following schema. I have a crude solution, but I'm looking for some better ideas. Performance and reliability is critical important, memory not so much (within reason), since the tables are fairly small (maximum a couple of hundred entries, more likely a few dozens). I would rather not use an in-memory DB for such a small data set, I think.

enter image description here

What I'm trying to accomplish is the ability to quickly query All B entries based on A.Name, all A entries based on B.Name, or all A entries based on T.Tag (I don't really need all B entries based on T.Tag currently, but could be useful in the future)

Currently I use three tables with duplicate data, and with the synchronization issues that that brings, and when I have a new piece of data, I store it three different ways. I am sure there has to be a better way.

// all A entries matching a tag
val Tag2A= new MutableHashMap[String, MutableSet[String]]() with MutableMultiMap[String, String] 

// all B entries matching a tag
val Tag2B = new MutableHashMap[String, MutableSet[List[Int]]]() with MutableMultiMap[String, List[Int]]

// all Tags matching a A entry
val A2Tag = new MutableHashMap[String, MutableSet[String]]() with MutableMultiMap[String, String]

Could someone recommend a more elegant solution?

EDIT: (clarification) My MutableMultiMap and MutableSet are just mutable.MultiMap and mutable.Set aliased at import time.

EDIT2: the tables need to be modifiable (add/remove).

like image 495
Will I Am Avatar asked Mar 27 '26 12:03

Will I Am


1 Answers

Assuming you can load everything into a memory, immutable solution is acceptable:

 abstract class A2B(tag: String) {
   def aMap: Map[String, A]
   def bMap: Map[String, B]
 }

 case class A(id: String, name: String, tag: A2B, payload: String)

 case class B(id: String, name: String, tag: A2B, payload: List[Int])

You can initialize it like that (to solve chicken-egg problem):

def getA2b(name: String): A2B = new A2B(name) {
    val aMap = { //you can query your external data source tableA here
        val a1 = "a1" -> A("a1", "a1", this, "aaaa")
        val a2 = "a2" -> A("a2", "a2", this, "aaaa")
        Map(a1, a2)
    }

    val bMap = { //you can query your external data source tableB here
        val b1 = "b1" -> B("b1", "b1", this, Nil)
        val b2 = "b2" -> B("b2", "b2", this, Nil)
        Map(b1, b2)
    } 

    override def toString = name
}


val a2b = Map("a2b1" -> getA2b("a2b1")) //you can query your external data source tableA2B here

And query with constant access time (sorry have no Scala REPL on current machine yet):

println(a2b("a2b1"))
println(a2b("a2b1").aMap)
println(a2b("a2b1").aMap("a1").tag.bMap)

a2b1                                                                                                                                                                                                                                                   
Map(a1 -> A(a1,a1,a2b1,aaaa), a2 -> A(a2,a2,a2b1,aaaa))                                                                                                                                                                                                
Map(b1 -> B(b1,b1,a2b1,List()), b2 -> B(b2,b2,a2b1,List()))

All relations here are modeled with links, so no overhead. The structure is immutable, so it's thread-safe. You can also notice that A2B class gets initialized inside constructor (and all vals are final by default), so no synchronization problems according to JSR-133 - your application always see final version of A2B, so no need for volatiles.

like image 175
dk14 Avatar answered Mar 29 '26 08:03

dk14



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!