Meteor Publish/Subscribe passing object with string parameter issue - meteor

I am trying to pass a object { key:value} and send it to meteor publish so i can query to database.
My Mongo db database has (relevant datas only) for products:
products : {
categs:['Ladies Top','Gents'],
name : Apple
}
In meteor Publish i have the following:
Meteor.publish('product', (query) =>{
return Clothings.find(query);
})
In client i use the following to subscribe:
let query = {categs:'/ladies top/i'}; // please notice the case is lower
let subscribe = Meteor.subscribe('product',query);
if (subscribe.ready()){
clothings = Products.find(query).fetch().reverse();
let count = Products.find(query).fetch().reverse().length; // just for test
}
The issue is, when i send the query from client to server, it is automatically encoded eg:
{categs:'/ladies%top/i'}
This query doesnot seem to work at all. There are like total of more than 20,000 products and fetching all is not an option. So i am trying to fetch based on the category (roughly around 100 products each).
I am new to ,meteor and mongo db and was trying to follow existing code, however this doesnot seem to be correct. Is there a better way to improve the code and achieve the same ?
Any suggestion or idea is highly appreciated.
I did go through meteor docs but they dont seem to have examples for my scenario so i hope someone out there can help me :) Cheers !

Firstly, you are trying to send a regex as a parameter. That's why it's being encoded. Meteor doesn't know how to pass functions or regexes as parameters afaict.
For this specific publication, I recommend sending over the string you want to search for and building the regex on the server:
client:
let categorySearch = 'ladies top';
let obj = { categorySearch }; // and any other things you want to query on.
Meteor.subscribe('productCategory',obj);
server:
Meteor.publish('productCategory',function(obj){
check(obj,Object);
let query = {};
if (obj.categorySearch) query.category = { $regex: `/${obj.categorySearch}/i` };
// add any other search parameters to the query object here
return Products.find(query);
});
Secondly, sending an entire query objet to a publication (or Method) is not at all secure since an attacker can then send any query. Perhaps it doesn't matter with your Products collection.

Related

How to do pattern searching in fire base real time DB [duplicate]

