Mongo node js query implicit $and - javascript

It should only update and return results that have match on the request data.
it should be not mutually exclusive (i.e. if one query does not match then do not return result results)
is using implicit $and a solution to that ?
Query
keystone.list('Vehicle').model.update({ $or: [{ Body: { $in: req.body.data[0].body } }, { Model: { $in: req.body.data[0].model } }, { Make: { $in: req.body.data[0].make } }, { Year: { $in: req.body.data[0].year } }] }, {
$set: {
'Summer_Sale_Event': req.body.summer_sale_value
}
}, {
'multi': true
}).exec(function (err, result) {
});
Request data
Request : [ { body: [ 'Convertible' ],
make: [ 'Chevrolet' ],
year: [ '2005' ],
model: [ 'Corvette' ] } ]

$or means that only one of the condition have to be true.
You should be using $and instead.

Related

how to catch mongo change streams when one key in object gets updated

I am using the mongo change stream to update some of my data in the cache. This is the Documentation i am following.
I want to know when one of the keys in my object gets updated.
Schema :- attributes: { position: { type: Number } },
How will I come to know when the position will get updated?
This is how my updateDescription object looks like after updation
updateDescription = {
updatedFields: { 'attributes.position': 1, randomKey: '1234'},
removedFields: []
}
Tried this and its not working
Collection.watch(
[
{
$match: {
$and: [
{ operationType: { $in: ['update'] } },
{
$or: [
{ 'updateDescription.updatedFields[attributes.position]': { $exists: true } },
],
},
],
},
},
],
{ fullDocument: 'updateLookup' },
);
I was able to fix it like this,
const streamUpdates = <CollectionName>.watch(
[
{
$project:
{
'updateDescription.updatedFields': { $objectToArray: '$updateDescription.updatedFields' },
fullDocument: 1,
operationType: 1,
},
},
{
$match: {
$and: [
{ operationType: 'update' },
{
'updateDescription.updatedFields.k': {
$in: [
'attributes.position',
],
},
},
],
},
},
],
{ fullDocument: 'updateLookup' },
);
and then do something with the stream
streamUpdates.on('change', async (next) => {
//your logic
});
Read more here MongoDB

Use mongoDB $lookup to find documents in another collection not present inside an array

I'm using the aggregate framework to query a collection and create an array of active players (up until the last $lookup) after which I'm trying to use $lookup and $pipeline to select all the players from another collection (users) that are not present inside the activeUsers array.
Is there any way of doing this with my current setup?
Game.aggregate[{
$match: {
date: {
$gte: ISODate('2021-04-10T00:00:00.355Z')
},
gameStatus: 'played'
}
}, {
$unwind: {
path: '$players',
preserveNullAndEmptyArrays: false
}
}, {
$group: {
_id: '$players'
}
}, {
$group: {
_id: null,
activeUsers: {
$push: '$_id'
}
}
}, {
$project: {
activeUsers: true,
_id: false
}
}, {
$lookup: {
from: 'users',
'let': {
active: '$activeUsers'
},
pipeline: [{
$match: {
deactivated: false,
// The rest of the query works fine but here I would like to
// select only the elements that *aren't* inside
// the array (instead of the ones that *are* inside)
// but if I use '$nin' here mongoDB throws
// an 'unrecognized' error
$expr: {
$in: [
'$_id',
'$$active'
]
}
}
},
{
$project: {
_id: 1
}
}
],
as: 'users'
}
}]
Thanks
For negative condition use $not before $in operator,
{ $expr: { $not: { $in: ['$_id', '$$active'] } } }

mongodb find documents using condition and update another field in the respective document

I want to find any document in my collection that:
its hobby field is empty
or its hobby field doesn't exist
and then set the value of its hobby field to be equivalent of its sports field.
db.myCollection.update(
{ $or: [{"hobby": {$exists: false} },{"hobby": ""}]}
, {
$set: {
hobby: ????sports
}
}, {
multi: true
},
function(err, result) {
console.log(result);
console.log(err);
})
With this command above, I suppose I will be passing the value of sports as fixed value to all matching documents. is there any way to pick up its corresponding sports value and set it to hobby?
here is the modified version that updates correctly the values:
db.myCollection.find({
$or: [{ "hobby": { $exists: false } }, { "hobby": "" }]
}).toArray().forEach(function (item) {
db.myCollection.update(
{ _id: item._id },
{ $set: { hobby: item.sports } },
{ multi: true }
)
})
})
You have to do this in 2 queries:
db.myCollection.find({
$or: [{ "hobby": { $exists: false } }, { "hobby": "" }]
}).toArray().forEach(function (item) {
db.myCollection.update(
{ _id: item._id },
{ $set: { hobby: item.sports } },
{ multi: true }
)
})
})

(mongoDB) find with empty or null values

