I've struggling trying to make my mongoose query return an array, since I need to:
Find 0..N docs from one collection;
Save this found array in another nested doc collection;
My code:
CarIndex.find({ '_id': { $in: ids } }, 'carID carBrand carModel location')
.then(collections => {
const out = []
collections.map( (doc) => {
const {carID ,carBrand ,carModel ,location} = doc
const car = {carID ,carBrand ,carModel ,location};
out.push(car)
})
console.log(out)
console.log(out.length)
console.log(typeof out)
return CarCollection.findOneAndUpdate({ '_id': collectionId }, { $addToSet: { carCollection: { $each: { out } } } })
});
The output error:
[04/01/2018 11:04:48.980] [LOG]
[ { carID: 'd82e41b0-f14f-11e7-b845-990cb852c2b3',
carBrand: 'Peugeot',
carModel: '207 Sed. Passion XR Sport 1.4 Flex 8V 4p',
location: [-23.539727799999998,-46.5111749] },
{ carID: 'd82f2c10-f14f-11e7-b845-990cb852c2b3',
carBrand: 'Nissan',
carModel: 'Sentra 2.0/ 2.0 Flex Fuel 16V Mec.',
location: [-23.607240972099525,-46.72912079051677] } ]
[04/01/2018 11:04:48.982] [LOG] 2
[04/01/2018 11:04:48.983] [LOG] object
[04/01/2018 11:04:48.997] [ERROR] MongoError: The argument to $each in
$addToSet must be an array but it was of type object
you can do like this
CarIndex.find({ '_id': { $in: ids } }, 'carID carBrand carModel location')
.then(collections => {
const out = []
collections.map( (doc) => {
const {carID ,carBrand ,carModel ,location} = doc
const car = {carID ,carBrand ,carModel ,location};
out.push(car)
})
console.log(out)
console.log(out.length)
console.log(typeof out)
return CarCollection.findOneAndUpdate({ '_id': collectionId }, { $addToSet: { carCollection: { $each: out } } })
});
Related
My code look like this:
const express = require('express');
const mongoose= require('mongoose');
const Schema = mongoose.Schema;
const ourDataSchema = new Schema ({
rank : Number,
totalPoints : Number
});
const rankTotalpoint = mongoose.model("rankTotalpoint", ourDataSchema);
const ourData = [
{rank :1, totalPoints : 2000},
{rank :2, totalPoints : 1980},
{rank :3, totalPoints : 1940},
{rank :4, totalPoints : 1890},
{rank :5, totalPoints : 1830},
{rank :6, totalPoints : 1765}
];
rankTotalpoint.create(ourData, function (error) {
console.log('saved!');
if (error) {
console.log(error)
}
});
...
I have 2 questions:
How can I fetch a rank by it's totalPoint?
I tried find() but it didn't work, I guess I'm using it in a wrong way.
I'm getting an input(Number) from user, I want to match the number with the totalPoint(if exists) that we fetched from our database and return it's rank, and if the exact number didn't exist, I want to match it with the closest totalPoint in our database and return the rank as a response.
Please at least answer my first question!
Highly appreciate your answers guys,
I'm STUCK!
These are my controller file codes where I'm getting the user input and passed it to the code that you send me which I put them in my model files.
const express = require('express');
const model = require('../model/logic');
exports.index = (req, res, next) => {
res.status(200).json({message : 'INSERT INPUTS HERE'});
};
exports.getUserData = (req, res, next) => {
const literature = req.body.literature * 4;
const arabic = req.body.arabic * 2;
const religion = req.body.religion * 3;
const english = req.body.english * 2;
const math = req.body.math * 4;
const physics = req.body.physics * 3;
const chemistry = req.body.chemistry *2;
const TOTALPOINT = literature + arabic + religion + english + math + physics + chemistry;
let result = model.result(TOTALPOINT);
res.status(200).json(result);
};
And this is my model/logic file which I imported them into controller above:
const express = require('express');
const mongoose= require('mongoose');
const Schema = mongoose.Schema;
const ourDataSchema = new Schema ({
rank : Number,
totalPoints : Number
});
const rankTotalpoint = mongoose.model("rankTotalpointData", ourDataSchema);
const ourData = [
{rank : 1, totalPoints : 2000},
{rank : 2, totalPoints : 1980},
{rank: 3, totalPoints : 1940},
{rank:4, totalPoints : 1890},
{rank :5, totalPoints : 1830},
{rank : 6, totalPoints : 1765},
{rank : 7, totalPoints : 1600}
];
rankTotalpoint.create(ourData, function (error, data) {
if (error) {
console.log(error)
}
else {
console.log('saved!');
}
});
exports.result = function (param) {
const finalResult = rankTotalpoint.aggregate([
{
$project: {
diff: {
$abs: {
$subtract: [
param, // <<<----------------------- THIS IS THE USER SUPPLIED VALUE
"$totalPoints"
]
}
},
doc: "$$ROOT"
}
},
{
$sort: {
diff: 1
}
},
{
$limit: 1
},
{
$project: {
_id: 0,
rank: "$doc.rank"
}
}
])
return finalResult;
}
When I'm testing my app with postman I get this response in there :
{
"_pipeline": [
{
"$project": {
"diff": {
"$abs": {
"$subtract": [
16,
"$totalPoints"
]
}
},
"doc": "$$ROOT"
}
},
{
"$sort": {
"diff": 1
}
},
{
"$limit": 1
},
{
"$project": {
"_id": 0,
"rank": "$doc.rank"
}
}
],
"options": {}
}
Question #1:
To find a rank by its totalPoint, you can do:
You can check out a live demo here
db.collection.find({
totalPoints: 2000 // <<<------------------ The exact value you want to find
},
{
_id: 0,
rank: 1
})
Question #2:
To find closest rank, by a user supplied totalPoints value, you should be able to use the following query...
You can check out a live demo here
db.collection.aggregate([
{
$project: {
diff: {
$abs: {
$subtract: [
1800, // <<<----------------------- THIS IS THE USER SUPPLIED VALUE
"$totalPoints"
]
}
},
doc: "$$ROOT"
}
},
{
$sort: {
diff: 1
}
},
{
$limit: 1
},
{
$project: {
_id: 0,
rank: "$doc.rank"
}
}
])
UPDATE/FINAL ANSWER:
Your issue is because Mongoose is promise/async based. You are not awaiting anything, so your code returns a variable that has not been set yet by your query..
I was testing using 2 files: myMongoose.js and index.js..
// myMongoose.js
// ** CODE THAT SAVES DATA TO DATABASE HAS BEEN REMOVED FOR BREVITY **
require('dotenv').config();
const mongoose = require('mongoose');
const RankTotalpointSchema = new mongoose.Schema({
rank: Number,
totalPoints: Number
});
mongoose.set('useCreateIndex', true);
const mongoConnection = mongoose.createConnection(process.env.MONGO_DB_STRING, {
useUnifiedTopology: true,
useNewUrlParser: true,
useFindAndModify: false,
});
const RankTotalpoint = mongoConnection.model("RankTotalpoint", RankTotalpointSchema, 'Testing');
/**
* ~~~~~~ **** THIS HAS TO BE AN ASYNC FUNCTION **** ~~~~~~
*/
exports.result = async function (param) {
const finalresult = await RankTotalpoint.aggregate([{
$project: {
diff: {
$abs: {
$subtract: [
param, // <<<----------------------- THIS IS THE USER SUPPLIED VALUE
"$totalPoints"
]
}
},
doc: "$$ROOT"
}
},
{
$sort: {
diff: 1
}
},
{
$limit: 1
},
{
$project: {
_id: 0,
rank: "$doc.rank"
}
}
])
return finalresult;
};
...and then in index.js:
// index.js
const { result } = require('./myMongoose');
// Use it like this:
async function init() {
try {
const d = await result(1800);
console.log(d);
} catch (err) {
console.error(err);
}
}
init(); // -> [ { rank: 5 } ]
// --------------------------------------------------------------------
// ...or like this:
(async () => {
try {
const d = await result(1800);
console.log(d); // -> [ { rank: 5 } ]
} catch (err) {
console.error(err);
}
})()
// --------------------------------------------------------------------
// ...or like this:
result(1800)
.then(d => console.log(d)) // -> [ { rank: 5 } ]
.catch(err => console.error(err))
I'm having issues adding an array of objects to mongodb. The problem is related to when I get the posts ngOnInit() and there is an entry for _id like this before I start adding anything to inviteGroup
If I add this.inviteGroup = [] to get rid of _id first entry, then I can successfully add to the database my invite like in this image. Is there a way to not have that _id that's related to my mongoose schema?
but naturally the this.inviteGroup = [] makes it so I can have only one entry at a time since it erases everything on page load. How can I make that _id entry not there anymore so that when I do a .push() it doesn't cause a page reload because it throws off the .push(). I want to have multiple entries in db for each invite. Is it my mongoose model that's the issue? I appreciate any help!
mongoose schema definition
inviteGroup: {
bidderId: { type: String, lowercase: true, trim: true },
username: { type: String, lowercase: true, trim: true }
}
app.js
app.patch("/api/listings/:id", (req, res) => {
console.log("INVITE GRdddOUP IS");
console.log(req.body);
console.log(req.body[0].biddingUserId);
let invites;
if (req.body[0].biddingUserId) {
invites = req.body;
console.log("INVITE IS");
}
console.log(invites);
if (invites) {
console.log("INVITE GROUP IS");
console.log(req.params.id);
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
inviteGroup: invites
},
function(err, docs) {
if (err) {
console.log(err);
res.json(err);
} else {
return true;
console.log(docs);
}
}
);
component.ts
import {
Component,
OnInit,
ViewChild,
OnDestroy,
AfterViewInit
} from "#angular/core";
import { Router } from "#angular/router";
import {
MatTableDataSource,
MatPaginator,
MatSort,
MatDialog
} from "#angular/material";
import { NgForm, FormControl } from "#angular/forms";
import { SubmitListingService } from "../submit-listing/submit-auction.service";
import { BidderInvite } from "./bidder-invite.model";
import { Observable, Subject } from "rxjs";
import { startWith, map, takeUntil } from "rxjs/operators";
import { Page } from "ngx-pagination/dist/pagination-controls.directive";
import { BidderInviteRetrieved } from "./bidder-invite-retrieved";
#Component({
selector: "app-private-auction-invite",
templateUrl: "./private-auction-invite.component.html",
styleUrls: ["./private-auction-invite.component.css"]
})
export class PrivateAuctionInviteComponent
implements OnInit, AfterViewInit, OnDestroy {
allMyPeopleAreInvited: boolean;
auctionId: string;
dataSource: MatTableDataSource<any> = new MatTableDataSource();
timeout: any = null;
posts: BidderInviteRetrieved[];
artistId: string;
bidderId: string;
inviteGroup: BidderInvite[] = [];
test: any[] = [];
value: string;
usernameFound: string;
userSearched: string;
invites: BidderInvite[] = [];
destroy = new Subject();
inviteName: string;
filteredOptions: Observable<string[]>;
myControl = new FormControl();
selectedValue: string;
url: string;
displayedColumnsInvites: string[] = ["User", "revokeInvite"];
options: string[] = [];
#ViewChild(MatSort, { static: false }) set sort(sort: MatSort) {
this.dataSource.sort = sort;
}
#ViewChild(MatPaginator, { static: false }) set paginator(
paginator: MatPaginator
) {
this.dataSource.paginator = paginator;
}
constructor(
private router: Router,
private submitListingService: SubmitListingService
) {}
ngOnInit() {
this.inviteGroup = [];
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.allMyPeopleAreInvited = false;
this.url = this.router.url;
const value = this.router.url.split("/");
this.auctionId = value[2];
this.artistId = value[3];
this.submitListingService
.getPrivateAuctionInviteList(this.auctionId)
.pipe(takeUntil(this.destroy))
.subscribe(res => {
this.inviteGroup = res.posts;
console.log("res");
console.log(res);
console.log(this.inviteGroup);
if (this.inviteGroup["_id"].length > 2) {
this.inviteGroup = [];
console.log(this.inviteGroup);
}
});
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(""),
map(value => this._filter(value))
);
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.dataSource = new MatTableDataSource(this.inviteGroup);
this.dataSource.data = this.inviteGroup;
}
sendInvite(form: NgForm) {
if (form.invalid) {
return;
}
let counter: number;
counter = 0;
console.log("USER " + this.value);
console.log("POST LEGNTH: " + this.posts.length);
for (let i = 0; i < this.posts.length; i++) {
counter = counter++;
console.log("post");
console.log(form.value.username);
let user = this.posts[i].username.trim().toLowerCase();
let enteredUser = form.value.username.trim().toLowerCase();
console.log("COUNTER LOOP NUMBER: " + counter);
if (enteredUser === user) {
this.bidderId = this.posts[i].id;
console.log(this.inviteGroup);
let invites = this.inviteGroup;
console.log("INVITE LENGTH = " + this.inviteGroup.length);
console.log(invites.indexOf);
this.inviteGroup.push({
biddingUserId: this.bidderId,
username: this.posts[i].username
});
console.log(this.inviteGroup);
console.log("invite group");
console.log(this.inviteGroup);
//this.posts = [];
this.dataSource.data = this.inviteGroup;
console.log("invite group");
}
}
console.log("BIDDER ID " + this.bidderId);
if (this.bidderId === null || this.bidderId === undefined) {
console.log("SOMETHING WENT WRONG");
}
console.log("made it to next section");
let invites = this.inviteGroup;
console.log("invites[0].username");
console.log("filtering....");
invites = invites.filter((obj, pos, arr) => {
return (
arr.map(mapObj => mapObj["bidderId"]).indexOf(obj["bidderId"]) === pos
);
});
console.log("invites");
console.log(invites);
this.submitListingService
.sendPrivateAuctionInvite(this.auctionId, invites)
.pipe(takeUntil(this.destroy))
.subscribe(res => {
console.log("res");
console.log(res);
});
}
private onKeySearch(event: any) {
console.log("EVENT IS ");
console.log(event);
clearTimeout(this.timeout);
var $this = this;
this.timeout = setTimeout(function() {
if (event.keyCode !== 13) {
$this.executeListing(event.target.value);
}
}, 1000);
}
private executeListing(bidderName: string) {
console.log("BIDDERNAME");
console.log(bidderName);
if (bidderName === "[Object object]") {
return;
}
if (bidderName.length < 4) {
return;
}
if (bidderName.length > 3) {
this.submitListingService
.getUserIdAutoComplete(bidderName)
.pipe(takeUntil(this.destroy))
.subscribe(res => {
console.log("res");
console.log(res);
this.posts = res.posts;
console.log(this.posts);
// this.artists = res.posts;
});
}
}
private _filter(value: string): string[] {
const filterValue = value.toLowerCase();
return this.options.filter(
option => option.toLowerCase().indexOf(filterValue) === 0
);
console.log("OPTION IS " + filterValue);
}
storeUserPrivaeAuctionInvite(user: Page) {
console.log("USER VALUE I S" + user);
}
ngOnDestroy() {
this.destroy.next();
this.destroy.complete();
}
}
angular service
sendPrivateAuctionInvite(id: string, inviteGroup1: BidderInvite[]) {
// console.log(inviteGroup1);
return this.http.patch(
`http://localhost:3000/api/listings/${id}/`,
inviteGroup1
);
}
BidderInvite model
export interface BidderInvite {
biddingUserId: string;
username: string;
}
Is your schema definition supposed to be:
inviteGroup: {
type: [inviteSchema]
default: undefined //if you want to unset []
}
invite: {
bidderId: { type: String, lowercase: true, trim: true },
username: { type: String, lowercase: true, trim: true }
}
(See https://mongoosejs.com/docs/schematypes.html#arrays)
Try caching to prevent a reload. (See https://github.com/isaacs/node-lru-cache) and How to stop MongoDB from reloading data every time I refresh a page?
Try projection to exclude _id in a query output with _id: 0. See https://docs.mongodb.com/v3.2/tutorial/project-fields-from-query-results/#return-all-but-the-excluded-field
Try this. Hope it will help you.
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
inviteGroup: invites
},
{select: {_id: 0}}, // sets the document fields to return
function(err, docs) {
if (err) {
console.log(err);
res.json(err);
} else {
return true;
console.log(docs);
}
}
);
If I understood your question right, the problem is related to the lack of operator at the update method.
When you use something like:
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
inviteGroup: invites // <-- Update statement
},
function(err, docs) {
//...
}
);
It will replace the full value of the inviteGroup field.
In order to add an item to an existent array on the database, you will need to use $push or $addToSet operator, along with $each operator.
The $push and $addToSet operators, only add/append one item per time, so the use of $each is necessary to interact with every item present on the invites array. In the following examples, I will include that because I believe is what you will need. But please, take the time to read the linked documentation of every operator so you can find more samples.
The $push operator appends a specified value to an array, making no extra verification if the value that is been added already exists on the field or not. As:
//document on mongodb, before the update
// { _id : "1", inviteGroup : [] }
//Invites from the request
// invites = [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ];
//update method
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $push : { inviteGroup: { $each : invites } } },
function(err, docs) {
//...
}
);
//document on mongodb, after the update
/*
{
_id : "1",
inviteGroup : [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ]
}
*/
If you call the update method again with the same values:
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $push : { inviteGroup: { $each : invites } } },
function(err, docs) { }
);
// the end document will be like:
/*
{
_id : "1",
inviteGroup : [
{ bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"},
{ bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"}
]
}
*/
In the same way, the $addToSet operator adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array. Like:
//document on mongodb, before the update
// { _id : "1", inviteGroup : [] }
//Invites from the request
// invites = [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ];
//update method
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $addToSet : { inviteGroup: { $each : invites } } },
function(err, docs) {
//...
}
);
//document on mongodb, after the update
/*
{
_id : "1",
inviteGroup : [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ]
}
*/
If you call the update method again with the same values:
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $addToSet : { inviteGroup: { $each : invites } } },
function(err, docs) { }
);
//the end document will be the same because the same value was already on the list:
/*
{
_id : "1",
inviteGroup : [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ]
}
*/
Well, I hope that was what you looking for. =]
I have this model:
const recordSchema = new Schema({
user: {
type: Schema.ObjectId,
ref: 'Person',
required: true
},
date: {
type: Date,
default: Date.now()
},
time: {
type: Date,
default: Date.now()
}
});
So, when I make a HTTP Get request I receive an array of records:
[{/*record1*/}, {/*record2*/}, ...]
The point is that I'm using aggregation to get the number of records of each user (I got that cover), but I would like merge this with the find results to receive something like this:
{
"records": [{/*record1*/}, {/*record2*/}, ...],
"stats": [
{
"_id" : ObjectId("5b6393f2a1d3de31d9547f63"),
"count" : 3.0
},
{
"_id" : ObjectId("5b5d22d8b6195d1b6a5d2574"),
"count" : 17.0
}
]
}
So, how do I get this?
Note: I'm using this for data for some charts, should I handle this on node or on the front-end?
This can be done in the back-end and if you're using MongoDB Server version 3.4.4 or greater in your backend, $facet aggregate pipeline should cover your needs.
Consider the following example which runs two aggregate queries in the same aggregate pipeline using $facet: one returns the records for today and the other returns the counts for each user in the collection:
let start = new Date();
start.setHours(0,0,0,0);
let end = new Date();
end.setHours(23,59,59,999);
Record.aggregate([
{ '$facet': {
'records': [
{ '$match': {
'date': {
'$gte': start,
'$lte': end
}
} }
],
'stats': [
{ '$group': {
'_id': '$user',
'count': { '$sum': 1 }
} }
]
} }
]).exec((err, results) => {
if (err) {
console.error(err);
throw new Error(err);
}
const data = results[0];
console.log(JSON.stringify(data, null, 4));
})
For MongoDB 3.2 and below
1. Using Promises
const recordsQuery = Record.find({ 'date': {
'$gte': start, // date object representing start of today
'$lte': end // date object representing end of today
} }).lean().exec();
const statsQuery = Record.aggregate([
{ '$group': {
'_id': '$user',
'count': { '$sum': 1 }
} }
]).exec();
Promise.all([ recordsQuery, statsQuery ]).then(([ recordsData, statsData ]) => {
const records = recordsData[0];
const stats = statsData[0];
const data = { records, stats };
console.log(JSON.stringify(data, null, 4));
}).catch(err => console.error(err));
2. Using async/await
(async () => {
try {
const recordsQuery = await Record.find({ 'date': {
'$gte': start, // date object representing start of today
'$lte': end // date object representing end of today
} }).lean().exec();
const statsQuery = await Record.aggregate([
{ '$group': {
'_id': '$user',
'count': { '$sum': 1 }
} }
]).exec();
const records = recordsQuery[0];
const stats = statsQuery[0];
const data = { records, stats };
console.log(JSON.stringify(data, null, 4));
} catch (err) {
console.error(err);
}
})();
You can use the $lookup operator to link the original records for the users by Id
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
I have a collection in mongoDb which looks like this
{
"slno" : NumberInt(1),
"name" : "Item 1"
}
{
"slno" : NumberInt(2),
"name" : "Item 2"
}
{
"slno" : NumberInt(3),
"name" : "Item 3"
}
I am receiving a request from angularJs frontend to update this collection to
{
"slno" : NumberInt(1),
"name" : "Item 3"
}
{
"slno" : NumberInt(2),
"name" : "Item 1"
}
{
"slno" : NumberInt(3),
"name" : "Item 2"
}
I am using Mongoose 5.0 ORM with Node 6.11 and express 4.15. Please help me find the best way to achieve this.
You basically want bulkWrite(), which can take the input array of objects and use it to make a "batch" of requests to update the matched documents.
Presuming the array of documents is being sent in req.body.updates, then you would have something like
const Model = require('../models/model');
router.post('/update', (req,res) => {
Model.bulkWrite(
req.body.updates.map(({ slno, name }) =>
({
updateOne: {
filter: { slno },
update: { $set: { name } }
}
})
)
})
.then(result => {
// maybe do something with the WriteResult
res.send("ok"); // or whatever response
})
.catch(e => {
// do something with any error
})
})
This sends a request given the input as:
bulkWrite([
{ updateOne: { filter: { slno: 1 }, update: { '$set': { name: 'Item 3' } } } },
{ updateOne: { filter: { slno: 2 }, update: { '$set': { name: 'Item 1' } } } },
{ updateOne: { filter: { slno: 3 }, update: { '$set': { name: 'Item 2' } } } } ]
)
Which efficiently performs all updates in a single request to the server with a single response.
Also see the core MongoDB documentation on bulkWrite(). That's the documentation for the mongo shell method, but all the options and syntax are exactly the same in most drivers and especially within all JavaScript based drivers.
As a full working demonstration of the method in use with mongoose:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const testSchema = new Schema({
slno: Number,
name: String
});
const Test = mongoose.model('Test', testSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
const data = [1,2,3].map(n => ({ slno: n, name: `Item ${n}` }));
const request = [[1,3],[2,1],[3,2]]
.map(([slno, n]) => ({ slno, name: `Item ${n}` }));
mongoose.connect(uri)
.then(conn =>
Promise.all(Object.keys(conn.models).map( k => conn.models[k].remove()))
)
.then(() => Test.insertMany(data))
.then(() => Test.bulkWrite(
request.map(({ slno, name }) =>
({ updateOne: { filter: { slno }, update: { $set: { name } } } })
)
))
.then(result => log(result))
.then(() => Test.find())
.then(data => log(data))
.catch(e => console.error(e))
.then(() => mongoose.disconnect());
Or for more modern environments with async/await:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const testSchema = new Schema({
slno: Number,
name: String
});
const Test = mongoose.model('Test', testSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
const data = [1,2,3].map(n => ({ slno: n, name: `Item ${n}` }));
const request = [[1,3],[2,1],[3,2]]
.map(([slno,n]) => ({ slno, name: `Item ${n}` }));
(async function() {
try {
const conn = await mongoose.connect(uri)
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await Test.insertMany(data);
let result = await Test.bulkWrite(
request.map(({ slno, name }) =>
({ updateOne: { filter: { slno }, update: { $set: { name } } } })
)
);
log(result);
let current = await Test.find();
log(current);
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Which loads the initial data and then updates, showing the response object ( serialized ) and the resulting items in the collection after the update is processed:
Mongoose: tests.remove({}, {})
Mongoose: tests.insertMany([ { _id: 5b1b89348f3c9e1cdb500699, slno: 1, name: 'Item 1', __v: 0 }, { _id: 5b1b89348f3c9e1cdb50069a, slno: 2, name: 'Item 2', __v: 0 }, { _id: 5b1b89348f3c9e1cdb50069b, slno: 3, name: 'Item 3', __v: 0 } ], {})
Mongoose: tests.bulkWrite([ { updateOne: { filter: { slno: 1 }, update: { '$set': { name: 'Item 3' } } } }, { updateOne: { filter: { slno: 2 }, update: { '$set': { name: 'Item 1' } } } }, { updateOne: { filter: { slno: 3 }, update: { '$set': { name: 'Item 2' } } } } ], {})
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"insertedIds": [],
"nInserted": 0,
"nUpserted": 0,
"nMatched": 3,
"nModified": 3,
"nRemoved": 0,
"upserted": [],
"lastOp": {
"ts": "6564991738253934601",
"t": 20
}
}
Mongoose: tests.find({}, { fields: {} })
[
{
"_id": "5b1b89348f3c9e1cdb500699",
"slno": 1,
"name": "Item 3",
"__v": 0
},
{
"_id": "5b1b89348f3c9e1cdb50069a",
"slno": 2,
"name": "Item 1",
"__v": 0
},
{
"_id": "5b1b89348f3c9e1cdb50069b",
"slno": 3,
"name": "Item 2",
"__v": 0
}
]
That's using syntax which is compatible with NodeJS v6.x
A small change in Neil Lunn's answer did the job.
const Model = require('../models/model');
router.post('/update', (req,res) => {
var tempArray=[];
req.body.updates.map(({slno,name}) => {
tempArray.push({
updateOne: {
filter: {slno},
update: {$set: {name}}
}
});
});
Model.bulkWrite(tempArray).then((result) => {
//Send resposne
}).catch((err) => {
// Handle error
});
Thanks to Neil Lunn.
I want to do all the find the data from the collection and then want to update some field as well as depending on want to empty the array.
const addCityFilter = (req, res) => {
if (req.body.aCities === "") {
res.status(409).jsonp({ message: adminMessages.err_fill_val_properly });
return false;
} else {
var Cities = req.body.aCities.split(","); // It will make array of Cities
const filterType = { "geoGraphicalFilter.filterType": "cities", "geoGraphicalFilter.countries": [], "geoGraphicalFilter.aCoordinates": [] };
/** While using $addToset it ensure that to not add Duplicate Value
* $each will add all values in array
*/
huntingModel
.update(
{
_id: req.body.id,
},
{
$addToSet: {
"geoGraphicalFilter.cities": { $each: Cities }
}
},
{$set:{filterType}},
).then(function(data) {
res.status(200).jsonp({
message: adminMessages.succ_cityFilter_added
});
});
}
};
Collection
geoGraphicalFilter: {
filterType: {
type:String,
enum: ["countries", "cities", "polygons"],
default: "countries"
},
countries: { type: Array },
cities: { type: Array },
aCoordinates: [
{
polygons: { type: Array }
}
]
}
But as result, the only city array is getting an update. No changes in filterType.
You appear to be passing the $set of filterType as the options argument, not the update argument.
huntingModel
.update(
{
_id: req.body.id,
},
{
$addToSet: {
"geoGraphicalFilter.cities": { $each: Cities }
},
$set: {
filterType
}
}
).then(function(data) {
res.status(200).jsonp({
message: adminMessages.succ_cityFilter_added
});
});