I am using firebase for data storage. The data structure is like this:
products:{
product1:{
name:"chocolate",
}
product2:{
name:"chochocho",
}
}
I want to perform an auto complete operation for this data, and normally i write the query like this:
"select name from PRODUCTS where productname LIKE '%" + keyword + "%'";
So, for my situation, for example, if user types "cho", i need to bring both "chocolate" and "chochocho" as result. I thought about bringing all data under "products" block, and then do the query at the client, but this may need a lot of memory for a big database. So, how can i perform sql LIKE operation?
Thanks
Update: With the release of Cloud Functions for Firebase, there's another elegant way to do this as well by linking Firebase to Algolia via Functions. The tradeoff here is that the Functions/Algolia is pretty much zero maintenance, but probably at increased cost over roll-your-own in Node.
There are no content searches in Firebase at present. Many of the more common search scenarios, such as searching by attribute will be baked into Firebase as the API continues to expand.
In the meantime, it's certainly possible to grow your own. However, searching is a vast topic (think creating a real-time data store vast), greatly underestimated, and a critical feature of your application--not one you want to ad hoc or even depend on someone like Firebase to provide on your behalf. So it's typically simpler to employ a scalable third party tool to handle indexing, searching, tag/pattern matching, fuzzy logic, weighted rankings, et al.
The Firebase blog features a blog post on indexing with ElasticSearch which outlines a straightforward approach to integrating a quick, but extremely powerful, search engine into your Firebase backend.
Essentially, it's done in two steps. Monitor the data and index it:
var Firebase = require('firebase');
var ElasticClient = require('elasticsearchclient')
// initialize our ElasticSearch API
var client = new ElasticClient({ host: 'localhost', port: 9200 });
// listen for changes to Firebase data
var fb = new Firebase('<INSTANCE>.firebaseio.com/widgets');
fb.on('child_added', createOrUpdateIndex);
fb.on('child_changed', createOrUpdateIndex);
fb.on('child_removed', removeIndex);
function createOrUpdateIndex(snap) {
client.index(this.index, this.type, snap.val(), snap.name())
.on('data', function(data) { console.log('indexed ', snap.name()); })
.on('error', function(err) { /* handle errors */ });
}
function removeIndex(snap) {
client.deleteDocument(this.index, this.type, snap.name(), function(error, data) {
if( error ) console.error('failed to delete', snap.name(), error);
else console.log('deleted', snap.name());
});
}
Query the index when you want to do a search:
<script src="elastic.min.js"></script>
<script src="elastic-jquery-client.min.js"></script>
<script>
ejs.client = ejs.jQueryClient('http://localhost:9200');
client.search({
index: 'firebase',
type: 'widget',
body: ejs.Request().query(ejs.MatchQuery('title', 'foo'))
}, function (error, response) {
// handle response
});
</script>
There's an example, and a third party lib to simplify integration, here.
I believe you can do :
admin
.database()
.ref('/vals')
.orderByChild('name')
.startAt('cho')
.endAt("cho\uf8ff")
.once('value')
.then(c => res.send(c.val()));
this will find vals whose name are starting with cho.
source
The elastic search solution basically binds to add set del and offers a get by wich you can accomplish text searches.
It then saves the contents in mongodb.
While I love and reccomand elastic search for the maturity of the project, the same can be done without another server, using only the firebase database.
That's what I mean:
(https://github.com/metaschema/oxyzen)
for the indexing part basically the function:
JSON stringifies a document.
removes all the property names and JSON to leave only the data
(regex).
removes all xml tags (therefore also html) and attributes (remember
old guidance, "data should not be in xml attributes") to leave only
the pure text if xml or html was present.
removes all special chars and substitute with space (regex)
substitutes all instances of multiple spaces with one space (regex)
splits to spaces and cycles:
for each word adds refs to the document in some index structure in
your db tha basically contains childs named with words with childs
named with an escaped version of "ref/inthedatabase/dockey"
then inserts the document as a normal firebase application would do
in the oxyzen implementation, subsequent updates of the document ACTUALLY reads the index and updates it, removing the words that don't match anymore, and adding the new ones.
subsequent searches of words can directly find documents in the words child. multiple words searches are implemented using hits
SQL"LIKE" operation on firebase is possible
let node = await db.ref('yourPath').orderByChild('yourKey').startAt('!').endAt('SUBSTRING\uf8ff').once('value');
This query work for me, it look like the below statement in MySQL
select * from StoreAds where University Like %ps%;
query = database.getReference().child("StoreAds").orderByChild("University").startAt("ps").endAt("\uf8ff");

How to get Iron-router query parameters in server hook

I am trying to add a referral system to my project, so currently I am basing it off of this package. The issue I am running into is my project only uses accounts-google and not accounts-password. The way this package works is it adds the iron router query parameters for the referrerCode (/register?r=ReferralCodeHere)through a preSignUpHook. I believe this only works with accounts-password wont work when creating an account with an API such as accounts-google.
My idea around this is to use a Meteor.users.before.insert hook to grab the iron router query parameters and insert them into my referrerCode field in Meteor.users since I'm already using Meteor Collection Hooks for a couple of other things.
The issue is I havent been able to find a way to get the query parameters on the server, I was hoping to do something like this:
Meteor.users.before.insert(function(userId, doc) {
doc.referrerCode = Referrer._referrerCode; // Link 1
});
(Link 1)
But this will just come up as undefined.
If I'm at my register page and it has a query like this for example: example.com/register?r=12345 Then I run Router.current().params.query.r on the client it returns 12345. Basically I just need to have that saved to the referralCode field in Meteor.users when a new user creates an account, if a referral code exists in the register URL.
I'm a bit lost with this one. I thought about setting it as a Session variable and then getting that in the before.insert hook, but that again only works on the client side. I'm thinking a meteor method might be best for this, but I'm not exactly sure how I would structure it. Any help is greatly appreciated!
Put the referral token into profile
Use that in your hook
Below I've copied some code that I've used before. It is built around an Invitations collection that tracks who invited who:
client:
var profile = {};
... any other profile settings you've captured
if ( token ) profile.referralToken = token;
Accounts.createUser({ email: email, password: password, profile: profile }, function(err){ ...})
hook:
if ( options.profile.referralToken ){ // referral case
var invitation = Invitations.findOne({ token: options.profile.referralToken });
if ( invitation )
user.invitationId = invitation._id; // the invitation used
user.invitedBy = invitation.userId; // the referring user
}
delete options.profile.referralToken;
}
return user;

Firebase and Angularfire nightmare migration for Update

