parse.com search for partial string in array - javascript

I'm trying to search a Parse.com field which is an array for a partial string.
When the field is in String format I can do the following:
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.searchResults removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.busnumber contains[c] %#", searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.objects filteredArrayUsingPredicate:predicate]];
This works, however the new field I want to search in is an Array.
It works when I change the it to the following:
PFQuery * query = [PFQuery queryWithClassName:#"Bus"];
[query whereKey:#"route" equalTo:[NSString stringWithFormat:#"%#", searchText]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"Objects: %#", objects);
if (error)
{
NSLog(#"ERROR: %#", error.localizedDescription);
}
else
{
[self.searchResults removeAllObjects];
[self.searchResults addObjectsFromArray:objects];
[self.searchDisplayController.searchResultsTableView reloadData];
}}];
However I need the exact String for this.
I want to be able to search for parts of a string though, but when I change it to:
[query whereKey:#"route" containsString:[NSString stringWithFormat:#"%#", searchText]];
I get:
[Error]: $regex only works on string fields (Code: 102, Version: 1.7.4)
Any ideas? Thanks :)

What you've attempted is rational, but the string qualifiers on PFQuery work only on strings.
I've seen this theme frequently on SO: PFQuery provides only basic comparisons for simple attributes. To do anything more, one must query for a superset and do app level computation to reduce the superset to the desired set. Doing so is expensive for two reasons: app-level compute speed/space, and network transmission of the superset.
The first expense is mitigated and the second expense is eliminated by using a cloud function to do the app level reduction of the superset. Unless you need the superset records on the client anyway, consider moving this query to the cloud.
Specific to this question, here's what I think the cloud function would resemble:
// very handy to have underscore available
var _ = require('underscore');
// return Bus objects whose route array contains strings which contain
// the passed routeSubstring (request.params.routeSubstring)
Parse.Cloud.define("busWithRouteContaining", function(request, response) {
// for now, don't deal with counts > 1k
// there's a simple adjustment (using step recursively) to get > 1k results
var query = new Parse.Query("Bus");
query.find().then(function(buses) {
var matching = _.select(buses, function(bus) {
var route = bus.get("route");
var routeSubstring = request.params.routeSubstring;
return _.contains(route, function(routeString) {
return routeString.includes(routeSubstring);
});
});
response.success(matching);
}, function(error) {
response.error(error);
});
});
If you do decide to perform the reduction on the client and need help with the code, I can edit that in. It will be a pretty simple switch to predicateWithBlock: with a block that iterates the array attribute and checks rangeOfString: on each.

Related

Javascript object with arrays to search param style query string

Looking for clean way to convert a javascript object containing arrays as values to a search param compatible query string. Serializing an element from each array before moving to the next index.
Using libraries such as querystring or qs, converts the object just fine, but handles each array independently. Passing the resulting string to the server (which I cannot change) causes an error in handling of the items as each previous value is overwritten by the next. Using any kind of array notation in the query string is not supported. The only option I have not tried is a custom sort function, but seems like it would be worse than writing a custom function to parse the object. Any revision to the object that would generate the expected result is welcome as well.
var qs = require("qs")
var jsobj = {
origString:['abc','123'],
newString:['abcd','1234'],
action:'compare'
}
qs.stringify(jsobj,{encode:false})
qs.stringify(jsobj,{encode:false,indices:false})
qs.stringify(jsobj,{encode:false,indices:false,arrayFormat:'repeat'})
Result returned is
"origString=abc&origString=123&newString=abcd&newString=1234&action=compare"
Result desired would be
"origString=abc&newString=abcd&origString=123&newString=1234&action=compare"
I tried reorder your json:
> var jsobj = [{origString: 'abc', newString: 'abcd' }, {origString: '123',
newString: '1234' }, {action:'compare'}]
> qs.stringify(jsobj,{encode:false})
'0[origString]=abc&0[newString]=abcd&1[origString]=123&1[newString]=1234&2[action]=compare'
But I don't know if this is a good alternative for your problem.
Chalk this up to misunderstanding of the application. After spending some more time with the API I realized my mistake, and as posted above by others, order does no matter. Not sure why my first several attempts failed but the question is 'answered'

Building a progressive query in Mongoose based on user input

I'm writing a simple REST API that is basically just a wrapper around a Mongo DB. I usually like to use the following query params for controlling the query (using appropriate safeguards, of course).
_filter=<field_a>:<value_a>,<field_b><value_b>
some values can be prepended with < or > for integer greater-than/less-than comparison
_sort=<field_c>:asc,<field_d>:desc
_fields=<field_a>,<field_b>,<field_c>
_skip=<num>
_limit=<num>
Anyway, the implementation details on these are not that important, just to show that there's a number of different ways we want to affect the query.
So, I coded the 'filter' section something like this (snipping out many of the validation parts, just to get to point):
case "filter":
var filters = req.directives[k].split(',');
var filterObj = {};
for(var f in filters) {
// field validation happens here...
splitFields = filters[f].split(':');
if (/^ *>/.test(splitFields[1])) {
filterObj[splitFields[0]] = {$gt: parseInt(splitFields[1].replace(/^ *>/, ''), 10)};
}
else if (/^ *</.test(splitFields[1])) {
filterObj[splitFields[0]] = {$lt: parseInt(splitFields[1].replace(/^ *</, ''), 10)};
}
else {
filterObj[splitFields[0]] = splitFields[1];
}
}
req.directives.filter = filterObj;
break;
// Same for sort, fields, etc...
So, by the end, I have an object to pass into .find(). The issue I'm having, though, is that the $gt gets changed into '$gt' as soon as it's saved as a JS object key.
Does this look like a reasonable way to go about this? How do I get around mongoose wanting keys like $gt or $lt, and Javascript wanting to quote them?

Parse - Create an AND query of two OR queries

I need to do the following query in Parse.com (pseudo language):
select all posts where
(expired < today OR expired not exists)
AND
(owner is {user} OR commenter contains {user})
I know in Javascript I can create the two OR queries using Parse.Query.or, but since there doesn't seems to be a Parse.Query.and variant, I don't know how I can combine these two OR queries into a single query.
Other people have suggested to simply use equalTo multiple times to create AND statements, but this doesn't work for a composed OR query.
I'm not familiar with Parse and this is too long for a comment, but see below from the documentation; the last sentence in particular seems to be what you're after.
Compound Queries
If you want to find objects that match one of several queries, you can use Parse.Query.or method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do:
var lotsOfWins = new Parse.Query("Player");
lotsOfWins.greaterThan("wins", 150);
var fewWins = new Parse.Query("Player");
fewWins.lessThan("wins", 5);
var mainQuery = Parse.Query.or(lotsOfWins, fewWins);
mainQuery.find({
success: function(results) {
// results contains a list of players that either have won a lot of games or won only a few games.
},
error: function(error) {
// There was an error.
}
});
You can add additional constraints to the newly created Parse.Query that act as an 'and' operator.
You can use both AND with OR like this. Attributes in the AND being the equalTo valuables.
const mainQuery = Parse.Query.or(
Parse.Query.and(lotsOfWins, fewWins2),
Parse.Query.and(lotsOfWins2, fewWins)
);
You can add as many as you like..
const lotsOfWins = new Parse.Query("CLASS");
lotsOfWins.equalTo('user', CurrentUser);
const fewWins = new Parse.Query("CLASS");
fewWins.equalTo('recipient', CurrentUser);

Parse.com : Query Max Value of Column in JS API

If in sql, we can get it with:
select Max(Column_name) from Table_name
but still can't find simple way to get Max value of Column in Parse.com JS API.
Please explain me how to get Max Value of Column in JS API?
The best way to do this is to use Parse.Query (api) and order by descending, then obtain the first item in the result.
Edit:
Maybe, it's not a good idea to use order by in the situation that there are thousands(or even more) items as it's time consuming(complexity at least O(nlogn)). An alternative is to write a function of your own to choose the maximum value yourself with complexity of O(n).
Write a query for descending order and fetch the first object out of it. You will have the max value of the column there.
var query = new Parse.Query("something");
query.descending("column_name");
query.first({
success: function(result){
var max = result.get("column_name");
},
error: function(){
},
})
I am not sure if you can do it directly with a max function. You can create your own max function by doing a query to get all entries from that column and sorting them in descending order. There are APIs for this. Then choose the first value from the sorted list.
You can basically follow the same pattern from all other queries:
// Query for ID
ParseQuery<ParseObject> query = ParseQuery.getQuery("Table_Name");
// Condition
query.orderByDescending("ID");
// First object will be retrieved, this will be the max_value
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (object == null) {
Log.d("score", "The getFirst request failed.");
} else {
Log.d("score", "Retrieved the object.");
}
Now you can get the value using something like:
String mensagem = object.getString("ID");
Hopefully this will help you!

optimize search through large js string array?

if I have a large javascript string array that has over 10,000 elements,
how do I quickly search through it?
Right now I have a javascript string array that stores the description of a job,
and I"m allowing the user to dynamic filter the returned list as they type into an input box.
So say I have an string array like so:
var descArr = {"flipping burgers", "pumping gas", "delivering mail"};
and the user wants to search for: "p"
How would I be able to search a string array that has 10000+ descriptions in it quickly?
Obviously I can't sort the description array since they're descriptions, so binary search is out. And since the user can search by "p" or "pi" or any combination of letters, this partial search means that I can't use associative arrays (i.e. searchDescArray["pumping gas"] )
to speed up the search.
Any ideas anyone?
As regular expression engines in actual browsers are going nuts in terms of speed, how about doing it that way? Instead of an array pass a gigantic string and separate the words with an identifer.
Example:
String "flipping burgers""pumping gas""delivering mail"
Regex: "([^"]*ping[^"]*)"
With the switch /g for global you get all the matches. Make sure the user does not search for your string separator.
You can even add an id into the string with something like:
String "11 flipping burgers""12 pumping gas""13 delivering mail"
Regex: "(\d+) ([^"]*ping[^"]*)"
Example: http://jsfiddle.net/RnabN/4/ (30000 strings, limit results to 100)
There's no way to speed up an initial array lookup without making some changes. You can speed up consequtive lookups by caching results and mapping them to patterns dynamically.
1.) Adjust your data format. This makes initial lookups somewhat speedier. Basically, you precache.
var data = {
a : ['Ant farm', 'Ant massage parlor'],
b : ['Bat farm', 'Bat massage parlor']
// etc
}
2.) Setup cache mechanics.
var searchFor = function(str, list, caseSensitive, reduce){
str = str.replace(/(?:^\s*|\s*$)/g, ''); // trim whitespace
var found = [];
var reg = new RegExp('^\\s?'+str, 'g' + caseSensitive ? '':'i');
var i = list.length;
while(i--){
if(reg.test(list[i])) found.push(list[i]);
reduce && list.splice(i, 1);
}
}
var lookUp = function(str, caseSensitive){
str = str.replace(/(?:^\s*|\s*$)/g, ''); // trim whitespace
if(data[str]) return cache[str];
var firstChar = caseSensitive ? str[0] : str[0].toLowerCase();
var list = data[firstChar];
if(!list) return (data[str] = []);
// we cache on data since it's already a caching object.
return (data[str] = searchFor(str, list, caseSensitive));
}
3.) Use the following script to create a precache object. I suggest you run this once and use JSON.stringify to create a static cache object. (or do this on the backend)
// we need lookUp function from above, this might take a while
var preCache = function(arr){
var chars = "abcdefghijklmnopqrstuvwxyz".split('');
var cache = {};
var i = chars.length;
while(i--){
// reduce is true, so we're destroying the original list here.
cache[chars[i]] = searchFor(chars[i], arr, false, true);
}
return cache;
}
Probably a bit more code then you expected, but optimalisation and performance doesn't come for free.
This may not be an answer for you, as I'm making some assumptions about your setup, but if you have server side code and a database, you'd be far better off making an AJAX call back to get the cut down list of results, and using a database to do the filtering (as they're very good at this sort of thing).
As well as the database benefit, you'd also benefit from not outputting this much data (10000 variables) to a web based front end - if you only return those you require, then you'll save a fair bit of bandwidth.
I can't reproduce the problem, I created a naive implementation, and most browsers do the search across 10000 15 char strings in a single digit number of milliseconds. I can't test in IE6, but I wouldn't believe it to more than 100 times slower than the fastest browsers, which would still be virtually instant.
Try it yourself: http://ebusiness.hopto.org/test/stacktest8.htm (Note that the creation time is not relevant to the issue, that is just there to get some data to work on.)
One thing you could do wrong is trying to render all results, that would be quite a huge job when the user has only entered a single letter, or a common letter combination.
I suggest trying a ready made JS function, for example the autocomplete from jQuery. It's fast and it has many options to configure.
Check out the jQuery autocomplete demo
Using a Set for large datasets (1M+) is around 3500 times faster than Array .includes()
You must use a Set if you want speed.
I just wrote a node script that needs to look up a string in a 1.3M array.
Using Array's .includes for 10K lookups:
39.27 seconds
Using Set .has for 10K lookups:
0.01084 seconds
Use a Set.

Categories

Resources