ts mongoose query specific string attribute as string array (not object array) - node.js

I have this mongoose schema, and I want to query all IP attributes as a string array.
like [IP,IP,IP,IP] this, not [ { ip : ip} ]
const proxyIpSchema = new Schema<IProxyIp>({
ip: {
type: String,
required: true,
//ignore duplicate ip
unique: true,
},
port: {
type: Number,
required: false,
default: null
},
reason: {
type: String,
required: true,
default: 'Unknown Detection.'
}
},
{
timestamps: true,
}
);
I cant use a map function because it will eat the backend processing power. like this
I want all ips as a string array
//get all ips from mongo db and push to redis
await ProxyIp.find({}, { ip: 1 }).then(async (docs) => {
docs.map(async (doc) => {
await this.RedisClient?.sAdd(redisTable, doc.ip);
});
}).catch((err) => {
});

Here's one way you can put all the "ip"s into a single array (within an object).
db.ProxyIp.aggregate([
{
"$group": {
"_id": null,
"ips": {
"$push": "$ip"
}
}
},
{
"$unset": "_id"
}
])
Example output:
[
{
"ips": [
"102.118.108.76",
"34.234.240.83",
"123.76.73.33",
"134.81.197.85",
"193.122.45.195",
"54.25.18.14",
"185.68.124.193",
"3.105.130.68",
"52.72.204.78",
"117.212.118.167",
"199.155.140.226",
"64.194.68.59",
"57.4.147.57",
"190.116.4.243",
"179.111.74.98",
...
]
}
[
Try it on mongoplayground.net.

Related

How to set "seen:true" in array of messages in mongoose

I am newbie in mongodb world, i was stuck in on Mongoose query.
basically I was developing a chat application for my website. the chat schema of my website shown below
const LiveChat = mongoose.Schema({
members: [String],
messages: [{
sender: String,
reciever: String,
text: String,
seen: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
}
}],
}, { timestamps: true });
for exapmle collection will looks like this
[
{
"_id":"627749f8dc5927d660f76172",
"members":[
"626a250d11ed1b096883aed1",
"626a234611ed1b096883ae13"
],
"messages":[
{
"sender":"626a250d11ed1b096883aed1",
"reciever":"626a234611ed1b096883ae13",
"text":"hello there!.",
"seen":false,
"_id":"6278b0c38031894a9ceefeae",
"date":"2022-05-09T06:12:19.857Z"
},
{
"sender":"626a250d11ed1b096883aed1",
"reciever":"626a234611ed1b096883ae13",
"text":"how are you?.",
"seen":false,
"_id":"6278b0e18031894a9ceefede",
"date":"2022-05-09T06:12:49.680Z"
},
{
"sender":"626a234611ed1b096883ae13",
"reciever":"626a250d11ed1b096883aed1",
"text":"hello Rupesh",
"seen":false,
"_id":"6278b1438031894a9ceeff98",
"date":"2022-05-09T06:14:27.388Z"
},
{
"sender":"626a234611ed1b096883ae13",
"reciever":"626a250d11ed1b096883aed1",
"text":"we are doing well",
"seen":false,
"_id":"6278b1588031894a9ceeffe0",
"date":"2022-05-09T06:14:48.203Z"
},
{
"sender":"626a250d11ed1b096883aed1",
"reciever":"626a234611ed1b096883ae13",
"text":"okay",
"seen":false,
"_id":"6278b1ed8031894a9cef0099",
"date":"2022-05-09T06:17:17.421Z"
}
],
"createdAt":"2022-05-08T04:41:28.416Z",
"updatedAt":"2022-05-09T06:17:17.420Z",
"__v":0
},
{
"_id":"62762021be68a5e2de8dc2d2",
members: ["626a250d11ed1b096883aed1", "6273bc879ff276ac89f9c4f8"]
"messages":[
{
"sender":"626a250d11ed1b096883aed1",
"reciever":"6273bc879ff276ac89f9c4f8",
"text":"hello there!",
"seen":false,
"_id":"6277ac0ba5fe501f98e1421e",
"date":"2022-05-08T11:39:55.263Z"
},
{
"sender":"626a250d11ed1b096883aed1",
"reciever":"6273bc879ff276ac89f9c4f8",
"text":"can you please tell me what is the date of start a project task",
"seen":false,
"_id":"6277ac30a5fe501f98e1424c",
"date":"2022-05-08T11:40:32.472Z"
},
],
"createdAt":"2022-05-07T07:30:41.447Z",
"updatedAt":"2022-05-08T12:24:31.400Z",
"__v":0
}
]
now i want to set seen:true for all messages into messages array whose sender == "626a250d11ed1b096883aed1" ( sender_id and conversation_id are given from req.body into a API).
for all messages in messages array:
seen:false means message is not seen by reciever
seen:true means message is seen by reciever
i was trying following way into my express API but its not wokring...
cosnt {conversation_id, sender_id} = req.body;
LiveChat.findByIdAndUpdate({ _id: conversation_id },
[{
$set: {
'messages.seen': { $cond: [{ $eq: ['messages.sender', sender_id] }, true, false] }
}
}]
,
{
new: true,
useFindAndModify: true,
}
please help me to write this query...
Here's one way you could do the update using "arrayFilters".
db.collection.update({
"_id": "627749f8dc5927d660f76172" // given _id
},
{
"$set": {
"messages.$[x].seen": true
}
},
{
"arrayFilters": [
{
"x.sender": "626a250d11ed1b096883aed1" // given sender
}
]
})
Try it on mongoplayground.net.

Get all data from subdocument via graphql

Model
const mongoose=require('mongoose');
const CustomerGeneralInformation = require('./CustomerGeneralInformation').schema;
const CustomerContact = require('./CustomerContact').schema;
const CustomerFinance=require('./CustomerFinancial').schema;
const CustomerPersonalData=require('./CustomerPersonalData').schema;
const CustomerIdentification=require('./CustomerIdentification').schema;
const Schema=mongoose.Schema;
const CustomerSchema=new Schema({
generalInformation: [CustomerGeneralInformation],
contactData: [CustomerContact],
financialData:[CustomerFinance],
personalData:[CustomerPersonalData],
identificationData:[CustomerIdentification]
});
module.exports=mongoose.model('Customer',CustomerSchema);
Graphql Schema
const RootQuery=new GraphQLObjectType({
name:'RootQueryType',
fields:{
customers:{
type: new GraphQLList(CustomerType),
resolve(parent,args){
return customer.find({});
}
}
}
});
GraphiQl
{
customers
{
id
generalInformation{
purposeOfBusiness
}
contactData{
phone
email
countryResidence
mailbox
houseNo
zip
city
}
financialData{
taxNo
countryTaxation
refBankIban
StringrefBankBic
refBankIban
}
}
}
Result
{
"data": {
"customers": [
{
"id": "5fa0f8ea4e028a2cf8d24c3f",
"generalInformation": {
"purposeOfBusiness": null
},
"contactData": {
"phone": null,
"email": null,
"countryResidence": null,
"mailbox": null,
"houseNo": null,
"zip": null,
"city": null
},
"financialData": {
"taxNo": null,
"countryTaxation": null,
"refBankIban": null,
"StringrefBankBic": null
}
}
]
}
}
SO I have created a customer model which have an id(autogenerated by mongodb) and 5 other fields of schema type.
I have used graphql for creating apis.
Everything is working fine, I have created mutations to add data, its all tested in mongo that data is being saved.
But if you check my graphql output I am unable to query subdocuments, it shows null. I want list of all subdocuments with data.
PlEASE HELP
I had tried this way and its working perfectly,
Firstly as you are passing the whole model in your new Customer model so instead of that I just have stored the ID as a reference.
const CustomerSchema = new mongoose.Schema(
{
generalInformation: {
type: mongoose.Schema.Types.ObjectId,
ref: "GeneralInfo"
},
contactData: { type: mongoose.Schema.Types.ObjectId, ref: "ContactData" },
financialData: {
type: mongoose.Schema.Types.ObjectId,
ref: "FinacialData"
},
personalData: { type: mongoose.Schema.Types.ObjectId, ref: "PersonalData" }
},
{
timestamps: true
}
);
After that, for CustomerType, I had done this
const CustomerType = new GraphQLObjectType({
name: "Customer",
fields: () => ({
id: { type: GraphQLID },
customerContact: {
type: CustomerContactType,
resolve(parent, args) {
return CustomerContact.findById(parent.contactData);
}
},
generalInformation: {
type: CustomerGeneralType,
resolve(parent, args) {
return CustomerGeneral.findById(parent.generalInformation);
}
},
financialData: {
type: CustomerFinanceType,
resolve(parent, args) {
return CustomerFinance.findById(parent.financialData);
}
},
personalData: {
type: CustomerPersonalType,
resolve(parent, args) {
return CustomerPersonal.findById(parent.personalData);
}
}
})
});
And them for RootQuery
customers: {
type: new GraphQLList(CustomerType),
resolve(parent, args) {
return Customar.find({});
}
}
and here is my Query:
{
customers{
id
generalInformation{
purposeOfBusiness
}
customerContact{
phone
email
countryResidence
}
financialData{
taxNo
}
}
}
And Output
{
"data": {
"customers": [
{
"id": "5fa3fe066f0fda0568e56456",
"generalInformation": {
"purposeOfBusiness": "Test Purpose"
},
"customerContact": {
"phone": "123456798",
"email": "test#demo.com",
"countryResidence": "Pak"
},
"financialData": {
"taxNo": "120033244"
}
}
]
}
}

Mongodb update multiple documents with different values

I have been trying to use updatemany with mongoose. I want to update the values in database using an array of objects.
[
{
"variantId": "5e1760fbdfaf28038242d676",
"quantity": 5
},
{
"variantId": "5e17e67b73a34d53160c7252",
"quantity": 13
}
]
I want to use variantId as filter.
Model schema is:
let variantSchema = new mongoose.Schema({
variantName: String,
stocks: {
type: Number,
min: 0
},
regularPrice: {
type: Number,
required: true
},
salePrice: {
type: Number,
required: true
}
})
I want to filter the models using variantId and then decrease the stocks.
As you need to update multiple documents with multiple criteria then .updateMany() wouldn't work - it will work only if you need to update multiple documents with same value, Try this below query which will help you to get it done in one DB call :
const Mongoose = require("mongoose");
let variantSchema = new mongoose.Schema({
variantName: String,
stocks: {
type: Number,
min: 0
},
regularPrice: {
type: Number,
required: true
},
salePrice: {
type: Number,
required: true
}
})
const Variant = mongoose.model('variant', variantSchema, 'variant');
let input = [
{
"variantId": "5e1760fbdfaf28038242d676",
"quantity": 5
},
{
"variantId": "5e17e67b73a34d53160c7252",
"quantity": 13
}
]
let bulkArr = [];
for (const i of input) {
bulkArr.push({
updateOne: {
"filter": { "_id": Mongoose.Types.ObjectId(i.variantId) },
"update": { $inc: { "stocks": - i.quantity } }
}
})
}
Variant.bulkWrite(bulkArr)
Ref : MongoDB-bulkWrite
I don't think this can be done with a single Model.updateMany query. You will need to loop the array and use Model.update instead.
for (const { variantId, quantity } of objects) {
Model.update({ _id: variantId }, { $inc: { stocks: -quantity } });
}
To run this in a transaction (https://mongoosejs.com/docs/transactions.html), the code should look something like this (however I have not tried or tested this):
mongoose.startSession().then(async session => {
session.startTransaction();
for (const { variantId, quantity } of objects) {
await Model.update({ _id: variantId }, { $inc: { stocks: -quantity } }, { session });
}
await session.commitTransaction();
});

How to grab field value during a MongooseModel.bulkWrite operation?

Context:
I am trying to upsert in bulk an array of data, with an additional computed field: 'status'.
Status should be either :
- 'New' for newly inserted docs;
- 'Removed' for docs present in DB, but inexistent in incoming dataset;
- a percentage explaining the evolution for the field price, comparing the value in DB to the one in incoming dataset.
Implementations:
data.model.ts
import { Document, model, Model, models, Schema } from 'mongoose';
import { IPertinentData } from './site.model';
const dataSchema: Schema = new Schema({
sourceId: { type: String, required: true },
name: { type: String, required: true },
price: { type: Number, required: true },
reference: { type: String, required: true },
lastModified: { type: Date, required: true },
status: { type: Schema.Types.Mixed, required: true }
});
export interface IData extends IPertinentData, Document {}
export const Data: Model<IData> = models.Data || model<IData>('Data', dataSchema);
data.service.ts
import { Data, IPertinentData } from '../models';
export class DataService {
static async test() {
// await Data.deleteMany({});
const data = [
{
sourceId: 'Y',
reference: `y0`,
name: 'y0',
price: 30
},
{
sourceId: 'Y',
reference: 'y1',
name: 'y1',
price: 30
}
];
return Data.bulkWrite(
data.map(function(d) {
let status = '';
// #ts-ignore
console.log('price', this);
// #ts-ignore
if (!this.price) status = 'New';
// #ts-ignore
else if (this.price !== d.price) {
// #ts-ignore
status = (d.price - this.price) / this.price;
}
return {
updateOne: {
filter: { sourceId: d.sourceId, reference: d.reference },
update: {
$set: {
// Set percentage value when current price is greater/lower than new price
// Set status to nothing when new and current prices match
status,
name: d.name,
price: d.price
},
$currentDate: {
lastModified: true
}
},
upsert: true
}
};
}
)
);
}
}
... then in my backend controller, i just call it with some route :
try {
const results = await DataService.test();
return new HttpResponseOK(results);
} catch (error) {
return new HttpResponseInternalServerError(error);
}
Problem:
I've tried lot of implementation syntaxes, but all failed either because of type casting, and unsupported syntax like the $ symbol, and restrictions due to the aggregation...
I feel like the above solution might be closest to a working scenario but i'm missing a way to grab the value of the price field BEFORE the actual computation of status and the replacement with updated value.
Here the value of this is undefined while it is supposed to point to current document.
Questions:
Am i using correct Mongoose way for a bulk update ?
if yes, how to get the field value ?
Environment:
NodeJS 13.x
Mongoose 5.8.1
MongoDB 4.2.1
EUREKA !
Finally found a working syntax, pfeeeew...
...
return Data.bulkWrite(
data.map(d => ({
updateOne: {
filter: { sourceId: d.sourceId, reference: d.reference },
update: [
{
$set: {
lastModified: Date.now(),
name: d.name,
status: {
$switch: {
branches: [
// Set status to 'New' for newly inserted docs
{
case: { $eq: [{ $type: '$price' }, 'missing'] },
then: 'New'
},
// Set percentage value when current price is greater/lower than new price
{
case: { $ne: ['$price', d.price] },
then: {
$divide: [{ $subtract: [d.price, '$price'] }, '$price']
}
}
],
// Set status to nothing when new and current prices match
default: ''
}
}
}
},
{
$set: { price: d.price }
}
],
upsert: true
}
}))
);
...
Explanations:
Several problems were blocking me :
the '$field_value_to_check' instead of this.field with undefined 'this' ...
the syntax with $ symbol seems to work only within an aggregation update, using update: [] even if there is only one single $set inside ...
the first condition used for the inserted docs in the upsert process needs to check for the existence of the field price. Only the syntax with BSON $type worked...
Hope it helps other devs in same scenario.

mongoDB find, update and pull in One Query

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
});
});

Resources