MongoDB conditional query - node.js

We want to query a collection using two fields (ids) to find a specific conversation that has userOne and or userTwo with same id number like we want to find all the conversations that has 57e334af0e5fcc9e8e800177 id in userOne or userTwo
Mongoose Schema
userOne: {type: mongoose.Schema.Types.ObjectId, ref:'users'},
userTwo: {type: mongoose.Schema.Types.ObjectId, ref:'users'},
created_at: {type: Date, default: Date.now}
Query that we tried.
db.getCollection('conversations').find({
$or : [{userOne: "57e334af0e5fcc9e8e800177"}],
$or : [{userTwo: "57e334af0e5fcc9e8e800177"}]
})
Sample Conversation Object
_id: "57eae04c4efbb25487a00293"
created_at: "2016-09-27T21:10:36.236Z"
userOne: "57e334c00e5fcc9e8e800178"
userTwo: "57e334af0e5fcc9e8e800177"

You only need a single $or condition because it takes an array which you can put both your fields into. From the example on the MongoDB documentation:
db.inventory.find( { $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] } )
So all you'd need to do is:
db.getCollection('conversations').find({
$or: [
{userOne: "57e334af0e5fcc9e8e800177"},
{userTwo: "57e334af0e5fcc9e8e800177"}
]
});

Related

find object by id which is in array mongoose

My chat object has got an array with two elements - users id. I have first id from url params, but second id I have in array of users. Now I want to get all chats where first id is this one from url, and second is in the array. I think that example how I tried to do it will be the best explanation of this problem :
Chat.find({
users: { $all: [req.params.id, { $in: usersIdArray }] }
})
where usersIdArray is for example:
[ 5f8777a01d8c122e74cb6f08, 5f8777721d8c122e74cb6f02 ]
they are numbers, not strings. I don't know if it is important...
The error I get now :
(node:12168) UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "{ '$in': [ 5f8777a01d8c122e74cb6f08, 5f8777721d8c122e74cb6f02 ] }" at path "users" for model "Chat"
And my chat schema:
// Create schema
const ChatSchema = new Schema({
users: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Users',
}, {
type: Schema.Types.ObjectId,
ref: 'Users',
}],
required: [true, 'Podaj uczestników czatu.'],
},
lastMessage: {
type: Schema.Types.ObjectId,
ref: 'Message'
}
}, { timestamps: opts });
Since the length of your array is fixed (2), you can just query based on array position:
Chat.find({
"users.0": req.params.id,
"users.1": {$in: usersIdArray}
});
If that doesn't work then probably because usersIdArray are actually not ObjectIds, in which case you'd need to map them:
usersIdArray.map(x => ObjectId(x))
#Christian Fritz, I had to add $or to your solution and everything is fine now:
Chat.find({
$or: [
{
"users.1": req.params.id,
"users.0": { $in: usersIdArray }
}, {
"users.0": req.params.id,
"users.1": { $in: usersIdArray }
}]
})

How can I filter by subdocument field in a mongoose aggregation?

