Rendering find().fetch() results in the Blaze UI


#1

I have a helper that I’m using to populate a datalist element with addresses from a collection. The user could start typing any address at any time, so I can’t really limit a publication of the data intelligently (that I’ve figured out), and provide this “quick look up” action. Basically as the user types more than 3 characters I start querying the address data and trying to provide a 20 row list of options for the user to select from. As the user types more, the list filters more closely to what they may be looking for.

The issue is with 15000 documents of addresses in a publication the UI takes about 10 seconds to become enabled. This is only a small sub-set of the total number of addresses in 1 client database, so the publish and subscribe aren’t going to work I don’t think.

No problem. I created a method on the server only, that takes the typed characters as a parameter, and queries the MongoDB directly. Still pretty quick from what I see coming back in the console logging. So, great.

I’m using this query to search as the user types:

'findMatchAddresses' (partialAddString) {
        check(partialAddString, String);

        if (!this.userId) {
            throw new Meteor.Error('User is not logged in, and not authorized to update user information.');
        }

        let addressList = Addresses.find({ addressString: {$regex: partialAddString + '.*' }}, { sort: { _id: 1 }, limit: 20 }).fetch();
        return addressList;
    },

I tried it with just find(), and no fetch(), but got 500 server error trying to return it.

Basically I had

return Addresses.find({ addressString: {$regex: partialAddString + '.*' }}, { sort: { _id: 1 }, limit: 20 });

As I said, got server error, so added the .fetch() and am getting results now.

In my helper, though, how do I get the results to cycle through and display in my datalist element.

Here’s what I have now:

locationInfoData: function() {
        let typedIn = Session.get("searchFor");

        if (typedIn == "") {
            return;
        } else {
            setTimeout(function() {
                Materialize.updateTextFields();
            }, 100);
            Meteor.call('findMatchAddresses', typedIn, function(err, result) {
                if (err) {
                    console.log('Error finding Address matches: ' + err);
                } else {
                    console.log(result);
                    return result;
                }
            });
        }
    },

I have the timeout just to get the Materialize UI stuff to refresh and show the data, which worked when I was using the publish/subscribe.

Here’s my template:

    <input list="locationData" id="callLocation" />
        <label for="callLocation">Call Location</label>
        <datalist id="locationData">
            {{#each locationInfoData}}
                <option value="{{this.addressString}}">{{this.landMark}}</option>
            {{/each}}
        </datalist>

As always, help, links, and hints are greatly appreciated.


#2

Hard to say what might be causing the hiccup here, but I’d probably try and break things up a bit. You could use Session vars in place of the template-scoped reactive vars, if you want:

Template.someTemplate.onCreated(function() {
  this.queryVar = new ReactiveVar('');
  this.resultsVar = new ReactiveVar([]);

  this.autorun(() => {
    const query = this.queryVar.get();
    if (query && query !== '') {
      Meteor.call('findMatchAddresses', query, (err, res) => {
        if (err) {
          console.log(`Error finding address matches: ${err}`);
        } else {
          this.resultsVar.set(res);
        }
      });
    }
  });
});

Template.someTemplate.events({
  'keyup input'(e, i) {
    const query = e.currentTarget.value;
    if (query) {
      i.queryVar.set(query);
    }
  },
});

Template.someTemplate.helpers({
  locationInfoData() {
    const i = Template.instance();
    return i.resultsVar.get();
  },
});

#3

Ok, I was trying to do this as a work around to my publish function publishing all 125000 addresses and taking forever to free up the UI. Obviously, I was not thinking clearly, and part of it was my simple lack of understanding the full breadth of capability given to us in the Publish / Subscribe feature set.

I was just doing a normal Publish, and only filtering lightly in a static way within the publish function. For whatever reason, it just never occurred to me that I may could pass values dynamically from the subscription to the publication, and have the subscription update as those values changed.

I found what I need in the documents (duh), and will be re-arranging my code to try and take advantage of it. For those facing similar problems, here’s where I started in the docs.