Use $in operator to match regex in mongoengine - mongodb

Following the MongoDB documentation, you can use the $in operator with a regular expression like wise db.inventory.find( { tags: { $in: [ /^be/, /^st/ ] } } ). Is there a way to achieve the same result using mongoengine?
For example pass {"tags__in": ["/^be/", "/^st/"]} to my query?

I don't think this is supported by MongoEngine in normal query construct (i.e I doubt that Doc.objects(tags__in=["/^be/", "/^st/"] will work)) but there is support for __raw__ query (https://docs.mongoengine.org/guide/querying.html#raw-queries)
so the following should work
Inventory.objects(__raw__={"tags": { "$in":["/^be/", "/^st/"]}})

Related

Comparing field values in MongoDB

I wanted to run a query that finds the following document from MongoDB database
{field1:"foo",field2:"barfoobar"}
I know for equality I can use the following query:
.find({ $where : "this.field1 == this.field2" })
In C#
new BsonDocument("$where", new BsonJavaScript("this.field1 == this.field2"));
But how I can use regex in above query?
Somthing like:
.find({ $where : "this.field1 like this.field2" })
And in C# ?
Use the following query and check if it works for you. Let me know if it helped:
db.col.find( { $where: function() {
var regex = new RegExp(this.field1);
return (regex.test(this.field2))
} }).pretty();
The query above turns foo into a regex (something like /foo/) and checks if field2 matches the regex you passed.
FYI: The following statement was taken from mongodb website. Take it into consideration:
Changed in version 3.6: The $expr operator allows the use of aggregation expressions within the query language. $expr is faster than $where because it does not execute JavaScript and should be preferred where possible.

MongoDB $pop element which is in 3 time nested array

