FOAM Sinks are the destination objects that receive and process data from DAO (Data Access Object) queries. They implement a streaming architecture where data flows from a source DAO through the select() operation to a Sink that accumulates, transforms, or processes the results.
The core Sink interface defines four methods for handling data streams:
interface Sink {
void put(obj, sub) // Called for each object
void remove(obj, sub) // Called for removed objects (listen() only)
void eof() // Called when stream ends normally
void reset(sub) // Called when result set may have changed (listen() only)
}
The sub parameter is a Detachable subscription that lets sinks stop receiving data by calling sub.detach().
FOAM’s query system follows a “source-to-sink” pattern where:
select() streams objects from DAO to SinkThis is analogous to Unix pipes, where data flows through processing stages to an output destination.
| Sink | Purpose | Result Property |
|---|---|---|
ArraySink |
Collects all objects into an array | .array |
COUNT() |
Counts matching objects | .value |
SUM(prop) |
Sums a numeric property | .value |
MAX(prop) |
Finds maximum value | .value |
MIN(prop) |
Finds minimum value | .value |
GROUP_BY(prop, sink) |
Groups results by property | .groups |
MAP(prop) |
Projects single property from each object | .delegate.array |
UNIQUE(prop, sink) |
Filters distinct values | — |
// Collect all results
var sink = await dao.select();
console.log(sink.array);
// Count matching objects
var count = await dao.select(COUNT());
console.log(count.value);
// Group by category with counts
dao.select(GROUP_BY(MyModel.CATEGORY)).then(sink => {
console.log(sink.groups);
});
Sinks can be composed through delegation, where one sink processes results and passes them to another sink for further processing:
// Filter → Transform → Collect
var sink = PredicatedSink.create({
predicate: someCondition,
delegate: Map.create({
arg1: MyModel.NAME,
delegate: ArraySink.create()
})
});
You can create custom sinks by implementing the Sink interface:
foam.CLASS({
name: 'CustomSink',
implements: ['foam.dao.Sink'],
methods: [
function put(obj, sub) {
// Process each object
console.log('Received:', obj);
},
function eof() {
// Called when complete
console.log('All data received');
}
]
});
For cross-platform sinks, provide both JavaScript and Java implementations to enable server-side execution, which reduces network traffic by processing data where it resides.
FOAM includes specialized sinks for specific use cases:
FnSink - Converts all sink events to calls on a single functionOrderedSink - Sorts results before delegating to another sinkNullSink - Do-nothing sink (Null Pattern)Sequence - Executes multiple sinks in sequencedao.select() but also work with dao.listen() for real-time updatesselect() method returns a Promise that resolves with the sink containing resultssub.detach() method allows early termination of data streams