want to increment field value in mongodb using sails in node - node.js

I want to increment the field value by 1 in update query using sails(node)
Users.update({ code: referral }, { $inc: [{ referredCount: 1 }] }).then(function (update) {})
I have tried this.
Users.update({ code: referral }, { $inc: [{ referredCount: 1 }] }).then(function (update) {})
"err": {
"error": "E_UNKNOWN",
"status": 500,
"summary": "Encountered an unexpected error",
"raw": {
"name": "MongoError",
"message": "The dollar ($) prefixed field '$inc' in '$inc' is not valid for storage.",
"driver": true,
"index": 0,
"code": 52,
"errmsg": "The dollar ($) prefixed field '$inc' in '$inc' is not valid for storage."
}
}

This is the only correct way to do it.
Users.update({ code: referral }, { $inc: { referredCount: 1 } })
If its not incrementing, check whether referredCount exists in the document or not.
If it exists then check whether its not nested in another object.
Lastly also check whether, there is an actual document of the applied filter by query below.
Users.find({ code: referral })

Related

I m having a problem with the result of my Join table query

This is the code of my queryBuilder
In database I have two tables:
enquiry_table (contains statusId(#ManyToOne) which is a foreign key for enquiry_status_table)
enquiry_status_table
async findByLocation(qParams) {
return await getRepository(Enquiry).createQueryBuilder('enq')
.where({ location: qParams.location })
.select(['enq.id', 'enq.location', 'enqStatus.name'])
.leftJoin('enq.status', 'enqStatus')
.getMany()
}
With the above code I get following Results
{
"id": 5,
"location": "Karnataka",
"status": { <-- This is what I m getting currently with the above code
"name": "CANCELLED"
}
},
{
"id": 5,
"location": "Karnataka",
"status": "CANCELLED" <-- this is what I m expecting
},
you should add transient property like 'statusName' on enquiry_table entity.
I guess you already have property named 'status' for enquiry_status_table, so I recommended name 'statusName'.
#Column({ select: false, update: false, insert: false })
statusName: string;
and use addSelect on queryBuilder
async findByLocation(qParams) {
return await getRepository(Enquiry).createQueryBuilder('enq')
.where({ location: qParams.location })
.select(['enq.id', 'enq.location'])
.addSelect('enqStatus.name', 'statusName')
.leftJoin('enq.status', 'enqStatus')
.getMany()
}
and this is expected result
{
"id": 5,
"location": "Karnataka",
"statusName": "foo"
},
I got one solution:
getRepository(Enquiry).createQueryBuilder('enq')
.select(['enq.id AS id', 'enq.location AS location', 'enqStatus.name AS status'])
.leftJoin('enq.status', 'enqStatus')
.where(enquiryReq)
.getRawMany() <-- I returned raw data and put alias to each field :)

Mongoose query to check array contains element or not in mongoose query

i have records like this..
{
"photo": "default.jpg",
"roles": [
{
"_id": "5f44d77be830263684eb7914",
"name": "Test",
"__v": 0
}
],
"_id": "5f44d2f4ff04993b40684bf9",
"name": "Test User",
"email": "test#gmail.com",
"id": "5f44d2f4ff04993b40684bf9"
}
i want to weather the role user trying to delete is in use or not. for this i have write a query but it always return an empty array.
const check = await User.find({roles: {$elemMatch: {_id: req.params.id}}});
these are the queries i checked in compass but it always an array
{"roles._id":"5f44d77be830263684eb7914"}
{roles: $elemMatch: {_id: "5f44d77be830263684eb7914"}}
my User document hass roles field something like this.
roles:[
{
type:mongoose.Schema.Types.ObjectId,
ref:"Roles",
validate: {
validator: async function(v) {
return await Roles.findById(v, (err, rec) => rec !== null)
},
message: 'Invalid Object ID'
},
required:true
}
],
IS there any way to enforce integrity constraint like if user tries to delete records which have dependencies on other records then we should not allow user to delete this record.

Best way to wrap mongoose validation error

We know that mongoose provides us an easy way to do validation. But suppose you are using express+mongoose to building a microservice; and some clients (could be web-app, mobile app etc.) needs to consume it.
Usually, I prefer to response JSON back with simple error code and message. In most cases, the clients who can create their own messages depending on which language they are showing to users.
By default, if we catch the error from mongoose, we can get JSON response such as:
JSON Response
{
"errors": {
"price": {
"message": "Path `price` (-1) is less than minimum allowed value (0).",
"name": "ValidatorError",
"properties": {
"min": 0,
"type": "min",
"message": "Path `{PATH}` ({VALUE}) is less than minimum allowed value (0).",
"path": "price",
"value": -1
},
"kind": "min",
"path": "price",
"value": -1,
"$isValidatorError": true
},
"code": {
"message": "Product with given code already exists",
"name": "ValidatorError",
"properties": {
"type": "user defined",
"message": "Product with given code already exists",
"path": "code",
"value": "p-1000"
},
"kind": "user defined",
"path": "code",
"value": "p-1000",
"$isValidatorError": true
}
},
"_message": "Product validation failed",
"message": "Product validation failed: price: Path `price` (-1) is less than minimum allowed value (0)., code: Product with given code already exists",
"name": "ValidationError"
}
Restful Api Controller
exports.createOne = async(function* list(req, res) {
try {
const product = new Product(req.body)
const newProduct = yield product.save()
res.json(newProduct)
} catch (err) {
res.status(400).json(err)
}
})
Model Product.js
const mongoose = require('mongoose')
const Schama = mongoose.Schema
const minlength = [5, 'The value of `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).'];
const ProductSchema = new Schama({
code: { type: String, required: true, minlength, index: true, unique: true, trim: true, lowercase: true },
name: { type: String, required: true, trim: true },
price: { type: Number, required: true, min: 0, max: 100000 },
categories: [String],
})
ProductSchema.path('code').validate(function uniqueEmail(code, fn) {
const Product = mongoose.model('Product')
// Check only when it is a new Product or when code field is modified
if (this.isNew || this.isModified('code')) {
Product.find({ code }).exec((err, products) => {
fn(!err && products.length === 0)
})
} else fn(true)
}, 'Product with given code already exists')
ProductSchema.statics = {
/**
* List products
*
* #param {Object} options
* #api private
*/
pageList: function pageList(conditions, index, size) {
const criteria = conditions || {}
const page = index || 0
const limit = size || 30
return this.find(criteria)
.populate('user', 'name username')
.sort({ createdAt: -1 })
.limit(limit)
.skip(limit * page)
.exec()
},
}
mongoose.model('Product', ProductSchema)
What I expect
I am trying to wrap the error message to make it simple to consumer.
It could be like:
{
"errors": [
{
"message": "Path `price` (-1) is less than minimum allowed value (0).",
"code": "100020"
},
{
"message": "Product with given code already exists",
"code": "100021"
}
],
"success": false
}
The code and the corresponding message will be maintained on api documents. The message is usualy useful for consumer to understand the code and consumer (such as web client) could create their own message such as French messages according to the code and show to end users.
How can I leverage mongoose's valiation to accomplish this? Maybe I could loop erros's properties and combine an code using ${path}-${kind}.
I know that in most case, Client side should do the validation before calling apis. But there must be some cases that errors have to be thrown by APIs.
Any idea for this?

MongoDB: How to query field array by _id

how to get the specific message by _id.
I have a database schema as shown below:
{
"_id": ObjectID("5846eedaf0b51ed02ed846e2"),
"name": "conversation name"
"messages": [
{
"_id": ObjectID("584820a96866b6283361a4b9"),
"content": "hello",
"status": 0,
"type": 0
},
{
"_id": ObjectID("584820d56866b6283361a4ba"),
"content": "voices/1481122005696.mp3",
"status": 0,
"type": 3
}
]
}
db.collection.find({}, {
'messages': {
$elemMatch: {
'_id': ObjectId("584820a96866b6283361a4b9")
}
}
})
This will match on all documents in your collection, and return the matching array subfield in the projection. See the link posted in Yogesh's comment.
Since you tagged mongoose, I put it like this, but the query syntax is valid because is part of mongoDB query syntax:
Conversation.findOne({
"messages._id": "584820a96866b6283361a4b9"
}, function(err, document){
if (err) {
console.log(err)
return
}
var message = document.messages.id("584820a96866b6283361a4b9")
})
The finOne() method will return the full parent document with all the messages (subdocuments) and other properties. You then need to filter out the subdocument. Mongoose provides a way to do this easily with: document.messages.id("584820a96866b6283361a4b9") where document would be the data object passed to the find callback
See:
Match a Field Without Specifying Array Index on
https://docs.mongodb.com/v3.2/tutorial/query-documents/#query-on-arrays

Updating a nested document mongo. Concurrency issues

I am struggling with a seemingly basic issue in Mongoose.
I have a method that looks like so:
changeStateOnIncident: function(req, res, next) {
Incident.findOneQ({ _id: req.params.id })
.then(function(incident) {
if (!incident) { return next(new restify.ResourceNotFoundError("Incident with id " + req.params.id + " not found.")) }
return incident.addStateChange(req.body)
.then(function(savedState) {
return res.json(201, savedState);
})
.then(function() {
var alertOrders = _.map(incident.alertees, function(alertee) {
return alertee.createAndSendAlert(req.body)
})
return Q.allSettled(alertOrders)
.then(function(results) {
return incident.save();
})
})
})
.fail(function(err) {
return next(new restify.InvalidContentError(JSON.stringify(err)))
})
},
As for createAndSendAlert and addStateChange, their definitions are here
What I am trying to do here is as follows:
Find the Incident matching the ID passed in.
Add the state event to the array of state changes.
Return that newly added change event back to the user.
For every alertee on the Incident, loop through and add an alert to that alertees list of alerts, the result of an SMS request.
Save that incident.
Unfortunately, the way I'm doing it now is failing for concurrent writes. Suppose I send two
{
_id: "53cc87d0feeb4302007eadbe",
alertees: [
{
_id: "eJ65nfJh_g",
alerts: [
{
"_id": "53cc87d1feeb4302007eadc1",
"created_at": "2014-07-21T03:24:01.304Z",
"response": "Sent",
"state": "First state change",
"updated_at": "2014-07-21T03:24:06.802Z"
},
{
"_id": "53cc87d2feeb4302007eadc5",
"created_at": "2014-07-21T03:24:02.348Z",
"response": "Sent",
"state": "Emergency",
"updated_at": "2014-07-21T03:24:06.802Z"
},
{
"_id": "53cc87d6feeb4302007eadcc",
"response": "Sent"
"state": "Ended",
}
]
}
],
states: [
{
_id: "53cc87d1feeb4302007eadbf",
state: "First state change"
},
{
_id: "53cc87d1feeb4302007eadc2",
state: "Second state change"
},
{
_id: "53cc87d2feeb4302007eadc4",
state: "Third state change"
},
{
_id: "53cc87d6feeb4302007eadcb",
state: "Fourth state change"
}
]
}
I think what is happening is IF I send two states A and B. And A's SMS response comes back before B's it will possibly invalidate the document B is trying to modify. I am losing changes to the sub document if state changes are sent too close to each other.
Any idea? How could I rewrite this? Using update methods perhaps?

Resources