Optional search parameters with mongoose? - javascript

I'm an inexperienced web developer and I'm having trouble with making an API.
This API should be able to take these parameters; all parameters should be optional (which are title, categories) and I should be able to limit the number of results I get i.e. where to start the results and how many results I should return.
app.get('/images', function(req, res) {
// var searchKey = req.query.keyword;
console.log("hello!");
var regex = new RegExp(req.query.q, 'i');
return Image.find({
title: regex
},function(err, q) {
console.log("do this");
return res.send(q);
});
});
For example, say we have 10 images with title "Cats A", "Cats B", etc and categories as empty ( [ ] ). We then want start from result 3 and display 6 results.
This is my code, and I'm not sure how to add the extra functionalities in.

You can build up your query as needed based on the supplied parameters. I'm guessing a bit with how the parameters are provided, but here's an example:
// Build up the query conditions based on the supplied parameters (if any).
var conditions = {};
if (req.query.q) {
// Match the query string against the either the title or categories fields
var regx = new RegExp(req.query.q, 'i');
conditions.$or = [
{title: regx},
{categories: regx}
];
}
var query = Image.find(conditions);
// Support optional skip and limit parameters.
if (req.query.skip) {
query = query.skip(req.query.skip);
}
if (req.query.limit) {
query = query.limit(req.query.limit);
}
// Execute the assembled query.
return query.exec(function(err, q) {
console.log("do this");
return res.send(q);
});

Related

Validating list of email extention set as JSON in JavaScript

How can I validate user inputted email and check their extension with a list of email extensions in a JSON?
Like if I type abc#efg.com its going to check only the email extension which is #efg.com in a list of JSON.
OR a regex that will only get the values after "#" and ignore anything before that.
[
{
"School": "Ivy Tech Community College",
"Email": "ivytech.edu"
},
{
"School": "Liberty University",
"Email": "liberty.edu"
},
{
"School": "Miami Dade College",
"Email": "mdc.edu"
},
{
"School": "Lone Star College",
"Email": "lonestar.edu"
},
{
"School": "Ashford University",
"Email": "ashford.edu"
}
]
// initial data
var data = '[ {"School":"Ivy Tech Community College","Email":"ivytech.edu"},' + '{"School":"Liberty University","Email":"liberty.edu"},' + '{"School":"Miami Dade College","Email":"mdc.edu"},' + '{"School":"Lone Star College","Email":"lonestar.edu"},' + '{"School":"Ashford University","Email":"ashford.edu"} ]';
// json-ify our data
var jsonData = JSON.parse(data);
// map the values of each JSON 'Email' property from jsonData in an array
var emailsArray = jsonData.map(function (x) { return x.Email; });
// email address for testing
var testEmail = "john#liberty.edu";
// split the email address by the "#" character and use the second part (domain)
if (arrayContains(testEmail.split("#")[1], emailsArray))
{
// this will fire as john#liberty.edu matches liberty.edu in emailsArray
console.log("emailsArray contains domain");
}
else
{
console.log("emailsArray does not contain domain");
}
// function to check if an item is contained in an array
function arrayContains(item, array)
{
return (array.indexOf(item) > -1);
}
Complete JSFiddle example here.
Notes:
you can ignore the first two lines of code as I'm guessing you're getting your JSON data from a web response
an assumption is being made that testEmail adheres to the format of an email address; you might need to implement some kind of validation to verify that the string being input is an actual email
we split testEmail by the # character and get the second part of the result (which will be at index 1, since arrays are zero-based) using String.prototype.split()
the emailsArray array is created using the Array.prototype.map() function
arrayContains uses the String.prototype.indexOf() method to check if testEmail exists in emailsArray
I think I've clarified what every line of code in the example does. You can now take it and adjust it to your own requirements—and even better, improve it.
Instead of regex, you can iterate through the array of schools, and match the domain like this:
var schools = [
{"School":"Ivy Tech Community College","Email":"ivytech.edu"},
{"School":"Liberty University","Email":"liberty.edu"},
{"School":"Miami Dade College","Email":"mdc.edu"},
{"School":"Lone Star College","Email":"lonestar.edu"},
{"School":"Ashford University","Email":"ashford.edu"}
]
function validate(email) {
var domain = email.split('#').pop();
for(var i = 0; i < schools.length; i++) {
if(domain === schools[i].Email) return schools[i].School;
}
return 'Domain Not Found';
}
You can replace schools[i].School with true and 'Domain Not Found' with false to just check with it exists.
Example: https://jsfiddle.net/TheQueue841/gwhkq520/
Something like trashrOx mentioned would work:
var edus = [
{"School":"Ivy Tech Community College","Email":"ivytech.edu"},
{"School":"Liberty University","Email":"liberty.edu"},
{"School":"Miami Dade College","Email":"mdc.edu"},
{"School":"Lone Star College","Email":"lonestar.edu"},
{"School":"Ashford University","Email":"ashford.edu"}
];
var emails = ['matty.fake#lonestar.edu','himom#mdc.edu','randomguy#yahoo.com'];
emails.forEach(function(element,index) {
var domain = element.substring(element.indexOf('#') + 1);
var match = 'none';
if (domain) {
edus.forEach(function(element,ind) {
if (element.Email === domain) {
match = element.School;
}
});
console.log(element + ' matched ' + match);
}
});
// matty.fake#lonestar.edu matched Lone Star College
// himom#mdc.edu matched Miami Dade College
// randomguy#yahoo.com matched none
Use Array.prototype.some() if you only want to validate.
some() executes the callback function once for each element present in the array until it finds one where callback returns a truthy value (a value that becomes true when converted to a Boolean). If such an element is found, some() immediately returns true. Otherwise, some() returns false. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
var schools = [
{"School":"Ivy Tech Community College","Email":"ivytech.edu"},
{"School":"Liberty University","Email":"liberty.edu"},
{"School":"Miami Dade College","Email":"mdc.edu"},
{"School":"Lone Star College","Email":"lonestar.edu"},
{"School":"Ashford University","Email":"ashford.edu"}
]
function validate(email) {
var domain = email.split('#').pop();
return schools.some(function(school) {
return school.Email === domain;
});
}
validate('test#ashford.edu');

