Mongoose. Use a constant in $or - node.js

I'm building a query and would like to include access control:
var q = { $and:
[
{ $or: [
isAdmin, // does this use have access to all Resources?
// This pattern doesn't appear to work
{ isPublic: true },
{ createdBy: userId }
]
},
query
]
};
Resource.find(q).sort('-createdAt').limit(10)...
isAdmin can be true or false. If false, then I'd like to do the other tests. query is the rest of the query.
It's not working in mongoose 4.0.4. Any suggestions?

Ok, I found two ways to do this. The best way is to not include the $or when isAdmin. Kind of obvious really.
function findNewestResources(query, callback) {
if (!isAdmin) {
query = { $and:
[
{ $or: [
{ isPublic: true },
{ createdBy: userId }
]
},
query
]
};
}
Resource.find(query).sort('-createdAt').limit(10)
An alternate way was to use $where, but it does a table-scan, so that's no good. The $where worked on 2.6.10, but not on 2.6.9.

Related

It is possible to ignore fields if it doesn't exists in document?

I have a model:
const schema = new Schema({
// ....
conditions: {},
// ....
});
Conditions - nested document and I can save anything into it with any key. And let's say we have such conditions:
{
"conditions": {
"age": 10,
"name": "John"
}
}
This is located on the base. Now, I want to find this document, but since I don't know what fields are there, I am facing problems...
const conditions = {
'conditions.age': 10,
'conditions.name': 'John',
'conditions.surname': 'White' // surname doesn't exists
}
const result = await Model.find(conditions);
console.log(result) // [];
And the question is, is it possible to exclude from the filter the fields that are missing in the document? So that find() simply skipped them, did not take them into account...
Use Logical Query Operators $and and $or as below-
const conditions = {
$and: [
{ 'conditions.age': 10, },
{ 'conditions.name': 'John', },
{ $or: [{ 'conditions.surname': { $exists: false } }, { 'conditions.surname': 'White' }] }
]
}
const result = await Model.find(conditions);

Mongoose: Infinite scroll with filtering

I have these two models:
User.js
const UserSchema = new Schema({
profile: {
type: Schema.Types.ObjectId,
ref: "profiles",
},
following: [
{
type: Schema.Types.ObjectId,
ref: "users",
},
],
});
module.exports = User = mongoose.model("users", UserSchema);
Profile.js
const ProfileSchema = new Schema({
videoURL: {
type: String,
},
});
module.exports = Profile = mongoose.model("profiles", ProfileSchema);
Here's an example of a User document:
{
"following": [
{
"profile":{
"videoURL":"video_url_1"
}
},
{
"profile":{
"videoURL":"video_url_2"
}
},
{
"profile":{}
},
{
"profile":{
"videoURL":"video_url_3"
}
},
{
"profile":{
"videoURL":"video_url_4"
}
},
{
"profile":{
"videoURL":"video_url_5"
}
},
{
"profile":{}
},
{
"profile":{
"videoURL":"video_url_6"
}
}
]
}
I am trying to implement an infinite scroll of the videos of the users followed by the connected user.
This means, I will have to filter user.following.profile.videoURL
WHERE videoURL exists
Suppose, I will be loading two videos, by two videos:
Response 1: ["video_url_1","video_url_2"]
Response 2: ["video_url_3","video_url_4"]
Response 3: ["video_url_5","video_url_6"]
Usually, infinite scroll is easy because all I have to load the documents 2 by 2 by order of storage without filtering on any field.
Example: Displaying the followed users two by two in an infinite scroll
User.findById(user_id).populate({
path: "following",
options: {
skip: 2 * page,
limit: 2,
},
});
But, now I have to perform filtering on each followed_user.profile.video, and return two by two. And I don't see how I can perform BOTH the filtering and the infinite scroll at the same time.
NOTE: According to the documentation:
In general, there is no way to make populate() filter stories based on properties of the story's author. For example, the below query won't return any results, even though author is populated.
const story = await Story.
findOne({ 'author.name': 'Ian Fleming' }).
populate('author').
exec();
story; // null
So I suppose, there is no way for me to use populate to filter based user.followers, based on each user.follower.profile.videoURL
I am not sure it is possible with populate method, but you can try aggregation pipeline,
$match user_id condition
$lookup with aggregation pipeline in users collection for following
$match following id condition
$lookup with profile for following.profile
$match videoURL should exists
$project to show profile field and get first element using $arrayElemAt
$slice to do pagination in following
let page = 0;
let limit = 2;
let skip = limit * page;
User.aggregate([
{ $match: { _id: mongoose.Types.ObjectId(user_id) } },
{
$lookup: {
from: "users",
let: { following: "$following" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$following"] } } },
{
$lookup: {
from: "profiles",
localField: "profile",
foreignField: "_id",
as: "profile"
}
},
{ $match: { "profile.videoURL": { $exists: true } } },
{
$project: {
profile: { $arrayElemAt: ["$profile", 0] }
}
}
],
as: "following"
}
},
{
$addFields: {
following: {
$slice: ["$following", skip, limit]
}
}
}
])
Playground
Suggestion:
You can improve your schema design,
removing profile schema and add profile object in users collection, so you can achieve easily your requirement using populate method,
put match condition in following populate for videoURL exists
const UserSchema = new Schema({
profile: {
type: {
videoURL: {
type: String
}
}
},
following: [
{
type: Schema.Types.ObjectId,
ref: "users"
}
]
});
module.exports = User = mongoose.model("users", UserSchema);
User.findById(user_id).populate({
path: "following",
match: {
"profile.videoURL": { $ne: null }
},
options: {
skip: 2 * page,
limit: 2,
}
});
So what you want is table with infinite scroll and:
You can opt given ways to approach your problem :
Load data (first page) into grid.
Set filter on a col.
Load data again, this time using the filter.

How to pass an optional argument in Mongoose/MongoDb

I have the following query:
Documents.find({
$and: [
{
user_id: {$nin:
myUserId
}
},
{ date: { $gte: dateMax, $lt: dateMin } },
{documentTags: {$all: tags}}
],
})
What I'm trying to do is make the documentTags portion of the query optional. I have tried building the query as follows:
let tags = " ";
if (req.body.tags) {
tags = {videoTags: {$all: req.body.tags}};
}
let query = {
$and: [
{
user_id: {$nin:
myUserId
}
},
{ date: { $gte: dateMax, $lt: dateMin } },
tags
],
}
and then Document.find(query). The problem is no matter how I modify tags (whether undefined, as whitespace, or otherwise) I get various errors like $or/$and/$nor entries need to be full objects and TypeError: Cannot read property 'hasOwnProperty' of undefined.
Is there a way to build an optional requirement into the query?
I tried the option below and the query is just returning everything that matches the other fields. For some reason it isn't filtering by tags. I did a console.log(queryArr) and console.log(query) get the following respectively:
[
{ user_id: { '$nin': [Array] } },
{
date: {
'$gte': 1985-01-01T00:00:00.000Z,
'$lt': 2020-01-01T00:00:00.000Z
}
},
push: { documentTags: { '$all': [Array] } }
]
console.log(query)
{
'$and': [
{ user_id: [Object] },
{ date: [Object] },
push: { documentTags: [Object] }
]
}
You are almost there. Instead you could construct the object outside the query and just put the constructed query in $and when done..
let queryArr = [
{
user_id: {$nin: myUserId}
},
{ date: { $gte: dateMax, $lt: dateMin } }
];
if (req.body.tags) {
queryArr.push({videoTags: {$all: req.body.tags}});
}
let query = {
$and: queryArr
}
Now you can control the query by just pushing object into the query Array itself.
I figured out why it wasn't working. Basically, when you do myVar.push it creates a key-value pair such as [1,2,3,push:value]. This would work if you needed to append a k-v pair in that format, but you'll have difficulty using it in a query like mine. The right way for me turned out to be to use concact which appends the array with just the value that you set, rather than a k-v pair.
if (req.body.tags){
queryArgs = queryArgs.concat({documentTags: {$all: tags}});
}
let query = {
$and: queryArgs
}

How to convert postgreSQL to Sequelize format

I have to try to convert my postgre sql query to orm concept using sequelize npm package, kindly guide me.
select * from "locationDepartmentMappings" as a
inner join "departments" as b on a."departmentId" = b.id
inner join "locations" as c on a."locationId" = c.id
where (
b."departmentName" like '%Accounting%' or c."locationName" like '%Accounting%'
)
limit 1;
As per below code still i have getting
error: column locationDepartmentMapping.department.departmentName does not exist
As #shivam mentioned i have tried depends mine below, can you what i need to changes,
let ldv = await LocationDepartmentModel.findAll({
include: [
{
model: LocationModel,
as: "location",
required: true,
},
{
model: DepartmentModel,
as: "department",
required: true,
}
],
where: {
$or: [
{
"department.departmentName": { like: `%${reqQueryName}%` }
},
{ "location.locationName": { like: `%${reqQueryName}%` } }
]
},
limit:1
});
Sequelize has pretty good documentation on how to write queries using the orm syntax.
To start with, the above query would look something like
Model.locationDepartmentMappings.findAll({
include: [
{
model: Model.departments
where: {departmentName: {$like: '% Accounting%'}}
},
{
model: Model.locations
where: {locationName: {$like: '% Accounting%'}}
}],
limit: 1
})
What you should consider getting to above query is
1. Learn how to create sequelize models
2. Learn Associations
3. Querying
There are a lot of great tutorials out there, which can help you get started, and are just a google search away!
Final with help got the solutions :
let ldData = await LocationDepartmentModel.findAll({
include: [
{
model: LocationModel,
as: "location",
required: true
},
{
model: DepartmentModel,
as: "department",
required: true
}
],
where: {
$or: [
{ "$department.departmentName$": { like: `%${reqQueryName}%` } },
{ "$location.locationName$": { like: `%${reqQueryName}%` } }
]
},
limit: 1
});
Courtesy for below :
#shivam answer for joining tables
#ManjulSigdel answer for where condition include table column

Mongoose .dot Notation equivalent for $and + $in + $or?

Am looking to '.find' all docs, '.where' the username is '.in' the to: array and either upVoted: true '.or' noVote: true, all sorted by rank descending
Here's an example doc structure:
to: [String],
voting: {
upVoted: Boolean,
noVote: Boolean,
downVoted: Boolean
},
rank: Number
This query is working, but how would this be written in Mongoose .dot notation:
Story.find({
$and: [
{ to: { $in: [ 'user1' ] } },
{ $or: [{ 'voting.upVote': true }, { 'voting.noVote': true }] }
]
}, function (err, stories) {
FYI am working on the correct syntax for sorting this
Your original query can be made simpler, because you don't need $and (it's implicit in MongoDB). Also, if you are looking for only one user from the ṫo array, then you also don't need the $in operator.
{
to: 'user1',
$or: [{ 'voting.upVote': true }, { 'voting.noVote': true }]
}
Using Mongoose query API:
var query = Story.find();
query.where('to', 'user1');
query.or({ 'voting.upVote': true }, { 'voting.noVote': true });
query.exec(function (err, doc) {
if (err) ...
});
Or if you're looking for more than one user then replace:
query.where('to', 'user1');
with:
query.where('to').in(['user1', 'user2']);

Resources