Search code examples
javascriptmeteormeteor-blazeminimongo

Concatenate results of two queries in a template helper


I have two collections: Commands and CommandHistory. I am trying to make an output that received the results of both of them, combined, where anything from CommandHistory is bold. That is, something that looked like:

**Do the thing**    <--| **Command History*
Doing thing           <-|
thing is in progress  <-| Commands
thing is done         <-|
**Do the other thing**             <-| **Command History**
I don't know what the other thing is <-| Commands

Here is a basic start of a helper:

log() {
  const commandCursor = Commands.find({}, { sort: { timestamp: 1 } });
  const commandHistory = CommandHistory.find({}, { sort: { timestamp: 1 } });
  // what now? Need to concatenate them.
  // `.fetch()` makes the interface seem non-reactive.
}

The only thing I can really think of is to make yet another (null) collection and output everything from both collections into it, which seems like overkill.

Is it possible to concatenate the results of these two cursors, and still have it act reactively?


Solution

  • I think what you are looking for is mainly a way to combine both result sets. For that, you could use something like this:

    log: function () {
            var commands = Commands.find({}, { sort: { timestamp: 1 } }).map(function(i){i.origin='command'; return i;});
          var history  = History.find({}, { sort: { timestamp: 1} }).map(function(i){i.origin='history'; return i;});
          var combined = _.union(commands, history);
          return _.sortBy(combined, function(i) { return Number(i.timestamp) });
        }
    

    Here I'm adding a field called "origin" to identify where each entry came from. Also, I'm sorting the combined data set based on the timestamp, which I'm considering a simple Number for simplifying the example.

    Once you have the data set combined, you can print them using another helper, which returns with or without **, depending on the origin:

    output: function(obj) {
      var text = obj.value || "";
      if(obj.origin == 'history') text = "**"+text+"**";
      return text
    }
    

    Here's a sample code you can try:

    HTML: hello

    <body>
      {{> hello}}
    </body>
    
    <template name="hello">
      <ul>
        {{#each log}}
          <li>{{output this}}</li>
        {{/each}}
      </ul>
    </template>
    

    JS:

    Commands = new Mongo.Collection("commands");
    History  = new Mongo.Collection("history");
    
    if (Meteor.isClient) {
      // counter starts at 0
      Session.setDefault('counter', 0);
    
      Template.hello.helpers({
        log: function () {
            var commands = Commands.find({}, { sort: { timestamp: 1 } }).map(function(i){i.origin='command'; return i;});
          var history  = History.find({}, { sort: { timestamp: 1} }).map(function(i){i.origin='history'; return i;});
          var combined = _.union(commands, history);
          return _.sortBy(combined, function(i) { return Number(i.timestamp) });
        },
    
        output: function(obj) {
          var text = obj.value || "";
          if(obj.origin == 'history') text = "**"+text+"**";
          return text
        }
      });
    
    }
    
    if (Meteor.isServer) {
      Meteor.startup(function () {
        Commands.remove({});
        History.remove({});
        for (var i=1; i<5; i++) {
          History.insert({value: 'history '+i, timestamp: 10*i});
          for (var j=1; j<4; j++) 
            Commands.insert({value: 'command '+i+'.'+j, timestamp: 10*i+j});
        }
      });
    }