Parse Multiple doesNotMatchKeyInQuery

I am having a problem using Parse queries in javascript. I want to try and use multiple doesNotMatchKeyInQuery functions but it only allows the last one to be used. Any ideas how I can make code like this work? Ignore the errors that might exist in other parts of the code. I wrote this as an example
//Query 1
var Class1 = Parse.Object.extend("Class1");
var class1Query = new Parse.Query(Class1);
class1Query.equalTo("id", id1);
//Query 2
var Class2 = Parse.Object.extend("Class2");
var class2Query = new Parse.Query(Class2);
class2Query.equalTo("id", id2);
//Query 3
var Class3 = Parse.Object.extend("Class3");
var class3Query = new Parse.Query(Class3);
class3Query.equalTo("id", id3);
//Bringing it all together
var finalQuery = new Parse.Query("User");
//This is the part below I am talking about
finalQuery.doesNotMatchKeyInQuery("objectId", "id1", class1Query);
finalQuery.doesNotMatchKeyInQuery("objectId", "id2", class2Query);
finalQuery.doesNotMatchKeyInQuery("objectId", "id3", class3Query);
finalQuery.find({
success: function (results) {
response.success(results);
},
error: function (error) {
response.error(error);
}
});
It's not possible to do such a complex query in a single request. However, you can fetch the keys you don't want to match ahead of time, and construct a secondary query from that.
I've written up an example based upon your code above:
// Assuming we're actually dealing with 3 different classes,
// and these can't be combined into a single query
var class1Query = new Parse.Query('Class1');
class1Query.equalTo('id', id1);
var class2Query = new Parse.Query('Class2');
class2Query.equalTo('id', id2);
var class3Query = new Parse.Query('Class3');
class3Query.equalTo('id', id3);
// Fetch the results from all three queries simultaneously
Parse.Promise.when([
class1Query.find(),
class2Query.find(),
class3Query.find()
]).then(function(results) {
// results will contain three arrays of results
// We can now build a query where the objectId is not equal
// to any of the objectIds of the results
var ids = [];
results.forEach(function(set) {
set.forEach(function(obj) {
ids.push(obj.id);
});
});
return new Parse.Query('FinalClass').notContainedIn('objectId', ids).find();
})
I want to caution you that this query will not be efficient for large sets of data. "Does not equal" queries are never fast, because they have to loop over every object in the table. If there is another way to get your data, I highly encourage it.

