Mongo query syntax error - regex

I don't understand why one of these syntaxs work and the other one doesn't. It's my understanding that they both pretty much mean the same.
This works
{ 'profile.fname' : { $regex: ".*" + this.queryParams.value + ".*", $options: '-i'}},
This does not work
{ profile : { fname : { $regex: ".*" + this.queryParams.value + ".*", $options: '-i'}}},
Example data structure looks like:
{
"_id":"ybhng3YCu4W4MSzz9",
"createdAt":"2016-08-23T10:44:33.088Z",
"emails":[{"address":"xy#z.co.uk","verified":false}],
"profile":
{
"fname":"name",
"lname":"otherName"
},
"roles":["admin"]
}
The first one produces the correct result but the second one produces nothing - as in an empty array. From debugging I know this must be the wrong syntax somewhere but I cannot see it.
I am using meteor as the server side.

If you are querying against an embedded document, you need to use dot notation like you're doing in your first query.
If you supply it a document, it must be an exact match. In this case you'd literally need:
{ profile: { fname: "name", lname :"otherName"} }
Of course this looks like bad design to me; why is profile an embedded document in the first place? Instead of something like this:
{
"_id":"ybhng3YCu4W4MSzz9",
"createdAt":"2016-08-23T10:44:33.088Z",
"emails":[{"address":"xy#z.co.uk","verified":false}],
"fname":"name",
"lname":"otherName"
"roles":["admin"]
}
Reference: Query on Embedded Documents

Related

How to write a regexp in elastisearch so that it gives me URLs with numbers?

I am trying to write a query in Kibana which works with Elastisearch Query DSL. The basic filter is as follows:
{
"query": {
"match": {
"path": {
"query": "/abc/",
"type": "phrase"
}
}
}
}
Now I need to write a query so that it gives me "path" which is of the form /abc/(0-9)/.
I tried the reference provided here but it does not make sense to me (I am not well versed with Elasticsearch):
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html
I would like to filter out results which are of the form path = /abc/12345/
This RegEx might help you to do so:
\x22query\x22:\s\x22(\/.*)\x22
It creates a target capturing group, where your desired output is and you might be able to call it using $1.
You may add additional boundaries to your pattern, if you wish, such as this RegEx:
\x22query\x22:\s\x22([\/a-z0-9]+)\x22

MongoDB: Aggregation using $cond with $regex

I am trying to group data in multiple stages.
At the moment my query looks like this:
db.captions.aggregate([
{$project: {
"videoId": "$videoId",
"plainText": "$plainText",
"Group1": {$cond: {if: {$eq: ["plainText", {"$regex": /leave\sa\scomment/i}]},
then: "Yes", else: "No"}}}}
])
I am not sure whether it is actually possible to use the $regex operator within a $cond in the aggregation stage. I would appreciate your help very much!
Thanks in advance
UPDATE: Starting with MongoDB v4.1.11, there finally appears to be a nice solution for your problem which is documented here.
Original answer:
As I wrote in the comments above, $regex does not work inside $cond as of now. There is an open JIRA ticket for that but it's, err, well, open...
In your specific case, I would tend to suggest you solve that topic on the client side unless you're dealing with crazy amounts of input data of which you will always only return small subsets. Judging by your query it would appear like you are always going to retrieve all document just bucketed into two result groups ("Yes" and "No").
If you don't want or cannot solve that topic on the client side, then here is something that uses $facet (MongoDB >= v3.4 required) - it's neither particularly fast nor overly pretty but it might help you to get started.
db.captions.aggregate([{
$facet: { // create two stages that will be processed using the full input data set from the "captions" collection
"CallToActionYes": [{ // the first stage will...
$match: { // only contain documents...
"plainText": /leave\sa\scomment/i // that are allowed by the $regex filter (which could be extended with multiple $or expressions or changed to $in/$nin which accept regular expressions, too)
}
}, {
$addFields: { // for all matching documents...
"CallToAction": "Yes" // we create a new field called "CallsToAction" which will be set to "Yes"
}
}],
"CallToActionNo": [{ // similar as above except we're doing the inverse filter using $not
$match: {
"plainText": { $not: /leave\sa\scomment/i }
}
}, {
$addFields: {
"CallToAction": "No" // and, of course, we set the field to "No"
}
}]
}
}, {
$project: { // we got two arrays of result documents out of the previous stage
"allDocuments" : { $setUnion: [ "$CallToActionYes", "$CallToActionNo" ] } // so let's merge them into a single one called "allDocuments"
}
}, {
$unwind: "$allDocuments" // flatten the "allDocuments" result array
}, {
$replaceRoot: { // restore the original document structure by moving everything inside "allDocuments" up to the top
newRoot: "$allDocuments"
}
}, {
$project: { // include only the two relevant fields in the output (and the _id)
"videoId": 1,
"CallToAction": 1
}
}])
As always with the aggregation framework, it may help to remove individual stages from the end of the pipeline and run the partial query in order to get an understanding of what each individual stage does.

