mongodb, how to use $not filter in query - mongodb

I have what should be a fairly simple query I'm trying to run in mongodb compass:
{ $and: [ { Source: "hostname" }, { Message: { $not: /.*unexpected data.*1234.*/ } } ] }
Basically, my document model contains a Source field and a Message field.
I need to query for all documents where the Source equals a given "hostname" and the Message does not contain "unexpected data...1234"
Everything works fine when I DO NOT include the $not filter on the regular expression... so I get all the documents where this message is contained... but now I need all the other messages where this is NOT contained... and I can't figure out how to use $not properly.
The example given in the MongoDb manual only shows using $not with one statement... but even this doesn't work for me for some reason...
{ Message: { $not: /.*unexpected data.*1234.*/ } }
Again, it works fine without the $not... what am I missing?
Edit:
Here is an image of what I'm talking about, placing this filter in MongoDb Compass, it indicates that the filter is incorrect... Is MongoDb Compass for some reason incapable of running complex filters?

Please try with $regex which should work:
find( { $and: [ { Source: "hostname" }, { Message: { $not: { $regex: /.*unexpected data.*1234.*/ } } } ] })

MongoDB Compass only supports regex surrounded by single quotes, not forward slashes. So $regex: /.*unexpected data.*1234.*/ should be $regex: '.*unexpected data.*1234.*'.
https://jira.mongodb.org/browse/COMPASS-2993

Related

MongoDB $elemMatch comparison to field in same document

I'm wanting to create an aggregation step to match documents where the value of a field in a document exists within an array in the same document.
In a very worked example (note this is very simplified; this will be fitting into a larger existing pipeline), given documents:
{
"_id":{"$oid":"61a9085af9733d0274c41990"},
"myArray":[
{"$oid":"61a9085af9733d0274c41991"},
{"$oid":"61a9085af9733d0274c41992"},
{"$oid":"61a9085af9733d0274c41993"}
],
"myField":{"$oid":"61a9085af9733d0274c41991"} // < In 'myArray' collection
}
and
{
"_id":{"$oid":"61a9085af9733d0274c41990"},
"myArray":[
{"$oid":"61a9085af9733d0274c41991"},
{"$oid":"61a9085af9733d0274c41992"},
{"$oid":"61a9085af9733d0274c41993"}
],
"myField":{"$oid":"61a9085af9733d0274c41994"} // < Not in 'myArray' collection
}
I want to match the first one because the value of myField exists in the collection, but not the second document.
It feels like this should be a really simple $elemMatch operation with an $eq operator, but I can't make it work and every example I've found uses literals. What I've got currently is below, and I've tried with various combinations of quotes and dollar signs round myField.
[{
$match: {
myArray: {
$elemMatch: {
$eq: '$this.myField'
}
}
}
}]
Am I doing something very obviously wrong? Is it not possible to use the value of a field in the same document with an $eq?
Hoping that someone can come along and point out where I'm being stupid :)
Thanks
You can simply do a $in in an aggregation pipeline.
db.collection.aggregate([
{
"$match": {
$expr: {
"$in": [
"$myField",
"$myArray"
]
}
}
}
])
Here is the Mongo playground for your reference.

Cannot use aggregation operations in $set inside updateMany

I have a mongoDB (4.4.8) collection where I want to change the value of some field based on its previous value. For example, I want to convert all strings to uppercase.
For this, I use the following query:
db.collection.updateMany(
{ field: { $regex: "[a-z]+"}},
{ $set: { field: { $toUpper: "$field" } } }
)
when executing the query, it gives me the following error:
MongoError: The dollar ($) prefixed field '$toUpper' in 'field.$toUpper' is not valid for storage
The same occurs if I use similar operations such as $concat (with an array parameter) to append something to the field.
When I look up similar questions, it all uses update and tells me to use updateMany instead, or it says that it only works in mongoDB >= 4.2. However, I have both of these things.
If I am correct, you are able to use aggregation syntax (among which $toUpper) in conjunction with $set inside updateMany queries for these newer versions of mongoDB.
Does anyone know what I'm doing wrong here?
As in the comments of J.F. and turivishal, I managed to solve this by changing it into the following:
db.collection.updateMany(
{ field: { $regex: "[a-z]+"}},
[ { $set: { field: { $toUpper: "$field" } } } ]
)