retrieve unique values for key from parse.com when class has > 1000 objects

In parse I have a class named "TestScore". Each object has a key named "quizName".
I need to get an array of unique "quizName" values. I wrote the below which queries the "TestClass" and loops through the results looking for unique "quizName" values.
At first seemed to do the job. But then I realized that the maximum number of returned objects is 1000. Soon there will be more than 1000 objects stored which means that this method will not guarantee that I end up will all values.
function loadTests(){
//create an array to hold each unique test name as we find them
var uniqueEntries = [];
//query parse to return TestScore objects
var TestScore = Parse.Object.extend("TestScore");
var query = new Parse.Query(TestScore);
query.limit(1000) //added this after realizing that the default query limit is only 100
query.find({
success: function(testScore) {
$(testScore).each(function(index, score) {
//here I loop though all of the returned objects looking at the "quizName" for each
if($.inArray(score.get("quizName"), uniqueEntries) === -1) {
//if the quiz name is not already in the "uniqueEntries" array, I add it to the array
uniqueEntries.push(score.get("quizName"));
}
});
//do stuff with quiznames here...., add them as options in select boxes mostly
}
});
}
I looked at {Parse.Query} notContainedIn(key, values) which looks promising but cant figure out if I can add values to the array as I find them. It seems like I would have to have an array to start with (defeating the whole point.)
This part of the docs "{Parse.Query} Returns the query, so you can chain this call." makes me think I might be able to chain queries together to get what I need, but that doesn't seem very efficient.
How can I retrieve unique values for key "quizName" when my class has > 1000 objects?
I'm sure you're long past this by now, but only way I know of to do it is to use one query after another by using a .skip(#) value for each query. So get 1000, then query again with .skip(1000), concatenate the items from the first list and second, then query again with .skip(2000), etc...
Be aware that I think there's a limit on skip values of 10,000. Don't take my word on that, just pointing you to something that I think is right that you should confirm if you think it applies to your situation.
I eventually found a tutorial online that I was able to modify and came up with the below. This effectively sets the return limit to 10,000 instead of 1,000 and allows setting several different parameters for the query.
My changes could surely be written better, maybe as an options object or similar but it works for my needs.
You can see a working demo here
function getStuff(){
// here we will setup and call our helper functions with callbacks to handle the results
var scheme =['SOTest',true]; // return all objects with value `true` in the `SOTest` column
// var scheme =['descending','createdAt']; // return all objects with sort order applied
// var scheme =''; // or just return all objects
// see `findChunk()` below for more info
var Remark = Parse.Object.extend("Remark");
schemePromise(Remark, scheme).done(function (all) {
console.log('Found ' + all.length+' Remarks');
$.each( all, function(i, obj){
$('#test').append(obj.get('Remark') +'<br>');
});
})
.fail(function (error) {
console.log("error: " + JSON.stringify(error));
});
}
getStuff(); // call our function
// helper functions used to get around parse's 1000 query limit
// raises the limit to 10,000 by using promises
function findChunk(model, scheme, allData) {
// if `scheme` was an empty string, convert to an array
// this is the default and returns all objects in the called class
if(scheme==''){ ['scheme',''] };
// will return a promise
var limit = 1000;
var skip = allData.length;
var findPromise = $.Deferred();
var query = new Parse.Query(model);
// to get all objects from the queried Class then sort them by some column
// pass `scheme` as an array like [ sort method, column to sort ]
if (scheme[0]=='descending') query.descending(scheme[1]);
else if (scheme[0]=='ascending') query.ascending(scheme[1]);
// to limt results to objects that have a certain value in a specific column
// pass `scheme` as an array like [ column name, value ]
else query.equalTo(scheme[0], scheme[1]);
// more options can easily be built in here using `scheme`
query
.limit(limit)
.skip(skip)
.find()
.then(function (results) {
findPromise.resolve(allData.concat(results), !results.length);
}, function (results) {
findPromise.reject(error);
});
return findPromise.promise();
}
function schemePromise(model, scheme, allResults, allPromise) {
// find a scheme at a time
var promise = allPromise || $.Deferred();
findChunk(model, scheme, allResults || [])
.done(function (results, allOver) {
if (allOver) {
// we are done
promise.resolve(results);
} else {
// may be more
schemePromise(model, scheme, results, promise);
}
})
.fail(function (error) {
promise.reject(error);
});
return promise.promise();
}

How to read columns of type doubles in MDB files in node?

I'm querying some MDB files in nodejs on linux using MDBTools, unixodbc and the node odbc package.
Using this code
db.query("select my_str_col, my_dbl_col from my_table", function (err, rows) {
if (err) return console.log(err);
console.log(rows);
db.close();
});
I can query the my_str_col string column but I can't decipher the my_dbl_col Double column, I get something like this :
[ { my_str_col: 'bla', my_dbl_col: '{\u0014�Gai�#' },
{ my_str_col: 'bla bla', my_dbl_col: '' },
{ my_str_col: 'bla', my_dbl_col: '�G�z\u0014NF#' } ]
All not empty strings are 7 or 8 bytes but what bothers me most is the second row of this example where I get an empty string while I know there is a not null number in the MDB : it means I can't try to build the numbers from the string bytes.
So, how can I read numbers of type Double in a MDB file in node on linux ?
I precise that
a tool like MDBViewer (using MDBTools) correctly reads those numbers
JavaScript numbers will be precise enough for me : those numbers would all fit in float32
I can't apply lengthy conversions on the MDB files : I must make fast queries on a few hundred frequently changed files...
a solution in which I can't really issue queries but which lets me read the whole table would be acceptable too
As I couldn't get node-odbc to correctly decipher numbers, I wrote a function calling mdb-export (which is very fast) and reading the whole table.
var fs = require("fs"),
spawn = require('child_process').spawn,
byline = require('byline'); // npm install byline
// Streaming reading of choosen columns in a table in a MDB file.
// parameters :
// args :
// path : mdb file complete path
// table : name of the table
// columns : names of the desired columns
// read : a callback accepting a row (an array of strings)
// done : an optional callback called when everything is finished with an error code or 0 as argument
function queryMdbFile(args, read, done) {
var cmd = spawn('/usr/bin/mdb-export', [args.path, args.table]);
var rowIndex = 0, colIndexes;
byline(cmd.stdout).on('data', function (line) {
var cells = line.toString().split(',');
if (!rowIndex++) { // first line, let's find the col indexes
var lc = function(s){ return s.toLowerCase() };
colIndexes = args.columns.map(lc).map(function(name) {
return cells.map(lc).indexOf(name);
});
} else { // other lines, let's give to the callback the required cells
read(colIndexes.map(function(index){ return ~index ? cells[index] : null }));
}
});
cmd.on('exit', function (code) {
if (done) done(code);
});
}
Here's an example in which I build an array with all rows of the question's example :
var rows = [];
queryMdbFile({
path: "mydatabase.MDB",
table: 'my_table',
columns : ['my_str_col', 'my_dbl_col']
},function(row) {
rows.push(row);
},function(errorCode) {
console.log(errorCode ? ('error:'+errorCode) : 'done');
});
Everything is read as strings but easy to parse :
[ ['bla', '1324' ],
['bla bla', '332e+5'],
['bla', '43138' ] ]
Surprisingly enough, this is faster than querying using node-odbc and linuxodbc.

Implementing MongoDB 2.4's full text search in a Meteor app

I'm looking into adding full text search to a Meteor app. I know MongoDB now supports this feature, but I have a few questions about the implementation:
What's the best way to enable the text search feature (textSearchEnabled=true) in a Meteor app?
Is there a way to add an index (db.collection.ensureIndex()) from within your app?
How can you run a Mongo command (i.e. db.quotes.runCommand( "text", { search: "TOMORROW" } )) from within a Meteor app?
Since my goal is to add search to Telescope, I'm searching for a "plug-and-play" implementation that requires minimal command line magic and could even work on Heroku or *.meteor.com.
The simplest way without editing any Meteor code is to use your own mongodb. Your mongodb.conf should look something like this (on Arch Linux it is found at /etc/mongodb.conf)
bind_ip = 127.0.0.1
quiet = true
dbpath = /var/lib/mongodb
logpath = /var/log/mongodb/mongod.log
logappend = true
setParameter = textSearchEnabled=true
The key line is setParameter = textSearchEnabled=true, which, as it states, enables text search.
Start mongod up
Tell meteor to use your mongod not its own by specifying the MONGO_URL environmental variable.
MONGO_URL="mongodb://localhost:27017/meteor" meteor
Now say you have collection called Dinosaurs declared say in collections/dinosaurs.js
Dinosaurs = new Meteor.Collection('dinosaurs');
To create an text index for the collection create a file server/indexes.js
Meteor.startUp(function () {
search_index_name = 'whatever_you_want_to_call_it_less_than_128_characters'
// Remove old indexes as you can only have one text index and if you add
// more fields to your index then you will need to recreate it.
Dinosaurs._dropIndex(search_index_name);
Dinosaurs._ensureIndex({
species: 'text',
favouriteFood: 'text'
}, {
name: search_index_name
});
});
Then you can expose the search through a Meteor.method, for example in the file server/lib/search_dinosaurs.js.
// Actual text search function
_searchDinosaurs = function (searchText) {
var Future = Npm.require('fibers/future');
var future = new Future();
Meteor._RemoteCollectionDriver.mongo.db.executeDbCommand({
text: 'dinosaurs',
search: searchText,
project: {
id: 1 // Only take the ids
}
}
, function(error, results) {
if (results && results.documents[0].ok === 1) {
future.ret(results.documents[0].results);
}
else {
future.ret('');
}
});
return future.wait();
};
// Helper that extracts the ids from the search results
searchDinosaurs = function (searchText) {
if (searchText && searchText !== '') {
var searchResults = _searchEnquiries(searchText);
var ids = [];
for (var i = 0; i < searchResults.length; i++) {
ids.push(searchResults[i].obj._id);
}
return ids;
}
};
Then you can publish only documents that have been searched for in 'server/publications.js'
Meteor.publish('dinosaurs', function(searchText) {
var doc = {};
var dinosaurIds = searchDinosaurs(searchText);
if (dinosaurIds) {
doc._id = {
$in: dinosaurIds
};
}
return Dinosaurs.find(doc);
});
And the client side subscription would look something like this in client/main.js
Meteor.subscribe('dinosaurs', Session.get('searchQuery'));
Props to Timo Brinkmann whose musiccrawler project was the source of most this knowledge.
To create a text index and try to add like this I hope so it will be useful if there is still problem comment
From docs.mongodb.org:
Append scalar index fields to a text index, as in the following
example which specifies an ascending index key on username:
db.collection.ensureIndex( { comments: "text",
username: 1 } )
Warning You cannot include multi-key index field or geospatial index
field.
Use the project option in the text to return only the fields in the
index, as in the following:
db.quotes.runCommand( "text", { search: "tomorrow",
project: { username: 1,
_id: 0
}
}
)
Note: By default, the _id field is included in the result set. Since the example index did not include the _id field, you must
explicitly exclude the field in the project document.

Categories

Resources