1
votes

I've been able to implement a publish method in Meteor that runs a query to my mongo collection through a given attribute when subscribing in the template.js, which works fine but now I'd like to add a multiple attribute search in the same manner. So let's say I have a collection in Mongo where the documents all have the same attributes but with differing values.

{batch:'HAHT020614' color: 'blue', material: 'plastic', printing: true, 
  model: 'H100', handle: 'plastic', product: 'C010' }
{batch:'HBTH060614' color: 'red', material: 'metal', printing: false, 
  model: 'V400', handle: 'metal', product: 'P001' }
...

I'm trying to send an object to the publish method which contains all the user chosen fields through reactive vars:

Template.inventory.onCreated( function appBodyOnCreated() {
    this.searchQuery = new ReactiveVar({
        color: anyItem,
        batch: anyItem,
        model: anyItem,
        material: anyItem,
        handle: anyItem,
        printing: anyItem,
        product: anyItem,
    });
    this.autorun(function () {
        let template = Template.instance();
        template.subscribe("stock.search", template.searchQuery.get());
    });
});

Then in publication.js:

Meteor.publish('stock.search', function stockQuery(search) {
  return Stock.find(
    { $and: [
      {color: { $regex : search.color }},
      {batch: { $regex : search.batch}},
      {product: { $regex : search.product}},
      {model: { $regex : search.model}},
      {material: { $regex : search.material}},
      {handle: { $regex : search.handle}},
      {printing: { $regex : search.printing}}
      ]
    }, 
    { limit: 10, sort: { batch: 1 } });
});

The problem is that some of the search fields will or not be used in the application depending on the user's needs, looking for it to be possible to search all items that are for example both blue and made or metal, and just mix and match whatever is necessary to find.

The object reaches the publish method correctly and I'm able to extract the attributes but the problem resides in the query, as I don't know if it's possible to ask for Mongo to match certain attributes to "any". I've tried to pass on { $exists: true } as a default (and when search field empty) attribute so it matches with any of the documents in the collection but the query doesn't seem to be returning correctly. In this case I'm using regex as some sort of "contains" while the var anyItem is just an empty string.

Is there a proper way to query mongo to match only certain attributes to chosen values while the others stay as "any"?

1

1 Answers

1
votes

You could pass only the non-null criteria to the publish method, and build a query with only the given criteria like this:

Meteor.publish('stock.search', function stockQuery(search) {
   const criteria = Object.keys(search).map(k => ({ [k]: { $regex: search[k] } }));
   return Stock.find(
       { $and: criteria }, 
       { limit: 10, sort: { batch: 1 } }
   );
});