Query to get data not present in multiple arrays - mongodb

Hi I have User Schema like this:-
var userSchema = new Schema({
name: {type: String, default: null},
location: {
type: { type: String },
coordinates: [Number],
},
sentFriendRequests: [
{type: Schema.Types.ObjectId, ref: 'user'}],
receivedFriendRequests: [
{type: Schema.Types.ObjectId, ref: 'user'}]
});
It is working good for all the requirements. I am searching nearby users with this query:-
User.aggregate(
[{
$geoNear: {
near: { type: "Point", coordinates: [ longitude , latitude ] },
distanceField: "dist.calculated",
num: 5,
spherical: true
}
}], function(err, nearByUsers){
console.log(nearByUsers);
})
The above query is working very good But now i want to search only the users who are not my in my friends array and not in both sent and received friend request array.

Well assuming you have the user document (because you're using its coordinates) then just add a $match to filter out the users before the $geonear phase.
{
$match: {
$and: [
{ _id: {$nin: user.sentFriendRequests},
{ _id: {$nin: user.receivedFriendRequests}
]
}
}

Related

MongoDB Find by id of object inside array

I have the following Model-Structure
[{
"orders":
[{
"_id": "5f24839133759d4859ef5233",
"owner": {
"_id": "5f17fdb08145508a94fc1bda"
},
}]
},
...
]
I want to get all objects that have an order with a specific owner id.
I tried find({"orders.owner._id": "5f17fdb08145508a94fc1bda" }) which just returns an empty array.
Tried many other variations also without dot-notation, but nothing worked.
I'm using mongoose to query the MongoDB database.
EDIT:
These are the Mongoose Schemata used for the collection:
const orderPoolShema:mongoose.Schema = new mongoose.Schema({
orders: [{type: mongoose.Types.ObjectId, ref: 'Order' }]
},
{
timestamps: true
});
const orderShema:mongoose.Schema = new mongoose.Schema({
owner: {type: mongoose.Types.ObjectId, ref: 'User', require },
},
{
timestamps: true
});
const userShema:mongoose.Schema = new mongoose.Schema({
firstname: {type: String, required: true},
lastname: {type: String, required: true}
},
{
timestamps: true
});
And this is my Node.js code:
OrderPool.find({"orders.owner._id": mongoose.Types.ObjectId("5f17fdb08145508a94fc1bda") })
.then((orderPools:any) => {
res.send(orderPools);
}).catch((err:any) => {
res.status(500).send({
message: err.message || "Some error occurred while retrieving orderPools."
});
});
Any ideas on how to get that query to work are very appreciated.
Thanks for your help.

mongodb query to show subdocument as maindocument and having pagination with it

First of all, I have this type of model:
const produkSchema = new mongoose.Schema({
nama_produk: String,
etalase: {type: mongoose.Schema.Types.ObjectID, ref: 'kategori'},
kategori: {type: mongoose.Schema.Types.ObjectID, ref: 'kategori'},
jenis: {type: mongoose.Schema.Types.ObjectID, ref: 'kategori.jenis'},
bahan: String,
warna: String,
deskripsi: String,
foto_produk: [String],
harga: Number,
link_bukalapak: String,
link_shopee: String,
link_tokopedia: String,
link_lazada: String,
link_website: String,
display: {type: Boolean, default: false},
}, {
weights: {
nama_produk: 5,
},
timestamps: true
})
const tokoSchema = new mongoose.Schema({
username: {type: String, trim: true},
password: {type: String, required: true, select: false},
merek: String,
listMerek: [{type: mongoose.Schema.Types.ObjectID, ref: 'produk'}],
deskripsi: String,
follower: [{type: mongoose.Schema.Types.ObjectID, ref: 'user'}],
email: {type: String, trim: true, unique: true},
instagram: String,
whatsapp: String,
website: String,
alamat: String,
foto_profil: String,
bukalapak: String,
shopee: String,
tokopedia: String,
fotoktp: String,
banner: [{
gambar: {type: String, required: true, trim: true},
// order: {type: Number, required: true},
}],
produk: [produkSchema],
etalase: [{type: mongoose.Schema.Types.ObjectID, ref: 'kategori'}],
// etalase: [{
// kategori: {type: mongoose.Schema.Types.ObjectID, ref: 'kategori'},
// order: {Number}
// }],
approve: {type: Number, default: 0}, // 0: pending, 1: reject, 2: approve
populer: {type: Boolean, default: false},
gambar_populer: [String],
pilihan: {type: Boolean, default: false},
}, {timestamps: true});
and I have an endpoint to filter this produkSchema with following code:
exports.filterProduk = (req, res) => {
const {merek, warna, kategori, jenis, hargaAwal, hargaAkhir, skip, limit} = req.body
let query = {}
const $and = []
if (merek) {
$and.push({$or: merek.map(id => ({_id: mongoose.Types.ObjectId(id)}))})
}
if (warna) {
$and.push({$or: warna.map(warna => ({"produk.warna": warna}))})
}
if (kategori) {
query["produk.etalase"] = mongoose.Types.ObjectId(kategori)
}
if (jenis) {
$and.push({$or: jenis.map(id => ({"produk.jenis": mongoose.Types.ObjectId(id)}))})
}
if (hargaAwal !== '') {
query["produk.harga"] = {
$gte: parseInt(hargaAwal),
}
}
if (hargaAkhir !== '') {
query["produk.harga"] = {
$lte: parseInt(hargaAkhir)
}
}
if ($and.length > 0) {
query = {$and, ...query}
}
toko.aggregate([
{$unwind: '$produk'},
{$match: query},
{
$lookup: {
from: "kategoris",
as: "produk.etalase",
let: {pjid: "$produk.jenis"},
pipeline: [
{$unwind: "$jenis"},
{$match: {$expr: {$eq: ["$$pjid", "$jenis._id"]}}},
{
$project: {
"jenis._id": 1,
"jenis.label": 1
}
}
]
}
},
{$unwind: {path: "$produk.etalase"}},
{$group: {_id: '$_id', produk: {$push: '$produk'}, foto_profil: {$first: '$foto_profil'}}},
{$limit: skip + limit},
{$skip: skip}
])
.then(async data => {
res.status(200).json({data, prefix: {produk: "uploads/produk", toko: "uploads/toko"}})
})
}
the actual result is:
[
{_id: blabla,
foto_profil: "blabla",
produk:[{nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query}]
},
{_id: blabla,
foto_profil: "blabla",
produk:[{nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query}]
},
{_id: blabla,
foto_profil: "blabla",
produk:[{nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query}]
}
]
expected:
[
{nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query, foto_profil(from toko schema): "blabla"},
{nama_produk: "blabla", bahan: "blabla", ...rest ProdukSchema as query, foto_profil(from toko schema): "blabla"}
]
and having pagination with this produkSchema (limit and offset)
actually, before this, I already ask for this query at here, but this query will produce a lot of data and need to be paginated,
how do I do this? Should I split my produkSchema to main subdocument? or any query exist for this condition?
I am not sure with exact requirement, but I can guess 2 options,
Assuming Variables:
let skip = 0;
let limit = 10;
First Option:
remove $group stage from the end and start after $unwind stage
$replaceRoot to merge objects produk and foto_profil using $mergeObjects and replace the root
{
$replaceRoot: {
newRoot: {
$mergeObjects: [{ foto_profil: "$foto_profil" }, "$produk"]
}
}
},
{ $skip: skip * limit },
{ $limit: limit }
Playground
Second Option:
remove $group stage from the end and start after $unwind stage
$group by toko id and produk id, this will group produk and get unique first produk
$replaceRoot to merge objects produk and foto_profil using $mergeObjects and replace the root
{
$group: {
_id: {
_id: "$_id",
produk_id: "$produk._id"
},
root: { $first: "$$ROOT" }
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [{ foto_profil: "$root.foto_profil" }, "$root.produk"]
}
}
},
{ $skip: skip * limit },
{ $limit: limit }
Playground
maybe you can do this after lookup:
$unwind produk (where the produk still on array type, then unwind it).
$group: { _id: nama_produk, etc... }
$project ...... or whatever you wanna do with.
so you can get the list of group by nama_produk as an _id.

Mongoose Geolocation, Get products ordered by the location which is belongs to a different mongoose model

I am creating a store manager app. Here user should be able to query the products using the products query. The condition is those products should be ordered by the distance to products store.
Eg: If the user is searching for a pencil then It should return all the products named pencil which belong to stores within 15 KM, and results should be ordered by the distance to the store.
// The Product Model
const productSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: 'Product name is required'
},
store: {
type: mongoose.Schema.ObjectId,
ref: 'Store',
required: 'Store must be connected to the user'
}
});
// The store model
const storeSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: 'Please enter a store name'
},
location: {
type: {
type: String,
default: 'Point'
},
coordinates: [{
type: Number,
required: 'You must supply coordinates!'
}]
},
});
this is how I querying products
const products = await Product.find({
name : args.name
store:{
near: { type: "Point", coordinates: [ 79.8612, 6.9271] },
maxDistance: store.maxDistance,
spherical: true
}
});
Unfortunately, It gives me the error
CastError: Cast to ObjectId failed for value "{
near: { type: 'Point', coordinates: [ 79.8612, 6.9271 ] },
maxDistance: 15,
spherical: true
}
I believe I have created indexes correctly
storeSchema.index({
location: '2dsphere'
});

