If you are new to search source, it's a good idea to look at this introductory article on MeteorHacks.
meteor add meteorhacks:search-source
var options = {
keepHistory: 1000 * 60 * 5,
localSearch: true
};
var fields = ['packageName', 'description'];
PackageSearch = new SearchSource('packages', fields, options);
- First parameter for the source is the name of the source itself. You need to use it for defining the data source on the server.
- second arguments is the number of fields to search on the client (used for client side search and text transformation)
- set of options. Here are they
keepHistory
- cache the search data locally. You need to give an expire time(in millis) to cache it on the client. Caching is done based on the search term. Then if you search again for that term, it search source won't ask the server to get data again.localSearch
- allow to search locally with the data it has.
In the server, get data from any backend and send those data to the client as shown below. You need to return an array of documents where each of those object consists of _id
field.
Just like inside a method, you can use
Meteor.userId()
andMeteor.user()
inside a source definition.
SearchSource.defineSource('packages', function(searchText, options) {
var options = {sort: {isoScore: -1}, limit: 20};
if(searchText) {
var regExp = buildRegExp(searchText);
var selector = {packageName: regExp, description: regExp};
return Packages.find(selector, options).fetch();
} else {
return Packages.find({}, options).fetch();
}
});
function buildRegExp(searchText) {
// this is dumb implementation
var parts = searchText.trim().split(' ');
return new RegExp("(" + parts.join('|') + ")", "ig");
}
You can get the reactive data source with the PackageSearch.getData
api. This is an example usage of that:
Template.searchResult.helpers({
getPackages: function() {
return PackageSearch.getData({
transform: function(matchText, regExp) {
return matchText.replace(regExp, "<b>$&</b>")
},
sort: {isoScore: -1}
});
}
});
.getData()
api accepts an object with options (and an optional argument to ask for a cursor instead of a fetched array; see example below). These are the options you can pass:
transform
- a transform function to alter the selected search texts. See above for an example usage.sort
- an object with MongoDB sort specifierslimit
- no of objects to limitdocTransform
- a transform function to transform the documents in the search result. Use this for computed values or model helpers. (see example below)
Template.searchResult.helpers({
getPackages: function() {
return PackageSearch.getData({
docTransform: function(doc) {
return _.extend(doc, {
owner: function() {
return Meteor.users.find({_id: this.ownerId})
}
})
},
sort: {isoScore: -1}
}, true);
}
});
Finally we can invoke search queries by invoking following API.
PackageSearch.search("the text to search");
You can provide options to limit your search query and implement pagination into your search: skip
and limit
. SearchSource will also sanitize your limiting options to only fetch data from you backend that is not already in history (that is, if you enabled local history caching by configuring a value for keepInHistory).
PackageSearch.search("the text to search", {
skip: 0,
limit: 12
});
For example, when you do the following subsequent searches with the same text string to seacrh for
[
{skip: 0, limit: 24},
{skip: 0, limit: 36},
{skip: 12, limit: 36},
{skip: 24, limit: 24},
];
SearchSource will send the following requests on your backend (when keepInhistory is enabled):
[
{skip: 0, limit: 24},
{skip: 24, limit: 12},
{skip: 36, limit: 12}
];
To see an elaborate example on how to implement a paged search application, go to (http://infinitesearch.meteor.com).
You can get the status of the search source by invoking following API. It's reactive too.
var status = PackageSearch.getStatus();
Status has following fields depending on the status.
- loading - indicator when loading
- loaded - indicator after loaded
- error - the error object, mostly if backend data source throws an error
With metadata, you get some useful information about search along with the search results. These metadata can be time it takes to process the search or the number of results for this search term.
You can get the metadata with following API. It's reactive too.
var metadata = PackageSearch.getMetadata();
Now we need a way to send metadata to the client. This is how we can do it. You need to change the server side search source as follows
SearchSource.defineSource('packages', function(searchText, options) {
var data = getSearchResult(searchText);
var metadata = getMetadata();
return {
data: data,
metadata: metadata
}
});
You can get the current search query with following API. It's reactive too.
var searchText = PackageSearch.getCurrentQuery();
You can clear the stored history (if enabled the keepHistory
option) via the following API.
PackageSearch.cleanHistory();
Sometime, we don't need to fetch data from the server. We need to get it from a data source aleady available on the client. So, this is how we do it:
PackageSearch.fetchData = function(searchText, options, success) {
SomeOtherDDPConnection.call('getPackages', searchText, options, function(err, data) {
success(err, data);
});
};