How to find all the results containing a given substring with spaces in mongoose

I found many solutions to this problem but none works. Let's say that I have the following schema:
var Schema = new Schema ({
name : String,
url : String
});
And let's say that one of my entries is:
{
name : "Product and Services",
url : "www.randomurl.com"
}
I want to be able to retrieve this result passing a substring, for example " and services" or "product and" and so on. I tried the following (partialToSearch is the partial string):
Schema.find({name: { "$regex": partialToSearch, "$options": "i" }}, function(err, res){...});
And also tried the following:
Schema.find({name: new RegExp(partialToSearch, "i")}, function(err, res) {...});
Both of them work when I only pass "product" "and" or "services" but when I put a space and another word no result is found (res is empty).
Thanks!
It's most probably because you are not converting the whitespaces into "\s" (\s+ is better though) character like
"$regex": new RegExp(partialToSearch.replace(/\s+/g,"\\s+"), "gi");
I haven't had the chance to try it out with mongoose but i am pretty sure it's fine.
Your research is very useful I found my answer from your question and I just read Mongoose and MongoDb documentation and got answer
https://docs.mongodb.com/manual/reference/operator/query/regex/#op._S_regex
Vehicle.find({ registrationNo: { "$regex": registrationNo, "$options": "ix" }}, function (err, vehicle) {}
// { <field>: { $regex: /pattern/, $options: '<options>' } }
Just provided options (i) will ignore case and (x) will truncate spaces from pattern
Screenshot from MongoDb documentation:

node.js - sending regex to server

I've created a client side mongodb interface to talk to server side mongodb.
it's very similar to the mini-mongo implemented in the meteor.
here is an example:
model.find({"field": /search/}).exec(function(err, model){
construct(model);
});
now normally everything works fine except when I use the regex.
and I know what's the problem but I cannot fix it.
the problem, as you have guessed it, is when the regex /regexParameter/ when sent by ajax to server, is converted to "/regexParameter/" and the single quotes(or double) make the regex a normal string.
in the server I have something like this:
var findObject = req.query.findObject // {"field": "/search/"} :(
req.models[config.table]
.find(findObject)
.exec(function(err, model){
return res.json({
error: err,
result: model,
});
});
is there anything I can do to make this work without writing like 100 of lines of code that iterates through each of the findObject and matches every string for a regex...?
Thanks everyone
You are right - you cannot pass RegExp objects between client and server because during serialization they are converted to strings.
Solution? (or perhaps - a workaround)
Use $regex operator in your queries, so you don't need to use RegExp objects.
So this:
{
field: /search/
}
Becomes this:
{
field: {
$regex: 'search'
}
}
Or, giving a case-insensitive search example:
{
field: {
$regex: 'search',
$options: 'i'
}
}
(instead of field: /search/i)
Read more about $regex syntax here (including about some of its restrictions).

'like' or $regex query inside $cond in MongoDB

Please go through this question of mine:
MongoDB $group and explicit group formation with computed column
But this time, I need to compare strings, not numbers.The CASE query must have a LIKE:
CASE WHEN source LIKE '%Web%' THEN 'Web'
I then need to group by source. How to write this in Mongo? I am trying the following but not sure if $regex is supported inside $cond. By the way, is there a list of valid operators inside $cond somewhere? Looks like $cond isn't very fond of me :)
db.Twitter.aggregate(
{ $project: {
"_id":0,
"Source": {
$cond: [
{ $regex:['$source','/.* Android.*/'] },
'Android',
{ $cond: [
{ $eq: ['$source', 'web'] }, 'Web', 'Others'
] }
]
}
} }
);
There're many other values that I need to write in there, doing a deeper nesting. This is just an example with just 'Android' and 'Web' for the sake of brevity. I have tried both with $eq and $regex. Using $regex gives error of invalid operator whereas using $eq doesn't understand the regex expression and puts everything under 'Others'. If this is possible with regex, kindly let me know how to write it for case-insensitive match.
Thanks for any help :-)
Well, it still seems to be not even scheduled to be implemented :(
https://jira.mongodb.org/browse/SERVER-8892
I'm using 2.6 and took a peek on 3.0, but it's just not there.
There's one workaround though, if you can project your problem onto a stable substring. Then you can $substr the field and use multiple nested $cond. It's awkward, but it works.
Maybe you can try it with MapReduce.
var map = function()
{
var reg1=new RegExp("(Android)+");
var reg2=new RegExp("(web)+");
if (reg1.test(this.source)){
emit(this._id,'Android');
}
else if (reg2.test(this.source))
{
emit(this._id,'web');
}
}
var reduce = function (key,value){
var reduced = {
id:key,
source:value
}
return reduced;
}
db.Twitter.mapReduce(map,reduce,{out:'map_reduce_result'});
db.map_reduce_result.find();
You can use JavaScript regular expresions instead of MongoDB $regex.