Query mongodb array of documents

I've a bunch of documents that look like:
{
"ids": [{"name":"aa", "age":1}, {"name":"bb", "age":2}]
}
I'd like to be able to query my documents providing a collection of ids, something like
db.getCollection('Collection').find({"ids":{$in : [{"name":"aa", "age":1}, {"name":"bb", "age":2}]}})
Generally that works, however it breaks when the fields order is changed, so for example I cannot find documents when I execute the following query
db.getCollection('Collection').find({"ids":{$in : [{"age":1,"name":"aa"}, { "age":2, "name":"bb"}]}})
I know that I could try to always execute a query with fields "in order", but from my current task perspective it's not always possible. Any help with that ?
You need $elemMatch when you want to run your query against an array of objects:
db.col.find({ $or: [ { "ids": { $elemMatch: {"age":1,"name":"aa"} } }, { "ids": { $elemMatch: { "age":2, "name":"bb"} } } ] })
Mongo Playground
EDIT: you can decide whether $or or $and should be a top level operator (depending on your use case)

MongoDB Aggregation: Unable to reference fields using $cond

I'm performing an aggregation on a MongoDB collection. The steps preceeding the topical $cond are not important, so I'll redact them for brevity:
db.mycoll.aggregate([
{ $match: ... },
{ $project: ... },
{ /* this is the problem step */ }
])
The documents that are being generated by step 2, $project, are shaped like this:
{
"blueTeam": true,
"redTeam": false,
"winner": true
}
Now assuming I add an additional projection which utilizes $cond - I'm not permitted to address the fields from the projected document. Here's a naive example:
{
$project: {
blueTeam: "$blueTeam",
winnerAsInteger: {
$cond: [ { "$winner": true }, 1, 0 ]
}
}
}
Expectation: The pipeline emits documents in which winning documents have field winnerAsInteger equal to 1, otherwise 0.
Reality: This pipeline step produces an error.
In Node's MongoDB client, the error is as follows:
(node:34380) UnhandledPromiseRejectionWarning: MongoError: Unrecognized expression '$winner'
In MongoDB Compass's aggregation GUI, the error is:
Field must not begin with '$' or '.', field path was: $winner
This seems to directly contradict the documentation regarding this, which references document fields with the $ syntax.
I'm on MongoDB 4.0.5, for what it's worth.
Try
{$eq:[ "$winner", true ]} instead of { "$winner": true }
Courtesy of #s7vr in comments, just added it here so that people don't think it's an unanswered question!

Optimized way of Querying in MongoDB using $in vs $or

Implementing an application that looks up a table for mail id presence from a list of around 10 email ids. Tried using $or and $in.
$in seems to give better performance but not significant. Is one more optimized than other?
MongoDB docs have the answer:
"When using $or with <expressions> that are equality checks for the value of the same field, choose the $in operator over the $or operator."
$or operator is logical operator where you can define your own login but $in operator is Comparison operator where you can compare you can not put your on logic.
Syntax of $in:
{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }
Example:
db.account.find( { qty: { $in: [ 5, 15 ] } } )
Syntax of $or:
{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
Example:
db.account.find( { $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] } )
Note: Account is your collection name
"While "$or"will always work, use "$in"whenever possible as the query optimizer
handles it more efficiently."
Moreover "$in" has more readability.
Ref: MongoDB: The Definitive Guide
Well that will insure no indecis to be ensured if you use $in, however i prefer to format it to $or as it will ensure index (readability won't concern me at is being handled in application logic in which i prefer to consume the memory of app rather than mongodb server)