Hi I have following model of a document in mongodb
Schema is
const ProductionsSchema=new Schema({
name: {type: String, required: true, unique: true},
isActive: {type: Boolean, default: true},
locations: [{
name: {type: String},
isActive : {type: Boolean, default: false}
}],
trackno: {type: String}
})
Productions:[{
_id: 125,
name: 'John Smith',
locations: [{ name: 'Boston', isActive: true}]
isActive: true,
trackno: 2123
},
{
_id: 126,
name: 'Moe Adam',
locations: [{ name: 'Chicago', isActive: true}]
isActive: true,
trackno: 5663
},
{
_id: 126,
name: 'Henry Noel',
locations: [{ name: 'Houston', isActive: false}]
isActive: true,
trackno: 4552
},
{
_id: 128,
name: 'Tim Johnson',
locations: [{ name: 'Denver', isActive: true}]
isActive: false,
trackno: 6672
}
]
I am trying to find list of with both isActive true
Productions.find({"isActive" : true , "locations.isActive": true}, (err, list)=>{
if(err){
callback(err);
}
callback(null, list)
})
I am trying to write query so both isActive are true. In above sample data only first two records should be in the answer. But I keep getting all the records even ones with 'false' I even tried $elemMatch on locations.isActive still didnt work.
Please let me know how I can fix this so that I only get result that contains only true values for both isActive.
As the original comment explained, the only query conditions you need are:
{ isActive: true, "locations.isActive": true }
This is a basic AND condition, and you don't need any special operators just to verify a condition is met on a single property anywhere in an array, which is all you are asking.
Since this works exactly as expected, I can only think to show you a full working listing to use as a basis to work out what you are doing differently thus causing you to not get the same result as what is expected.
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/productions';
const opts = { useNewUrlParser: true };
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('debug', true);
const productionSchema = new Schema({
name: String,
isActive: { type: Boolean, default: true },
locations: [{
name: String,
isActive: { type: Boolean, default: false }
}],
trackno: Number
})
const Production = mongoose.model('Production', productionSchema);
const data =
[
{
name: 'John Smith',
locations: [{ name: 'Boston', isActive: true}],
isActive: true,
trackno: 2123
},
{
name: 'Moe Adam',
locations: [{ name: 'Chicago', isActive: true}],
isActive: true,
trackno: 5663
},
{
name: 'Henry Noel',
locations: [{ name: 'Houston', isActive: false}],
isActive: true,
trackno: 4552
},
{
name: 'Tim Johnson',
locations: [{ name: 'Denver', isActive: true}],
isActive: false,
trackno: 6672
}
];
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// clean data
await Promise.all(
Object.entries(conn.models).map(([k, m]) => m.deleteMany())
);
// set up
await Production.insertMany(data);
// Query
let query = { isActive: true, "locations.isActive": true };
let results = await Production.find(query);
log(results);
} catch(e) {
console.error(e)
} finally {
mongoose.disconnect()
}
})()
Which outputs the two expected documents:
Mongoose: productions.deleteMany({}, {})
Mongoose: productions.insertMany([ { isActive: true, _id: 5c7f7e9367daed19d6773e9b, name: 'John Smith', locations: [ { isActive: true, _id: 5c7f7e9367daed19d6773e9c, name: 'Boston' } ], trackno: 2123, __v: 0 }, { isActive: true, _id: 5c7f7e9367daed19d6773e9d, name: 'Moe Adam', locations: [ { isActive: true, _id: 5c7f7e9367daed19d6773e9e, name: 'Chicago' } ], trackno: 5663, __v: 0 }, { isActive: true, _id: 5c7f7e9367daed19d6773e9f, name: 'Henry Noel', locations: [ { isActive: false, _id: 5c7f7e9367daed19d6773ea0, name: 'Houston' } ], trackno: 4552, __v: 0 }, { isActive: false, _id: 5c7f7e9367daed19d6773ea1, name: 'Tim Johnson', locations: [ { isActive: true, _id: 5c7f7e9367daed19d6773ea2, name: 'Denver' } ], trackno: 6672, __v: 0 } ], {})
Mongoose: productions.find({ isActive: true, 'locations.isActive': true }, { projection: {} })
[
{
"isActive": true,
"_id": "5c7f7e9367daed19d6773e9b",
"name": "John Smith",
"locations": [
{
"isActive": true,
"_id": "5c7f7e9367daed19d6773e9c",
"name": "Boston"
}
],
"trackno": 2123,
"__v": 0
},
{
"isActive": true,
"_id": "5c7f7e9367daed19d6773e9d",
"name": "Moe Adam",
"locations": [
{
"isActive": true,
"_id": "5c7f7e9367daed19d6773e9e",
"name": "Chicago"
}
],
"trackno": 5663,
"__v": 0
}
]
Related
I'm trying to create an object according to my model in mongoose using a controller, but I'm running into a problem that the last array nesting with prize patterns is not being written. Theoretically it is, but it is empty. I do not understand what the problem is, since I cannot add the necessary value to it even after creating the object through the constructor and after that doing something like: newGame.prizes = prizes;
With this behavior, all other properties within the prize property appear in the object, but the pattern array is empty.
Here is what my model looks like:
const GameSchema = mongoose.Schema({
type: String,
roundId: String,
userId: String,
calloutNumbersCount: Number,
tickets: [
{
id: String,
columns: [[Number]],
},
],
prizes: [
{
id: String,
name: String,
nameLocaleKey: String,
winAmount: Number,
patterns: [
{
type: String,
count: Number,
},
],
},
],
ticketPrice: Number,
boughtTicketIds: [String],
ended: Boolean,
});
Controller:
function createGame(gameType, userId) {
const currentGameConfig = config[gameType];
console.log("Controller");
currentGameConfig.prizes.map((el) => console.log(el.patterns));
const roundId = nanoid(LENGTH_OF_ROUND_ID);
const game = new Game({
type: currentGameConfig.type,
roundId,
userId,
calloutNumbersCount: currentGameConfig.callout.numbersCount,
tickets: [
{
id: 123, // temp
columns: [[0]], // temp
},
],
prizes: currentGameConfig.prizes,
ticketPrice: currentGameConfig.tickets.price,
boughtTicketIds: ["1", "2"], // temp
ended: false,
});
return game;
}
Place of creation and modification of the object in the route:
if (!game) {
const newGame = gameController.createGame(type, userId);
// newGame.prizes = currentGameConfig.prizes;
Game.addGame(newGame, (err, game) => {
if (err) {
res.json({ success: false, message: err.message });
throw err;
}
return res.json({
roundId: newGame.roundId,
});
});
}
Expected result:
The result:
EDITED
CurrentGameConfig:
{
type: 'BINGO_75',
name: 'Bingo 75',
nameLocaleKey: 'GAMES.BINGO_75',
prizes: [
{
name: 'Prize 1',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_1',
winAmount: 10,
patterns: [Array]
},
{
name: 'Prize 2',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_2',
winAmount: 10,
patterns: [Array]
},
{
name: 'Prize 3',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_3',
winAmount: 10,
patterns: [Array]
},
{
name: 'Prize 4',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_4',
winAmount: 10,
patterns: [Array]
},
{
name: 'Full house',
nameLocaleKey: 'PRIZES.BINGO_75.PRIZE_5',
winAmount: 10,
patterns: [Array]
}
],
tickets: { price: 10, quantity: 72, grid: { cols: [Object], rows: [Object] } },
callout: { numbersCount: 25 }
}
In node.js patterns look like [Array], but if display each pattern it look like:
[
{type: 'HORIZONTAL', count: 1},
{type: 'VERTICAL', count: 1},
{type: 'DIAGONAL', count: 1}
]
I have this function: printRecords which receives an Array of IDs:numbers, and I have to find() and print (before sorting and doing other stuff) the user's object that matches the IDs given as arguments.
So I did the following:
function printRecords(recordIds) {
for (const currId of recordIds) {
const person = studentRecords.find((student) => student.id == currId)
const personArr = Array.of(person)
console.log([...personArr])
}
}
const currentEnrollment = [ 410, 105, 664, 375 ];
var studentRecords = [
{ id: 313, name: "Frank", paid: true, },
{ id: 410, name: "Suzy", paid: true, },
{ id: 709, name: "Brian", paid: false, },
{ id: 105, name: "Henry", paid: false, },
{ id: 502, name: "Mary", paid: true, },
{ id: 664, name: "Bob", paid: false, },
{ id: 250, name: "Peter", paid: true, },
{ id: 375, name: "Sarah", paid: true, },
{ id: 867, name: "Greg", paid: false, },
];
printRecords(currentEnrollment)
I already iterate the argument and found the users object that match ID but now the question is… How I turn it back into an object's array. I thought that Array.of()/ spread would have done it, but instead of taking the objects as a whole it works individually on every one.
the log is:
[ { id: 410, name: 'Suzy', paid: true } ]
[ { id: 105, name: 'Henry', paid: false } ]
[ { id: 664, name: 'Bob', paid: false } ]
[ { id: 375, name: 'Sarah', paid: true } ]
and I need:
[{ id: 410, name: 'Suzy', paid: true },
{ id: 105, name: 'Henry', paid: false },
{ id: 664, name: 'Bob', paid: false },
{ id: 375, name: 'Sarah', paid: true } ]
Thanks in advance.
You can simplify the logic by using filter() and includes() to find the matches. This will already return the array you need without any further amendments:
function printRecords(recordIds) {
let personArr = studentRecords.filter(record => recordIds.includes(record.id));
console.log(personArr)
}
const currentEnrollment = [410, 105, 664, 375];
var studentRecords = [
{ id: 313, name: "Frank", paid: true, },
{ id: 410, name: "Suzy", paid: true, },
{ id: 709, name: "Brian", paid: false, },
{ id: 105, name: "Henry", paid: false, },
{ id: 502, name: "Mary", paid: true, },
{ id: 664, name: "Bob", paid: false, },
{ id: 250, name: "Peter", paid: true, },
{ id: 375, name: "Sarah", paid: true, },
{ id: 867, name: "Greg", paid: false, },
];
printRecords(currentEnrollment)
My issue today is with MongoDB and mongoose in javascript. So first off I have this schema:
var playerModel = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "Users",
},
class: {
type: String,
required: true,
},
level: {
type: Number,
default: 1,
},
spells: {
type: [String],
default: ["", "", "", "", ""],
},
inventory: [], <- IMPORTANT
toolbelt: {
axe: [{}],
pickaxe: [{}],
fishingrod: [{}],
hammer: [{}],
saw: [{}],
sewingkit: [{}],
knife: [{}],
gemkit: [{}],
food: [{}],
potion: [{}],
},
gear: {
weapon: [{}],
head: [{}],
chest: [{}],
gloves: [{}],
legs: [{}],
shoes: [{}],
earrings: [{}],
ring: [{}],
necklace: [{}],
},
gold: {
type: Number,
default: 0,
},
bagSize: {
type: Number,
default: 20,
},
skills: {
type: [userSkills],
default: defaultSkills,
},
});
and my problem is with inventory: [].
In my code I manually push items in my array which save properly in the database, but whenever I wanna modify a field of one of my array object, it won't update it like a qty for example:
player.inventory[i].qty += 1; //Where player is my model containing my inventory
...
player.save()
.then((doc) => console.log("DOC", doc))
.catch((err) => console.log("ERR", err));
and the doc returned look something like this:
...
inventory: [
{
_id: 626a1468cdbeced19102942d,
slug: 'crude_campfire',
qty: 1,
charges: 19,
efficiency: -0.2
},
{ _id: 626a14e7530afad2c5c1894c, slug: 'wheat', qty: 1 },
{ _id: 626a14f1530afad2c5c1894d, slug: 'carrot', qty: 1 },
{ _id: 626a150f530afad2c5c1894e, slug: 'potato', qty: 1 },
{ _id: 626a155b530afad2c5c1894f, slug: 'red_bream', qty: 1 },
{ _id: 626a15b5530afad2c5c18950, slug: 'bone', qty: 1 },
{ _id: 626a15c9530afad2c5c18951, slug: 'clam_shell', qty: 1 },
{ _id: 626a15d5530afad2c5c18952, slug: 'stone', qty: 8 },
{ _id: 626a15df530afad2c5c18953, slug: 'taeroot', qty: 1 },
{ _id: 626a15e9530afad2c5c18954, slug: 'shiny_pendant', qty: 1 },
{ _id: 626a15fd530afad2c5c18955, slug: 'sickleweed', qty: 1 },
{ _id: 626a1625530afad2c5c18956, slug: 'ceruleaf', qty: 1 },
{ _id: 626a1dba272403ea21fb71ef, slug: 'stone', qty: 1 },
{ _id: 626a1e4030a144f8179789e0, slug: 'birch_log', qty: 22 }, <- IMPORTANT
{ _id: 626a1e72372733f90cfdc003, slug: 'tree_twig', qty: 2 },
{ _id: 626a1e9a372733f90cfdc004, slug: 'honey', qty: 2 }
],
...
where you can see that my birch_log qty is now at 22. But whenever I go check in the database,
I don't know what's going on, and I'm kind of tired right, so I'll go to bed hoping someone can save me tomorrow :)
Thanks and have a good night ! :)
To update the object having {slug: birch_log}, you can use the following query:
player.update( {user : 123456 , inventory.slug : "birch_log" } ,
{$inc : {"inventory.$.qty" : 1} });
instead of using save() with promise, you can use update() query with async await. It will be short and clean code.
This answer fixed my problem
Since i'm using an "anonymous" array or whatever, mongoose doesn't know what changes to track in it so I need to tell it that it's been modified !
I am using MongoDB with mongoose as ORM library for my NodeJS project. Here I am putting two versions of same query, the problem is NoSqlBooster version is returning value correctly, where mongoose version is returning empty array, Please find the both versions of the query.
NoSQLBooster Version:
db.users.find({
isDeleted: false,
skills: { '$in': [ ObjectId('613b9951e42a5203c421cbcc'), ObjectId('613b9951e42a5203c421cbcd') ] },
interests: { '$in': [ ObjectId('613b9951e42a5203c421cbce'), ObjectId('613b9951e42a5203c421cbcf') ] },
university: { '$in': [ ObjectId('613b9951e42a5203c421cbd0'), ObjectId('613b9951e42a5203c421cbd1') ] },
title: { '$in': [ ObjectId('613b785b7bacfe1336ab9dbf'), ObjectId('613b9951e42a5203c421cbd2') ] }
});
which is working perfectly, but when I am pushing the same query on mongoose find() its returning empty array. Please find the mongoose version below.
{
isDeleted: false,
skills: { '$in': [ '613b9951e42a5203c421cbcc', '613b9951e42a5203c421cbcd' ] },
interest: { '$in': [ '613b9951e42a5203c421cbce', '613b9951e42a5203c421cbcf' ] },
university: { '$in': [ '613b9951e42a5203c421cbd0', '613b9951e42a5203c421cbd1' ] },
title: { '$in': [ '613b785b7bacfe1336ab9dbf', '613b9951e42a5203c421cbd2' ] }
}
Please note, above is the request I get from frontend itself, I am just pushing it to find.
Please find the schema structure for these specific fields,
{
skills: [
{ type: schema.Types.ObjectId, ref: "Skills" }
],
interests: [
{ type: schema.Types.ObjectId, ref: "Interests" }
],
university: [{ type: schema.Types.ObjectId, ref: "University" }],
title: [{ type: schema.Types.ObjectId, ref: "Titles"}]
}
here is the find() function that I am using,
if (
this.req.body &&
this.req.body.filterParam &&
!_.isEmpty(this.req.body.filterParam)
) {
makeQuery = JSON.parse(JSON.stringify(this.req.body.filterParam));
}
if(_.isEmpty(this.req.body.searchText) && _.isEmpty(this.req.body.filterParam)) {
makeQuery = { isDeleted: false };
}
console.log(makeQuery);
let getListOfUsers = await Users.find(makeQuery)
.sort(sort)
.skip(skip)
.limit(perPage)
.populate("skills", { type: 1 }, { isDeleted: false })
.populate("interests", { type: 1 }, { isDeleted: false })
.populate("university", { type: 1 }, { isDeleted: false })
.populate("business_tags", { type: 1 }, { isDeleted: false })
.populate("title", { type: 1 }, { isDeleted: false })
.populate("likesUserManagement", { likedFlag: 1 }, { isDeleted: false, likedFlag: true, createdBy: currentUser })
.select({
password: 0,
status: 0,
emailVerificationStatus: 0,
isProfileCompleted: 0,
isDeleted: 0,
previouslyUsedPasswords: 0,
ifForgetOtpVerified: 0,
portfolio_name: 0,
portfolio_s3_name: 0,
image_name: 0,
createdAt: 0,
updatedAt: 0,
__v: 0,
});
Can anyone help me to solve this issue. I am stuck with this. Please help me. Thanks.
I'm trying to do an inner join that matches, but for some reason I get a left join.
I have 2 relations tables, and I want to get the movie with the genre names.
Consider the following models:
// Movie
const MovieSchema = new mongoose.Schema({
id: {
type: Number,
default: null,
required: true,
unique: true
},
title: {
type: String,
default: null,
required: true,
unique: true,
trim: true
},
});
const Movie = mongoose.model('Movie', MovieSchema);
module.exports = Movie;
// Genre
const GenreSchema = new mongoose.Schema({
id: {
type: Number,
default: null,
required: true,
unique: true
},
name: {
type: String,
default: null,
required: false,
trim: true,
unique: true
}
});
const Genre = mongoose.model('Genre', GenreSchema);
module.exports = Genre;
// MovieGenre
const MovieGenreSchema = new mongoose.Schema({
genreId: {
type: Number,
default: null,
required: true
},
movieId: {
type: Number,
default: null,
required: true
}
});
const MovieGenre = mongoose.model('MovieGenre', MovieGenreSchema);
module.exports = MovieGenre;
I try to do the following query:
{
$lookup:
{
from: MovieGenre.collection.name,
localField: 'id',
foreignField: 'movieId',
as: 'movieGenres'
}
},
{
$lookup: {
from: Genre.collection.name,
localField: 'g.id',
foreignField: 'genreId',
as: 'genreNames'
}
},
{
$match: {
'genreNames.name': 'Action'
}
}
and I get the results:
{
_id: 5ee9b51609f44c0f38262c94,
id: 26583,
title: 'The Keeper',
__v: 0,
movieGenres: [
{
_id: 5ee8b8cf0d186c20b4bf3ccd,
genreId: 28,
movieId: 26583,
__v: 0
},
{
_id: 5ee8b8cf0d186c20b4bf3cce,
genreId: 53,
movieId: 26583,
__v: 0
}
],
genreNames: [
{ _id: 5ee8b68f0d186c20b4b03a3d, id: 35, name: 'Comedy', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a3e, id: 80, name: 'Crime', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a40, id: 18, name: 'Drama', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a42, id: 53, name: 'Thriller', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a43, id: 28, name: 'Action', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a45, id: 14, name: 'Fantasy', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a46, id: 27, name: 'Horror', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a4a, id: 10752, name: 'War', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a4b, id: 10402, name: 'Music', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a4c, id: 37, name: 'Western', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a4d, id: 36, name: 'History', __v: 0 }
]
}
but what i expected to get is:
{
_id: 5ee9b51609f44c0f38262c94,
id: 26583,
title: 'The Keeper',
__v: 0,
movieGenres: [
{
_id: 5ee8b8cf0d186c20b4bf3ccd,
genreId: 28,
movieId: 26583,
__v: 0
},
{
_id: 5ee8b8cf0d186c20b4bf3cce,
genreId: 53,
movieId: 26583,
__v: 0
}
],
genreNames: [
{ _id: 5ee8b68f0d186c20b4b03a43, id: 28, name: 'Action', __v: 0 },
{ _id: 5ee8b68f0d186c20b4b03a42, id: 53, name: 'Thriller', __v: 0 },
]
}
Can you please tell me,
What am I doing wrong?
Thanks.
You just need to correct your second lookup with genre collection, i have added 2 approaches you can use anyone,
1) Using your approach:
localField pass previous lookup result's movieGenres.genreId
foreignField pass id of genre collection
{
$lookup: {
from: Genre.collection.name,
localField: "movieGenres.genreId",
foreignField: "id",
as: "genreNames"
}
}
if you want to filter the genreNames names from above lookup by name,
$filter to iterate loop of genreNames array and filter by name: Action
{
$addFields: {
genreNames: {
$filter: {
input: "$genreNames",
cond: { $eq: ["$$this.name", "Action"] }
}
}
}
}
Your final query would be,
{
$lookup: {
from: MovieGenre.collection.name,
localField: "id",
foreignField: "movieId",
as: "movieGenres"
}
},
{
$lookup: {
from: Genre.collection.name,
localField: "movieGenres.genreId",
foreignField: "id",
as: "genreNames"
}
},
{
$match: {
"genreNames.name": "Action"
}
},
{
$addFields: {
genreNames: {
$filter: {
input: "$genreNames",
cond: { $eq: ["$$this.name", "Action"] }
}
}
}
}
Playground
2) Using lookup with pipeline approach:
The alternate way to do this using lookup with pipeline,
let to pass movieGenres.genreId from above lookup
$match to match genreId using $expr expression match and name field and combine conditions using $and operations
{
$lookup: {
from: MovieGenre.collection.name,
localField: "id",
foreignField: "movieId",
as: "movieGenres"
}
},
{
$lookup: {
from: Genre.collection.name,
let: { genreIds: "$movieGenres.genreId" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $in: ["$id", "$$genreIds"] } },
{ name: "Action" }
]
}
}
],
as: "genreNames"
}
}
Playground