I am new to firebase and I am having a bit of a nightmare trying to adapt old code to what is now deprecated and what is not. I am trying to write a function which updates one "single" record in my datasource using the now approved $save()promise but it is doing some really strange stuff to my data source.
My function (should) enables you to modify a single record then update the posts json array. However, instead of doing this, it deletes the whole datasource on the firebase server and it is lucky that I am only working with testdata at this point because everything would be gone.
$scope.update = function() {
var fb = new Firebase("https://mysource.firebaseio.com/Articles/" + $scope.postToUpdate.$id);
var article = $firebaseObject(ref);
article.$save({
Title: $scope.postToUpdate.Title,
Body: $scope.postToUpdate.Body
}).then(function(ref) {
$('#editModal').modal('hide');
console.log($scope.postToUpdate);
}, function(error) {
console.log("Error:", error);
});
}
Funnily enough I then get a warning in the console "after" I click the button:
Storing data using array indices in Firebase can result in unexpected behavior. See https://www.firebase.com/docs/web/guide/understanding-data.html#section-arrays-in-firebase for more information. Also note that you probably wanted $firebaseArray and not $firebaseObject.
(No shit?) I am assuming here that $save() is not the right call, so what is the equivalent of $routeParams/$firebase $update()to do a simple binding of the modified data and my source? I have been spending hours on this and really don't know what is the right solution.
Unless there's additional code that you've left out, your article $firebaseObject should most likely use the fb variable you created just before it.
var article = $firebaseObject(fb);
Additionally, the way in which you're using $save() is incorrect. You need to modify the properties on the $firebaseObject directly and then call $save() with no arguments. See the docs for more.
article.Title = $scope.postToUpdate.Title;
article.Body = $scope.postToUpdate.Body;
article.$save().then(...

When I connect to firebase, I only see the structures and no devices (Nest API)

I am trying to read basic information about thermostats using the methods in the thermostat control example (https://developer.nest.com/documentation/control), but when I connect to firebase I only see the structure object (which only contains name, away, smoke_co_alarms, structure_id and thermostats) in the snapshot– There is no devices object. I am connecting to firebase using
var nestToken = $.cookie('nest_token');
var dataRef = new Firebase('wss://developer-api.nest.com/');
dataRef.auth(nestToken);
I tried to connect directly to devices using wss://developer-api.nest.com/devices, but that only returns an undefined data-structure.
I've also tried connecting to firebase using https://developer-api.nest.com/ and https://developer-api.nest.com/, but they raised an authorization error and caused my javascript to go into an infinite loop sending requests.
I'm reading data using:
dataRef.on('value', function (snapshot) {
var data = snapshot.val();
structure = firstChild(data.structures);
console.log(data);
console.log(data.structures);
console.log(data.devices);
console.log(data.devices.thermostats);
console.log(structure.thermostats);
};
Lastly, I tried it on an account with real devices and one with virtual devices, so I know that couldn't be causing it (even though I didn't expect it to).
Any ideas what I am doing wrong? The issue couldn't be in my App.js file, could it? Is there some configuration I need to do on the user's end in addition to the authentication? I get the feeling it's probably something really simple that's staring me in the face.
So I figured it out: It's a permissions issue. When my client-profile was setup, it only requested permission to read the away/home status. So when I query Firebase it only returns the a snapshot with structure because that is where the away/home status can be read. So, in summary, if you're not seeing the devices structure, even though devices are associated with the user, check your client permissions.
Using (some of) your code, I have no trouble seeing the devices object:
var dataRef = new Firebase('wss://developer-api.nest.com');
dataRef.auth(nestTokenLive);
dataRef.on('value', function (snapshot) {
var data = snapshot.val();
console.log(data);
console.log(data.devices);
});
Results in:
> Object {devices: Object, structures: Object}
> Object {thermostats: Object}

Firebase "Where" like search

Tryng to get a simple result using "Where" style in firebase but get null althe time, anyone can help with that?
http://jsfiddle.net/vQEmt/68/
new Firebase("https://examples-sql-queries.firebaseio.com/messages")
.startAt('Inigo Montoya')
.endAt('Inigo Montoya')
.once('value', show);
function show(snap) {
$('pre').text(JSON.stringify(snap.val(), null, 2));
}
Looking at the applicable records, I see that the .priority is set to the timestamp, not the username.
Thus, you can't startAt/endAt the user's name as you've attempted here. Those are only applicable to the .priority field. These capabilities will be expanding significantly over the next year, as enhancements to the Firebase API continue to roll out.
For now, your best option for arbitrary search of fields is use a search engine. It's wicked-easy to spin one up and have the full power of a search engine at your fingertips, rather than mucking with glacial SQL-esque queries. It looks like you've already stumbled on the appropriate blog posts for that topic.
You can, of course, use an index which lists users by name and stores the keys of all their post ids. And, considering this is a very small data set--less than 100k--could even just grab the whole thing and search it on the client (larger data sets could use endAt/startAt/limit to grab a recent subset of messages):
new Firebase("https://examples-sql-queries.firebaseio.com/messages").once('value', function(snapshot) {
var messages = [];
snapshot.forEach(function(ss) {
if( ss.val().name === "Inigo Montoya" ) {
messages.push(ss.val());
}
});
console.log(messages);
});
Also see: Database-style queries with Firebase

Resources