2

I'm having trouble getting Meteor.publish to update in response to a changing form field. The first call to publish seems to stick, so the query operates in that subset until the page is reloaded.

I followed the approach in this post, but am having no luck whatsoever.

Any help greatly appreciated.

In lib:

SearchResults = new Meteor.Collection("Animals");

function getSearchResults(query) {
  re = new RegExp(query, "i");
  return SearchResults.find({$and: [ {is_active: true}, {id_species: {$regex: re}} ] }, {limit: 10});
}

In client:

Session.set('query', null);

Template.searchQuery.events({
  'keyup .query' : function (event, template) {
    query = template.find('.query').value
    Session.set("query", query);
  }
});

Meteor.autosubscribe(function() {
  if (Session.get("query")) {
    Meteor.subscribe("search_results", Session.get("query"));
  }
});

Template.searchResults.results = function () {
  return getSearchResults(Session.get("query"));
}

On server:

Meteor.publish("search_results", getSearchResults);

Template: Search for Animals

<body>
  {{> searchQuery}}

  {{> searchResults}}
</body>

<template name="searchQuery">
  <form>
  <label>Search</label>
  <input type="text" class="query" />
</form>
</template>

<template name="searchResults">
  {{#each results}}
  <div>
    {{_id}}
  </div>
  {{/each}}
</template>

Update [WRONG]

Apparently, the issue is that the collection I was working with was (correctly) generated outside of Meteor, but Meteor doesn't properly support Mongo's ObjectIds. Context here and related Stackoverflow question.

Conversion code shown there, courtesy antoviaque:

db.nodes.find({}).forEach(function(el){
    db.nodes.remove({_id:el._id}); 
    el._id = el._id.toString(); 
    db.nodes.insert(el); 
});

Update [RIGHT]

So as it turns out, it was an issue with RegExp / $regex. This thread explains. Instead of:

function getSearchResults(query) {
  re = new RegExp(query, "i");
  return SearchResults.find({$and: [ {is_active: true}, {id_species: {$regex: re}} ] }, {limit: 10});
}

At the moment, one needs to do this instead:

function getSearchResults(query) {
  // Assumes query is regex without delimiters e.g., 'rot'
  // will match 2nd & 4th rows in Tim's sample data below
  return SearchResults.find({$and: [ {is_active: true}, {id_species: {$regex: query, $options: 'i'}} ] }, {limit: 10});
}

That was fun.

PS -- The ddp-pre1 branch has some ObjectId functionality (SearchResults = new Meteor.Collection("Animals", {idGeneration: "MONGO"});)

Community
  • 1
  • 1
mukmuk
  • 222
  • 4
  • 10

1 Answers1

1

Here's my working example:

UPDATE the original javascript given was correct. The problem, as noted in the comments, turned out to be that meteor doesn't yet support ObjectIds.

HTML:

<body>
  {{> searchQuery }}
  {{> searchResults}}
</body>

<template name="searchQuery">
  <form>
  <label>Search</label>
  <input type="text" class="query" />
</form>
</template>

<template name="searchResults">
  {{#each results}}
  <div>
     {{id_species}} | {{name}} - {{_id}}
  </div>
  {{/each}}
</template>

Javascript:

Animals = new Meteor.Collection("Animals");

function _get(query) {
    re = new RegExp(query, "i");
    console.log("rerunning query: " + query);
    return Animals.find({$and: [ {is_active: true}, {id_species: {$regex: re}} ] }, {limit: 10});
};

if (Meteor.isClient) {

    Session.set("query", "");

    Meteor.autosubscribe(function() {
        Meteor.subscribe("animals", Session.get("query"));
    });

    Template.searchQuery.events({
      'keyup .query' : function (event, template) {
        query = template.find('.query').value
        Session.set("query", query);
      }
    });

    Template.searchResults.results = function () {
        return _get(Session.get("query"));              
    }
}

if (Meteor.isServer) {
    Meteor.startup(function() {
        if (Animals.find().count() === 0) {
            Animals.insert({name: "panda", is_active: true, id_species: 'bear'});
            Animals.insert({name: "panda1", is_active: true, id_species: 'bearOther'});
            Animals.insert({name: "panda2", is_active: true, id_species: 'bear'});
            Animals.insert({name: "panda3", is_active: true, id_species: 'bearOther'}); 
        }
    });
    Meteor.publish("animals", _get);
}
Community
  • 1
  • 1
TimDog
  • 8,758
  • 5
  • 41
  • 50
  • Tim, thanks for your reply. The primary change above (beyond combining the files) seems to be removal of the query selectors from the server side. If I'm not mistaken, this has the effect then of publishing the entire collection i.e., the same as if I'd had autopublish turned on. Unfortunately, there are about 500k documents in the Animals collection (collection name changed to protect innocent). Won't this approach just crash my browser? – mukmuk Jan 15 '13 at 16:43
  • Oh, thanks for clarifying. Okay. I modified my original example. Your first stab was actually closer than I realized :) Can you let me know how this looks with your larger record set? – TimDog Jan 15 '13 at 17:05
  • Thanks for followup - just tried it. Result is the same (actually a bit worse because the checks for query being null are gone so it returns first 10 records with a blank query). I'm not sure why the code above would yield anything different since the only change now appears to be renaming getSearchResults to _get and calling that function indirectly from publish. I'm sure it's amazing, but at the moment Meteor is driving me bonkers! – mukmuk Jan 15 '13 at 17:20
  • Now I rewrote it, and it looks very similar to yours :) I put a `console.log` line in my _get function and it *is rerunning* the publish function every time I `keyup` in the browser. Are you not seeing the same thing? – TimDog Jan 15 '13 at 17:22
  • I did this: `Meteor.publish("search_results", function(q) { console.log("Publish query is: " + q); result = _get(q); console.log("Publish result count is: " + result.count()); return result; });` - The query counts are returning correct-looking values, but the results list in the template doesn't refresh. Could it be something as simple as a template error? – mukmuk Jan 15 '13 at 17:30
  • hmmm. very strange...my template **is** refreshing. No js errors client side I assume? I'd bet that your publish function was always firing correctly, just the template wasn't refreshing. `Template.searchResults.results` should fire when the Session changes..what if you paste the above code into a new app and try? – TimDog Jan 15 '13 at 17:37
  • Ok, so I think the issue is that the collection I was working with was (correctly) generated outside of Meteor, but *Meteor doesn't properly support Mongo's ObjectIds!* Ugh. Context [here](https://github.com/meteor/meteor/issues/61) and [related Stackoverflow question](http://stackoverflow.com/questions/11979403/meteor-collection-update-with-traditional-id). – mukmuk Jan 15 '13 at 18:25
  • Aha. Thanks for posting that. I'll update my answer with what you finally found. – TimDog Jan 15 '13 at 19:11