MongoDB Query from multiple models / schemas and return in one field

I am using Nodejs and MongoDB, mongoose and expressjs, creating a Blog API having users, articles, likes & comments schema. Below are schemas that I use.
const UsersSchema = new mongoose.Schema({
username: { type: String },
email: { type: String },
date_created: { type: Date },
last_modified: { type: Date }
});
const ArticleSchema = new mongoose.Schema({
id: { type: String, required: true },
text: { type: String, required: true },
posted_by: { type: Schema.Types.ObjectId, ref: 'User', required: true },
images: [{ type: String }],
date_created: { type: Date },
last_modified: { type: Date }
});
const CommentSchema = new mongoose.Schema({
id: { type: String, required: true },
commented_by: { type: Schema.Types.ObjectId, ref: 'User', required: true },
article: { type: Schema.Types.ObjectId, ref: 'Article' },
text: { type: String, required: true },
date_created: { type: Date },
last_modified: { type: Date }
});
What I actually need is when I * get collection of articles * I also want to get the number of comments together for each articles. How do I query mongo?
Since you need to query more than one collection, you can use MongoDB's aggregation.
Here: https://docs.mongodb.com/manual/aggregation/
Example:
Article
.aggregate(
{
$lookup: {
from: '<your comments collection name',
localField: '_id',
foreignField: 'article',
as: 'comments'
}
},
{
$project: {
comments: '$comments.commented_by',
text: 1,
posted_by: 1,
images: 1,
date_created: 1,
last_modified: 1
}
},
{
$project: {
hasCommented: {
$cond: {
if: { $in: [ '$comments', '<user object id>' ] },
then: true,
else: false
}
},
commentsCount: { $size: '$comments' },
text: 1,
posted_by: 1,
images: 1,
date_created: 1,
last_modified: 1
}
}
)
The aggregation got a little big but let me try to explain:
First we need to filter the comments after the $lookup. So we $unwind them, making each article contain just one comment object, so we can filter using $match(that's the filter stage, it works just as the <Model>.find(). After filtering the desired's user comments, we $group everything again, $sum: 1 for each comment, using as the grouper _id, the article's _id. And we get the $first result for $text, $images and etc. Later, we $project everything, but now we add hasCommented with a $cond, simply doing: if the $comments is greater than 0(the user has commented, so this will be true, else, false.
MongoDB's Aggregation framework it's awesome and you can do almost whatever you want with your data using it. But be aware that somethings may cost more than others, always read the reference.

How $geoNear chooses the field of the model to find the coordenates in MongoDB?

I'm using mongoose and I'm trying to get activities from the schema 'Activity' with an specific filter using $geoNear, but it always returns an empty array and I think it's because the aggregate function is not finding the coordenates of my model.
This is an example of the JSON to send:
{
"coordinates": {
"lat": 37.3890924,
"lon": -5.984458899999936
},
"distance": 100000000
}
This is the query:
Activity.aggregate([
{ $geoNear: {
near: {
type: "Point",
coordinates: [req.body.coordinates.lon, req.body.coordinates.lat]
},
maxDistance: req.body.distance * 1000,
spherical: true,
distanceField: "distance"
}
}
])
And this is the schema, where the coordinates are into the field 'meetingPoint':
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate');
const Schema = mongoose.Schema;
var ActivitySchema = Schema({
title: {type: String, require: true },
meetingPoint: {
meet_name:{type: String, require: true },
coordinates: {
lat: {type: Number, require: true },
lon: {type: Number, require: true }
}
},
date: {type: Date, require: true },
car: Boolean,
sites: {type: Number, min: 0 },
price: {type: Number, min: 0.0, require: true },
description: String,
eventTime: String,
event: Boolean,
sport: {type: Schema.Types.ObjectId, ref: 'Sport', require: true },
owner: {type: Schema.Types.ObjectId, ref: 'User', require: true },
attenders: [{type: Schema.Types.ObjectId, ref: 'User'}],
comments: [{type: Schema.Types.ObjectId, ref: 'Comment'}],
ratings: [{
rating:{type: Number},
user: {type: Schema.Types.ObjectId, ref:'User'}
}],
creationDate: {type: Date, default: Date.now, require: true },
updateDate: {type: Date, default: Date.now, require: true },
public: Boolean,
image: String
});
ActivitySchema.plugin(mongoosePaginate);
ActivitySchema.index({location: "2dsphere"});
module.exports = mongoose.model('Activity', ActivitySchema);
EDIT: I changed the coordinates in mongoose schema to the GeoJSON format(I think) and now they look like this:
meetingPoint: {
meet_name:{type: String, require: true },
coordinates: {
type: String,
coordinates: []
}
}
But the result is the same.
EDIT2: I also updated the existing data in the collection according to the meetingPoint with the pertinent format. This is an example:
"_id" : ObjectId("5ac36a2aasdsd3242b"),
"meetingPoint" : { "meet_name" : "Av. de Menéndez Pelayo, 76, 41030 Madrid, España", "coordinates" : { "type" : "Point", "coordinates" : [ -5.984458899
9936, 37.3890924 ] } },
"attenders" :....
But I still receive no activities with the filter.