Here is the data structure for each document in the collection. The datastructure is fixed.
{
'_id': 'some-timestamp',
'RESULT': [
{
'NUMERATION': [ // numeration of divisions
{
// numeration of producttypes
'DIVISIONX': [{'PRODUCTTYPE': 'product xy', COUNT: 100}]
}
]
}
]
}
The query result should be in the same structure but only contain producttypes matching a regular expression.
I tried using an nested $elemMatchoperator but this doesn't get me any closer. I don't know how I can iterate each value in the producttypes array for each division.
How can I do that? Then I could apply $pop, $in and $each.
I looked at:
Querying an array of arrays in MongoDB
https://docs.mongodb.com/manual/reference/operator/update/each/
https://docs.mongodb.com/manual/reference/operator/update/pop/
... and more
The solution I want to avoid is writing something like this:
collection.find().forEach(function(x) { /* more for eaches */ })
Edit:
Here is an example document to copy:
{"_id":"5ab550d7e85d5930b0879cbe","RESULT":[{"NUMERATION":[{"DIVISION":[{"PRODUCTTYPE":"Book","COUNT":10},{"PRODUCTTYPE":"Giftcard","COUNT":"300"}]}]}]}
E.g. the query result should only return the entry with the giftcard:
{"_id":"5ab550d7e85d5930b0879cbe","RESULT":[{"NUMERATION":[{"DIVISION":[{"PRODUCTTYPE":"Giftcard","COUNT":"300"}]}]}]}
Using the forEach approach the result is in the correct format. I'm still looking for a better way which does not involve the use of that function - therefore I will not mark this as an answer.
But for now this works fine:
db.collection.find().forEach(
function(wholeDocument) {
wholeDocument['RESULT'].forEach(function (resultEntry) {
resultEntry['NUMERATION'].forEach(function (numerationEntry) {
numerationEntry['DIVISION'].forEach(function(divisionEntry, index) {
// example condition (will be replaced by regular expression evaluation)
if(divisionEntry['PRODUCTTYPE'] != 'Giftcard'){
numerationEntry['DIVISION'].splice(index, 1);
}
})
})
})
print(wholeDocument);
}
)
UPDATE
Thanks to Rahul Raj's comments I have read up the aggregation with the $redact operator. A prototype of the solution to the issue is this query:
db.getCollection('DeepStructure').aggregate( [
{ $redact: {
$cond: {
if: { $ne: [ "$PRODUCTTYPE", "Giftcard" ] },
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}
]
)
I hope you're trying to update nested array.
You need to use positional operators $[] or $ for that.
If you use $[], you will be able to remove all matching nested array elements.
And if you use $, only the first matching array element will get removed.
Use $regex operator to pass on your regular expression.
Also, you need to use $pull to remove array elements based on matching condition. In your case, its regular expression. Note that $elemMatch is not the correct one to use with $pull as arguments to $pull are direct queries to the array.
db.collection.update(
{/*additional matching conditions*/},
{$pull: {"RESULT.$[].NUMERATION.$[].DIVISIONX":{PRODUCTTYPE: {$regex: "xy"}}}},
{multi: true}
)
Just replace xy with your regular expression and add your own matching conditions as required. I'm not quite sure about your data set, but I came up with the above answer based on my assumptions from the given info. Feel free to change according to your requirements.

MongoDB $and on two columns

I am following MongoDB documentation for C# & shell commands to understand find() for $and for restaurants example. However, following issue I observed:
ar builder = Builders<BsonDocument>.Filter;
var filter = builder.Eq("cuisine", "Italian") & builder.Eq("address.zipcode", "10075");
filters the restaurants collection however when I change filter as:
var filter = builder.Eq("borough", "Manhattan") & builder.Eq("cuisine", "American");
This doesn't return any record as I can see combination of these two are present in restaurants collection.
Shell command which is not working:
db.restaurants.find( { $and: [ {borough: "Queens" }, { cuisine: "American" } ] } )
Any clue?
Yo don't need to use the $and operator in this case.
db.restaurants.find({borough: "Queens", cuisine: "American"})
Only use $and operator when you need to specify more than one condition for the same field (i.e: db.restaurants.find( { $and: [ {score:{$gte:5}}, {score:{$lte:10}} ] } ))

is it possible to use "$where" in mongodb aggregation functions

I need to get the length of a string value in MongoDB using aggregation functions.
it works in
db.collection_name.find({"$where":"this.app_name.length===12"})
but when implanted to
db.collection_name.aggregate({$match:
{"$where":"this.app_name.length===12"}
},
{
$group :
{
_id : 1,
app_downloads : {$sum: "$app_downloads"}
}
}
);
I got this result:
failed: exception: $where is not allowed inside of a $match aggregation expression
The question is: is it possible to use $where in aggregation functions?
or is there any way of getting the length of a string value in aggregation function?
Thanks in advance
Eric
MongoDB doesn't support $where in aggregation pipeline and hope this will never happen, because JavaScript slows things down. Never the less, you still have options:
1) Мaintain additional field(e.g. app_name_len) than will store app_name length and query it, when needed.
2) You can try extremely slow MapReduce framework, where you allowed to write aggregations with JavaScript.
Today I had the same problem.
Mongodb doesn't support this.app_name.length, but you can do this condition with $regex - this is not very quick, but it still works.
{"app_name": { $regex: /^.{12}$/ }}
A simple way to achieve the behaviour expected of OP would be chaining up $expr with $strLenCP
db.collection.find({
$expr: {
$eq: [
12,
{
$strLenCP: "$app_name"
}
]
}
})
Mongo Playground

mongoDB PHP query where is NOT

ok I need a quick way to do the following with mongoDB
I was looking to do a query that would search for anything that did NOT have the word Apple in the fruit or veg column in the collection
here etc
{
"fruit":"apple"
},{
"fruit":"orange"
},{
"fruit":"banana"
}
The operator you probably want to use is $nin ("not in"):
db.market.find({
'fruit': {$nin:['apple']},
'veg': {$nin:['apple']}
})
You could also use $not to negate a standard where condition.