Skip to content

image

left.image(right, :images, [:foreign_key])
left.image(right, :images, [:foreign_key], array: true)

Extend each tuple with its related tuples from another relation, creating a nested sub-relation.

Example: I want each supplier along with all the parts they supply as a nested collection.

The image operator extends each tuple from the left relation with a new attribute containing all matching tuples from the right relation. This creates a hierarchical/nested structure.

For each left tuple, the operator finds all right tuples that match on the specified join attributes, collects them into a relation (or array), and adds them as a new attribute.

Parameters:

ParameterDescription
rightThe relation to get the image from
asThe name of the new attribute (default: :image)
onThe attributes to join on (default: [])
array: trueConvert the image to an Array instead of a Relation
preserve: trueKeep the join attributes in the nested tuples

The join attributes must exist in both relations.

Consult the Overview page for the data model used in these examples.

Get each supplier with their supplies as a nested relation:

suppliers.image(supplies, :supplies, [:sid]).to_a
=>
[{:sid=>"S1", :name=>"Smith", :status=>20, :city=>"London",
:supplies=>#<Bmg::Relation ...>},
{:sid=>"S2", :name=>"Jones", :status=>10, :city=>"Paris",
:supplies=>#<Bmg::Relation ...>},
{:sid=>"S3", :name=>"Blake", :status=>30, :city=>"Paris",
:supplies=>#<Bmg::Relation ...>},
{:sid=>"S4", :name=>"Clark", :status=>20, :city=>"London",
:supplies=>#<Bmg::Relation ...>},
{:sid=>"S5", :name=>"Adams", :status=>30, :city=>"Athens",
:supplies=>#<Bmg::Relation []>}]

Note that S5 has an empty relation because they have no supplies.

Get each supplier with their supplies as an array (useful for JSON serialization):

suppliers.image(supplies, :supplies, [:sid], array: true)
.project([:sid, :name, :supplies])
.to_a
=>
[{:sid=>"S1", :name=>"Smith",
:supplies=>[{:pid=>"P1", :qty=>300}, {:pid=>"P2", :qty=>200}, ...]},
{:sid=>"S2", :name=>"Jones",
:supplies=>[{:pid=>"P1", :qty=>300}, {:pid=>"P2", :qty=>400}]},
{:sid=>"S3", :name=>"Blake",
:supplies=>[{:pid=>"P2", :qty=>200}]},
{:sid=>"S4", :name=>"Clark",
:supplies=>[{:pid=>"P2", :qty=>200}, {:pid=>"P4", :qty=>300}, {:pid=>"P5", :qty=>400}]},
{:sid=>"S5", :name=>"Adams",
:supplies=>[]}]

The key difference between image and join:

  • join produces one result tuple for each match (flattened, may duplicate left tuples)
  • image produces one result tuple per left tuple, with matches nested inside
# join: one row per supplier-supply combination
suppliers.join(supplies, [:sid]).count
=> 12 # Multiple rows for suppliers with multiple supplies
# image: one row per supplier, supplies nested
suppliers.image(supplies, :supplies, [:sid]).count
=> 5 # One row per supplier