how to make a mongodb collection find (db.collection.find) with empty values?
currently i have:
function test(s) {
if (!s) {
return null;
} else {
return s;
}
}
var content = {
date: {
from: '2017-10-15',
to: '2017-11-15'
},
'name': 'some text', //this can be null or empty
'text': 'some other text' //this can be null or empty
}
col.find({
"date": {
$gte: new Date(content.date.from),
$lte: new Date(content.date.to)
},
"name": {
$ne: {
$type: null
},
$eq: test(content.name)
},
"text": {
$ne: {
$type: null
},
$eq: test(content.text)
},
}).toArray((err, items) => {
console.log(items)
});
but it returns an empty array, because "name" or "text" is null / an empty string,
i want that it query only the values that have something specified or ignore it (like content.name is something in it or its empty)
how do i get it? i already searched ... but didnt found something
thanks!
( already testet mongoDB : multi value field search ignoring null fields)
Versions:
Node: 8.9.0
(npm) mongodb: 2.2.33
mongodb: 3.4
Try using $and, $or operators. Something like.
col.find({
$and:[
{"date": {$gte: new Date(content.date.from),$lte: new Date(content.date.to)}},
{"$or":[{"name": {$ne: {$type: null}}},{"name":test(content.name)}]},
{"$or":[{"text": {$ne: {$type: null}}},{"text":test(content.text)}]}
]
}).toArray((err, items) => {
console.log(items)
});
col.find({
$and: [
{
$and: [
{
"date": {
$gte: new Date(content.date.from)
}
},
{
"date": {
$lte: new Date(content.date.to)
}
},
]
},
{
$or: [
{ 'name': null },
{ 'name': content.name }
]
},
{
$or: [
{ 'text': null },
{ 'text': content.text }
]
}
]
})
Edited:
col.find({
$and: [
{
$and: [
{
"date": {
$gte: new Date(content.date.from)
}
},
{
"date": {
$lte: new Date(content.date.to)
}
},
]
},
{
$or: [
{ 'name': null },
{ 'name':'' }
{ 'name': content.name }
]
},
{
$or: [
{ 'text': null },
{ 'text': '' },
{ 'text': content.text }
]
}
]
})
null and empty is different, you need to add one more condition for empty string in query.
Logical Query Operators

nodejs how to dynamically manipulate your MongoDB aggregate in your controller?

I have a very loooong aggregate in my nodejs controller:
agentSaleModel.aggregate([
{
$match: {
$and:
[{
_id: { $in: req.body.propertyID },
active : true
}]
}
}, etc....
And it works great when I got elements in my req.body.propertyID
like ["property01","property02"] etc...
My problem is that I also want the aggregate to work when there are nothing in req.body.propertyID. (when its blank) - and then get all records.
This does not work:
agentSaleModel.aggregate([
{
$match: {
$and:
[{
_id: { $in: '' },
active : true
}]
}
}, etc....
So rather than doing an "if" with two huge sets of almost identical code:
if (req.body.propertyID) {
...the entire aggregate...
} else {
...the entire aggregate minus the _id: { $in: req.body.propertyID },...
}
Is there a smarter way to do this?
SOLUTION!! Thanks to FluffyNights :)
if (req.body.propertyID!='') {
var matchStr = {
$match: {
$and:
[{
_id: { $in: req.body.propertyID },
active : true
}]
}
}
} else {
var matchStr = {
$match: {
active : true
}
}
}
agentSaleModel.aggregate([ matchStr, etc..... (rest of pipeline)
you could do it like this:
let query = [
{
$match: {
$and:
[{
active : true
}]
}
}];
if(req.body.propertyID) {
query[0]["$match"]["$and"][0]["_id"] = { $in: req.body.propertyID };
}
agentSaleModel.aggregate(query, ...)
you could also use regex, like:
if(!req.body.properyID){
req.body.propertyID = [ ".*" ];
}
agentSaleModel.aggregate([
{
$match: {
$and:
[{
_id: { $in: req.body.propertyID },
active : true
}]
}
}, etc....
however, this might get slow.
Im not sure if passing null to $in would work the way you want, you could try it though.
What about trying to construct query before running it.
For example.
var query = req.body.propertyID ? { $and: [{_id: { $in: req.body.propertyID }, active : true}]} : {active : true}
agentSaleModel.aggregate([
{
$match: query
}, etc....
Hope this helps.
Here's an inline solution using computed property names:
$match: {
$and: [
{
_id: { [ req.body.propertyID ? '$in' : '$exists' ] : req.body.propertyID || true },
active: true,
},
],
}
When req.body.propertyID exists, the query becomes:
_id : { $in : req.body.propertyID }
If not:
_id : { $exists : true }
EDIT: this will also allow req.body.propertyID to equal "ALL" if you explicitly want to match all documents:
let selectAll = ! req.body.propertyID || req.body.propertyID === 'ALL';
const query = {
$match: {
$and: [
{
_id: { [ selectAll ? '$exists' : '$in' ] : selectAll || req.body.propertyID },
active: true,
},
],
},
};

Categories

Resources