MONGO DB Like Operator - javascript

I'm using JavaScript to query an API based on a Mongo DB.
I would need filter the result based on LIKE operator, something similar to
select * from playlist where title like '%a%'
At the moment I call this URL
var assetUrl = 'https://example.com/playlist?oauth_token=' + accessToken + '&account=XXX'+ '&fields={"title":true,"splash":true,"description":true,"source":true}'+ '&criteria={"title":/.*a.*/}';
With no success (Return 0 objects).
I would like to know if I should use Regular Expressions instead and how to use them in this context. Thanks

Yes, MongoDB supports regular expressions. You can read about it in the documentation. Here is an example:
db.collection.find( { url: /.*a.*/ } );
This finds all documents in the collection where the field "url" matches the regular expression. There is also an alternative syntax using the $regex operator:
db.collection.find( { url: { $regex: ".*a.*"} } );
Note that regular expressions are slow and scale badly. The search time is linear to the number of records in the collection, and indices only help when your regular expression begins with a begin-of-string anchor ^ (thanks, chx).
The documentation also has a chapter about Full Text Search in Mongo which recommends to split each string into an array of individual words, so that you can index it for faster lookup. This of course doesn't allow to search for word fragments, but greatly speeds up search for complete words.
Update: MongoDB 2.4 has a new experimental text-index feature which allows to speed up text-search with indices.
Update2: As of version 2.6, text search is enabled by default and ready for productive use.

var assetUrl = 'xxx.com/playlist?oauth_token=' + accessToken + '&account=xxx'+ '&fields='+escape('{"title":true,"splash":true,"description":true,"source":true}')+ '&criteria='+escape('{"title": {"$regex":".*ar.*"}}');
This solved my problem

