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

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,
},
],
},
};

Related

return what was not found in array in MongoDB

say my database collection has
* user collection*
[
{id:'1'}
{id:'2'}
]
I have an array of object
[
{id:'1'}
{id:'2'}
{id:'3'}
]
I want the object that was not found in the collection.
I want
[
{id:'3'}
]
I'm currently have this
const records = await dbo
.collection('user collection')
.find({
'id': { $in: newArr },
})
.toArray();
I'm a bit stumped on what to do! ... hope someone can help Thanks!
Option 1:
Looks like this is what you need via the not in operation ( $nin ) when you need to check the not exisitng id in collection documents from provided array:
db.collection.aggregate([
{
$match: {
id: {
"$nin": [
1,
2
]
}
}
},
{
$group: {
_id: null,
"idnotIntheArray": {
$push: "$id"
}
}
}
])
Explained:
$match for any documents with id not in provided array.
$group all id's in an array
plaground1
Option 2:
And this is the option where you output only the array elements not existing in the collection:
db.collection.aggregate([
{
$group: {
_id: null,
ids: {
$push: "$id"
}
}
},
{
$project: {
missingFromCollection: {
"$setDifference": [
[
1,
5,
4
],
"$ids"
]
}
}
}
])
Explained:
Push all id elements from collection to array ids ( note this solution will not allow more then 16MB total size of id's )
Use $setDifference to identify the difference between the two arrays.
playground2
You can use this aggregation:
db.entity.aggregate([
{
$match : {
"myObjList.id" : 1
}
},
{
$unwind : "$myObjList"
},
{
$match : {
"myObjList.id" : 1
}
}
])
and my aggregation result:
{
"_id" : ObjectId("6225a0f78d435fd2845f1dd1"),
"myObjList" : {
"id" : 1
}
}

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'] } } }

get the latest document after $group by

I'm using the official MongoDB driver for Node.js.
And this is how my message data is structured. As you can see, every post has a timestamp, an userId and the id of the topic.
[
{
"_id" : ObjectId("5b0abb48b20c1b4b92365145"),
"topicId" : "XN7iqmCFD4jpgJZ6f",
"timestamp" : 1527429960,
"user" : "5b0869636f4e363e300d105a",
"content" : "lorem ipsum"
}
]
Now I need to check if there are topics, which newest post (=highest timestamp) doesn't match my own ID.
With that I do know which topic has a new answer (which is not my own post).
So I started with this:
db.messages.find({
$query: {
user: { $ne: "myUserId" }
},
$orderby: {
timestamp: -1
}
}).limit(1).toArray()
My problem is, that I do not know how to group my query by the topicId. And somehow there seems to be wrong syntax in my attempt.
You have to use aggregate to group by topicId and then $sort for sorting according to time and then $limit to limit the query
db.messages.aggregate([
{ $match: { user: { $ne: "myUserId" } }},
{ $group: {
_id: "$topicId",
timestamp: { $first: "$timestamp"},
user: { $first: "$user" },
content: { $first: "$content" }
}},
{ $sort: { timestamp: -1 } },
{ $limit: 1 }
])
Use aggregate pipeline to do this instead of find.
1) $match $ne :"myUserId"
2) $sort timestamp:-1
3) $group topicId $first
A sample query I have written in the past..
{ "$match": { $and: [ {'latestTimeStamp': { "$gte": new Date('3/11/2018') }}, {'latestTimeStamp': { "$lte": new Date('4/12/2018') }} ]} },
{ $unwind: '$latestSev'},
{ $sort: {'latestSev.sevRating': -1}},
{ $group:{ _id:{_id:'$_id', latestTimeStamp:'$latestTimeStamp', latestLikeli:'$latestLikeli', latestControl:'$latestControl', residualRatingArray:'$residualRatingArray'},
latestMaxImpName: {$first:'$latestSev.impName'} ,
latestMaxSevRating: {$first:'$latestSev.sevRating'}
}
}

(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

Find element in an array that may or may not exist

I have a document that looks a bit like this:
> db.orders.find()
{
_id: ObjectId(),
_reminders: [{
notified: true,
timestamp: ISODate(),
completed: false
}]
}
{
_id: ObjectId(),
_reminders: []
}
What I am trying to find is a document in the orders collection where the "reminders" does not contain a reminder in a specific time range, and is not completed.
db.orders.find({
'_reminders': {
$elemMatch: {
completed: false,
timestamp: { $ne: time }
}
}
});
The problem is that this will not find an order which does not have any reminders at all.
How would one query this?
This should get you what you want
db.getCollection('Clock').find({
$or : [
{
_reminders : {
$elemMatch : {
timestamp : {
$lte : ISODate("2019-07-12T15:35:32.278Z"),
$gte : ISODate("2012-07-12T15:35:32.278Z")
},
completed : false
}
}
},
{
_reminders : {$size : 0}
},
{
_reminders : {$exists : false}
}
]
})
you should use $or query.
db.orders.find({$or: [ { _reminders: [] }, here_put_your_query_with_time_match ]}) - it will return both documents which match your query and these with empty _reminders

Categories

Resources