1
\$\begingroup\$

The code combines data from two Ballerina table data structures.

The program prints the following expected and correct results (edited for readability):

[
  {"fooId":1,"barId":2,"data":"foo 1;bar 2"},
  {"fooId":1,"barId":4,"data":"foo 1;bar 4"},
  {"fooId":3,"barId":4,"data":"foo 3;bar 4"},
  {"fooId":3,"barId":6,"data":"foo 3;bar 6"},
  {"fooId":5,"barId":6,"data":"foo 5;bar 6"}
]

However I'm looking here other ways to implement/improve the data join as I feel this way is a rather clumsy (3 foreach loops and 1 query expression). I had several failed attempts to use a single query expression that IMO is the idiomatic Ballerina way.

Note that answers that modify the record definitions can not be accepted. I'm aware of an option to refactor the relationships to an associative (or junction) table as I would do in the database but unfortunately, it's not an option in this case (the record definitions can't be changed).

This is Ballerina 2201.2.0 (Swan Lake Update 2)

import ballerina/io;

// the record definitions can't be changed

type Foo record {|
    readonly int id;
    string data;
    int[] barIds;
|};
type FooTable table<Foo> key(id);

type Bar record {|
    readonly int id;
    string data;
|};
type BarTable table<Bar> key(id);

public function main() {
    FooTable foos = table [
        {id: 1, data: "foo 1", barIds: [2, 4]},
        {id: 3, data: "foo 3", barIds: [4, 6]},
        {id: 5, data: "foo 5", barIds: [6]}
    ];

    BarTable bars = table [
        {id: 2, data: "bar 2"},
        {id: 4, data: "bar 4"},
        {id: 6, data: "bar 6"}
    ];

    table<record {|int fooId; int barId; string data;|}> fooBars = table [];

    // the code below can be changed

    foreach var foo in foos {
        table <record {|int id;|}> barIds = table [];
        foreach var barId in foo.barIds {
            barIds.add({id: barId});
        }

        var temps =
            from var barId in barIds
            join var bar in bars on barId.id equals bar.id
            select {
                fooId: foo.id,
                barId: barId.id,
                data: string`${foo.data};${bar.data}`
            }
        ;

        foreach var temp in temps {
            fooBars.add(temp);
        }   
    }

    io:println(fooBars);
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You can do the combining part like this:

table<record {|int fooId; int barId; string data;|}> fooBars2 = table key(fooId, barId) from var foo in foos
    from var barId in foo.barIds
    where bars.hasKey(barId)
    let Bar bar = bars.get(barId)
    select {
        fooId: foo.id,
        barId: barId,
        data: foo.data + ";" + bar.data
    };

Here we use nested from clauses where the inner from clause will iterate over the barIds in a Foo record.

\$\endgroup\$
0
\$\begingroup\$

Here is another variation (from Ballerina Discord):

fooBars =
  from Bar bar in bars
  from Foo foo in foos where foo.barIds.indexOf(bar.id) != ()
  select {
    fooId: foo.id,
    barId: bar.id,
    data: string`${foo.data};${bar.data}`
};
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.