In MongoDB Compass none of the proposed solutions worked, but I've had success with this queries:
Using RegExp object:
{email: RegExp('#gmail.com$', 'i')}
or like this for emails starting with foo.bar:
{ "email" : /^foo.bar.*/ }
or like this for email containing #gmail within:
{ "email" : /.*#gmail.*/ }

Related

CouchDB query issues

I will start off by saying while I am not new to CouchDB, I am new to querying the views using JavaScript and the web.
I have looked at multiple other questions on here, including CouchDB - Queries with params, couchDB queries, Couchdb query with AND operator, CouchDB Querying Dates, and Basic CouchDB Queries, just to list a few.
While all have good information in them, I haven't found one that has my particular problem in it.
I have a view set up like so:
function (docu) {
if(docu.status && docu.doc && docu.orgId.toString() && !docu.deleted){
switch(docu.status){
case "BASE":
emit(docu.name, docu);
break;
case "AIR":
emit(docu.eta, docu);
break;
case "CHECK":
emit(docu.checkTime, docu);
break;
}
}
}
with all documents having a status, doc, orgId, deleted, name, eta, and checkTime. (I changed doc to docu because of my custom doc key.
I am trying to query and emit based on a set of keys, status, doc, orgId, where orgId is an integer.
My jQuery to do this looks like so:
$.couch.db("myDB").view("designDoc/viewName", {
keys : ["status","doc",orgId],
success: function(data) {
console.log(data);
},
error: function(status) {
console.log(status);
}
});
I receive
{"total_rows":59,"offset":59,"rows":[
]}
Sometimes the offset is 0, sometimes it is 59. I feel I must be doing something wrong for this not to be working correctly.
So for my questions:
I did not mention this, but I had to set docu.orgId.toString() because I guess it parses the URL as a string, is there a way to use this number as a numeric value?
How do I correctly view multiple documents based on multiple keys, i.e. if(key1 && key2) emit(doc.name, doc)
Am I doing something obviously wrong that I lack the knowledge to notice?
Thank you all.
You're so very close. To answer your questions
When you're using docu.orgId.toString() in that if-statement you're basically saying: this value must be truthy. If you didn't convert to string, any number, other than 0, would be true. Since you are converting to a string, any value other than an empty string will be true. Also, since you do not use orgId as the first argument in an emit call, at least not in the example above, you cannot query by it at all.
I'll get to this.
A little.
The thing to remember is emit creates a key-value table (that's really all a view is) that you can use to query. Let's say we have the following documents
{type:'student', dept:'psych', name:'josh'},
{type:'student', dept:'compsci', name:'anish'},
{type:'professor', dept:'compsci', name:'kender'},
{type:'professor', dept:'psych', name:'josh'},
{type:'mascot', name:'owly'}
Now let's say we know that for this one view, we want to query 1) everything but mascots, 2) we want to query by type, dept, and name, all of the available fields in this example. We would write a map function like this:
function(doc) {
if (doc.type === 'mascot') { return; } // don't do anything
// allow for queries by type
emit(doc.type, null); // the use of null is explained below
// allow queries by dept
emit(doc.dept, null);
// allow for queries by name
emit(doc.name, null);
}
Then, we would query like this:
// look for all joshs
$.couch.db("myDB").view("designDoc/viewName", {
keys : ["josh"],
// ...
});
// look for everyone in the psych department
$.couch.db("myDB").view("designDoc/viewName", {
keys : ["psych"],
// ...
});
// look for everyone that's a professor and everyone named josh
$.couch.db("myDB").view("designDoc/viewName", {
keys : ["professor", "josh"],
// ...
});
Notice the last query isn't and in the sense of a logical conjunction, it's in the sense of a union. If you wanted to restrict what was returned to documents that were only professors and also joshs, there are a few options. The most basic would be to concatenate the key when you emit. Like
emit('type-' + doc.type + '_name-' + doc.name, null);
You would then query like this: key : ["type-professor_name-josh"]
It doesn't feel very proper to rely on strings like this, at least it didn't to me when I first started doing it, but it is a quite common method for querying key-value stores. The characters - and _ have no special meaning in this example, I simply use them as delimiters.
Another option would be what you mentioned in your comment, to emit an array like
emit([ doc.type, doc.name ], null);
Then you would query like
key: ["professor", "josh"]
This is perfectly fine, but generally, the use case for emitting arrays as keys, is for aggregating returned rows. For example, you could emit([year, month, day]) and if you had a simple reduce function that basically passed the records through:
function(keys, values, rereduce) {
if (rereduce) {
return [].concat.apply([], values);
} else {
return values;
}
}
You could query with the url parameter group_level set to 1 or 2 and start querying by year and month or just year on the exact same view using arrays as keys. Compared to SQL or Mongo it's mad complicated and convoluted, but hey, it's there.
The use of null in the view is really for resource saving. When you query a view, the rows contain an _id that you can use in a second ajax call to get all the documents from, for example, _all_docs.
I hope that makes sense. If you need any clarification you can use the comments and I'll try my best.

mongodb - find and replace partial string across various fields

Say I have a URL: aaa.something.com/id that is found in several collections, in many different fields.
I would like to change it to bbb.something.com/id via regex (or similar) to find and replace only the prefix of the URL string.
The following:
db.tests.find({ "url": /^aaa\.something\.com\// }).forEach(function(doc) {
doc.url = doc.url.replace(/^aaa\.something\.com\//, "bbb.something.com/");
db.tests.update({ "_id": doc._id },{ "$set": { "url": doc.name } });
});
assumes that the field is always known to be url.
But in the database, The URL could be found in a number of locations such as:
content.photo
content.media
content.media[i].data
avatar
url
You can a wildcard text index and then use $text to find documents which match the specified regex. Once you get these docs you can write Javascript code for finding keys which match your regex and replacing them as needed.

Mongo get inverse of query

Hello I'm having trouble trying to understand how to write this query
My collection is a series of entries like this:
{
id:1,
name:"peter",
number:3
}
I want to be able to write a query which will return all items except for documents where the name='peter' and number=3
I know I can write something like:
db.test.find({$and:[{'name':'peter'},{'num':3}]})
to return all matching items, but is there any way of rewriting this query to return everything but the matching elements?
The $not operator requires a field to be bound to , but in this case it wont work.
Basically I had to rethink my query, using DeMorgan's law
¬(A^B) = ¬(A)V¬(B)
NOT (A AND B) = NOT(A) OR NOT(B)
so my query is
db.test.find({ $or:[{name:{$not:{$eq:'peter'}}},{num:{$not:{$eq:3}}}]});
Boolean algebra to the rescue!!
You can use a trick involving $nor with only one statement. Your only statement is then the original query. This works because $nor means that all conditions must be false; if you have only one condition, you get the negation.
So try:
db.test.find({$nor:[{$and:[{'name':'peter'},{'num':3}]}]})
I think this is nice because it's the negation of your original query exactly as it was
You can use the $ne operator to test for not equality:
find({$and: [{name: {$ne: peter}}, {num: {$ne: 2}}]})

How can I check for word similarity from a list in JavaScript (without looping through an entire dictionary)?

I'm trying to write a spell-check script. Let's say I have a keyed list of 10 English words:
var wordList = {
"moas" : "moas",
"moat" : "moat",
"moated" : "moated",
"moating" : "moating",
"moatlike" : "moatlike",
"moats" : "moats",
"mob" : "mob",
"mobbed" : "mobbed",
"mobber" : "mobber",
"mobbers" : "mobbers",
}
and one misspelled word: "motelike" which I want to correct from the word list by finding the most similar word. If the closest word has a similarity above a certain threshold, I'll replace it.
I could put something together that loops through all of the words and checks each letter for a match, but that would be monstrously expensive to process when my dictionary is > 200,000 items). I think there must be a way to target the word's possible matches more efficiently than looping the whole array with the advantage of having a keyed list.
I can't think of how to go about doing this. It seems like it shouldn't be that hard, but I'm drawing a blank on how to get it done. Maybe something with regex involved?
The keyword you're looking for is fuzzy string searching. There are many libraries for that, for example fuzzyset.js. You use it like this:
f = FuzzySet(['moas', 'moat', 'moated', 'moating', 'moatlike', 'moats', 'mob', 'mobbed', 'mobber', 'mobbers']);
f.get('moateb');
// returns [[0.8333333333333334, 'moated']]
// (array of pairs [score, match])
Of course, you can implement it yourself instead of using a library. This wikipedia article is about this problem.

How do I find records in Azure table storage that don't match an array of values?

I'm trying to perform a 'doesNotContainAllObjectsInArray' type operation on Azure Mobile Services. For example, let's say I have a table called Number and within that table are these records with these 'number' values: 11111, 22222, 33333, 44444.
I want to be able to write a query that will allow me to pass in an array of numbers that I specifically don't want, for example: [11111,44444] should yield me with [22222, 33333].
I've tried using JavaScript in my where operator, but I'm getting an error back stating that the expression isn't supported. This is what I've tried:
var numberTable = tables.getTable('Number');
var ignoreNumbers = ['11111', '44444'];
numberTable.where(function(numbers) {
return (numbers.indexOf(this.number) > -1);
}, ignoreNumbers).read({
success: function(foundNumbers) {
console.log('Found ' + foundNumbers.length + ' numbers!');
},
error: function(error) {
console.error('Error with query! ' + error);
}
});
Note: I can't hard code the ignoreNumbers values, since that array is produced from a previous query.
Can anyone recommend how I might go about executing a query like this? Would I need build a SQL statement and execute it with mssql? (...is that even possible with Table Storage?)
You are describing the SQL Except operator which isn't supported in Table Queries. The only way I've found to do this is to load the table into memory (often not feasible due to size) and then use LINQ to do an Except query.
I managed to solve this by creating a SQL query and executing it through the request.service.mssql object, something like this:
SELECT * FROM Number WHERE (number != '11111' && number != '22222')
The WHERE part of the query is built by iterating the ignoreNumbers array and building the SQL statement by string concatenation.
Not sure if it's the most efficient thing in the world, but in reality there are only going to be a couple of numbers (maybe 5-10) and so far it seems to work.

Categories