I want to filter the charges of only the specific user for the payment order. Here are my schemas:
var PaymentOrderSchema = new Schema({
name: {type: String, required: true},
description: {type: String},
amount: {type: Number},
charges: [{type: Schema.Types.ObjectId, ref: 'Charge'}],
},
var ChargeSchema = new Schema({
amount: {type: Number},
user: {type: Schema.Types.ObjectId, ref: 'User'},
status: {type: String},
description: {type: String},
creation_date:{type: Date}
}
I am trying to use aggregation, but I'm not able to filter the charges. When I use $ne instead of $eq it does return me the charges, so I know I'm on the right track. Here is my actual code:
const paymentOrder = await PaymentOrder.aggregate([
{$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
{$project: {
charges: {$filter: {
input: '$charges',
as: 'charge',
cond: {$eq: ['$$charge.user._id', mongoose.Types.ObjectId(req.user._id)]}
}}
}}
]);
Thanks!
If I understand your requirement right, then we can to use $lookup with pipeline
something like this
db.paymentOrder.aggregate([
{
$match: {
_id: ObjectId("5a934e000102030405000003") // replace this hard-coded objectId with mongoose.Types.ObjectId(req.query.payment_order_id)
}
},
{
$lookup: {
from: "charge",
let: {
chargeIds: "$charges"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$user",
"userId1" // replace this with mongoose.Types.ObjectId(req.user._id)
]
}
}
}
],
as: "charges"
}
}
])
check this Mongo Plaground
hope it helps
Aggregation runs on the database server while references are expanded by the driver on the client side, so the value that is actually seen in the pipeline is
DBRef("User",ObjectId(...),"DatabaseName")
Which can be accessed sort of like an object
{"$ref":"User", "$id":ObjectId(...), "$db":"DatabaseName"}
But this raises a further problem: field names are not allowed to begin with $ so this will throw an error:
{$eq:["$$charge.user.$id",mongoose.Types.ObjectId(req.user._id)]}
So you can either skip that step in the aggregation and bring the document back to mongoose to populate, or if matching on the $id is all you need, you can use $objectToArray to break the DBRef down so you can match:
{$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
{$unwind: "$charges"},
{$addFields: {charges:{$objectToArray:"$charges"}}},
{$match: {charges:{
$elemMatch:{k:"$id",v:mongoose.Types.ObjectId(req.user._id}
}}},
{$group:{_id:"$_id", charges:{$push:{$arrayToObject("$charges"}}}}}
This gives you back the DBRefs that can be expanded by the driver, but this matches against the _id of the charges document, which is probably not what you want.
However, once you have the _id of the charge document, you can use $lookup and then filter them, but then we also have to deal with the user being a DBref:
{$match: {'_id': mongoose.Types.ObjectId(req.query.payment_order_id)}},
{$unwind: "$charges"},
{$addFields: {chargeId:{$filter:{
input:$objectToArray:"$charges"
cond:{$eq:["$$this.k","$id"]}
}}}},
{$lookup:{
from:"charge",
localField:"$chargeId.v",
foreignField:"$_id",
as: charges
}},
{$unwind:"$charges"},
{$addField:{chargeUser:{$filter:{
input:{$objectToArray:"$charges.user"},
cond:{$eq:["$$this.k","$id"]}
}}}},
{$match: {"chargeUser.v":mongoose.Types.ObjectId(req.user._id)}},
{$project:{
chargeUser:0,
chargeId:0
},
{$group:{
_id:"$_id",
document:{$first:"$$ROOT"},
charges:{$push:"$charges"}
}},
{$addFields:{"document.charges":"$charges"}},
{$replaceRoot:{newRoot:"$document"}}

Mongoose query to check for specific value in array of searched element

I have s schema like:
var EntitySchema = new Schema({
name : {type: String, default: null},
organizations : [{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Organization'
}}]
});
I have an id of organization, How can I make a query for Entity.find({}) and find Entity that has this id in its organizations array?
I used this in mongo shell
db.entity.find({"organizations.id": { $in: [ObjectId("ididididididid")]}}).pretty()
and it worked, but it doesn't work in express method, do I do something wrong? Does $in works both ways? I have a feeling that its not what I should be using in this query.
As per mongo documentation, you can query as below
db.entity.find({ "organizations" : { $elemMatch: { "id" : ObjectId("ididididididid") } }}).pretty();
Ref:
https://docs.mongodb.com/manual/tutorial/query-array-of-documents/

Mongoose find() returning irrelevant documents

I have a schema like this one:
var WorkSchema = new mongoose.Schema({
works: [{
name:String,
times:[{
day:String,
timeOfDay:[{
startTime: Number,
endTime: Number
}],
date: Date
}],
locations:[String]
}],
worker: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
},{timestamps: true}
);
I have saved a lot of document on this schema. Now, i want to find the documents which has name:'idle'. I'm using Worker.find({'works.name':req.body.name}) but it's not giving me the exact documents i want and giving irrelevant documents. But in MongoDb Compass, this exact line is finding the desired documents.
How do i find the values in mongoose?

How to query all documents in mongodb that do not have a specific id?

I am trying to return all documents that do not have the user's id in the document's followers array.
I am using mongoose with Node.Js but the regular mongodb way should be fine.
EDIT
Adding more info:
Here is an example document:
{
__v: 0,
_id: ObjectId("53333273adf3ede81f0ce23b"),
title: "fashion",
date: 1395864179,
followers: [
ObjectId("53343ac3abcd1b6016c52c1b")
]
}
The query I tried to write but not sure if $nin is correct since the examples on mongodb are the opposite of making sure a field in the document does not equal an array of things, where I want to make sure an array of the document does not include a single element.
Story.find({followers: {$nin: [user]}}, "_id title", {$sort: {date: {{$gte: 30 } }, skip: 5, limit: 100}, function(err, results) {
The model:
var Story = new Schema({
title: {type: String},
followers: [{ type: ObjectId, ref: 'User' }],
date: {type: Number}
}, {collection: "stories"});
module.exports = mongoose.model('Story', Story);
The call to the server: (note I am converting it from a string to an object Id
GET /stories?userid=53343ac3abcd1b6016c52c1b&count=0 200 201ms - 2b
You can do that with following query;
Story.inventory.find({followers: {$exists: true, $nin: [ "53343ac3abcd1b6016c52c1b" ]}},
{$sort: {date: {{$gte: 30 } }, skip: 5, limit: 100, function(err, results) {
});
This query will select all documents in the User collection where the followers field exists and its value does not equal to 53343ac3abcd1b6016c52c1b.
Note: Do not convert id to Object, because mongoose does that for you.

Resources