I am using Sequelize as ORM along with express.js Below is my minimalistic table structure.
Id status phone
1 0 09620701828
2 0 09620701828
Now when an api request is to make on an end-point then I have to update status for last id belonging to a particular phone that I am getting from request. Raw MySql query for which will be UPDATE table SET status=1 where phone='09620701828' ORDER BY id DESC LIMIT 1;
How I can achieve the same using Sequelize update method. Below is what I have tried
models.customerLeadFeedback.update(
{status: 1},
{
where: {phone : '09620701828'},
order: [['id', 'DESC']],
limit : 1
}).then(function () {
res.sendStatus(200);
})`
But order by clause is not working for above and updating status for id 1. Can anyone help me with this without using raw query in sequelize.
It would appear that Sequelize'sModel#update function doesn't support ordering - see the docs at http://sequelize.readthedocs.io/en/v3/api/model/#updatevalues-options-promisearrayaffectedcount-affectedrows
If you really want to target the row by ordering, you could fetch the row first, then update it:
models.customerLeadFeedback.findOne(
{status: 1},
{
where: {phone : '09620701828'},
order: [['id', 'DESC']]
}).then(function (record) {
return record.update({status: 1});
}).then(function (record) {
res.sendStatus(200);
});
It's less efficient, using two queries instead of one, but currently looks like your only option if you don't want to use raw queries.
Related
I have started working in loopback 4.I have created a default CRUD controller with basic CRUD operations which can get 'the table data', 'the table record for particular id' etc.. what I need now is I want to execute my own query for this table. How to write a query in controller and execute it?
/* below is the default function to get table data*/
#get('/customers', {
responses: {
'200': {
description: 'Array of customers model instances',
content: {
'application/json': {
schema: {type: 'array', items: {'x-ts-type': Customers}},
},
},
},
},
})
async find(
#param.query.object('filter', getFilterSchemaFor(Customers)) filter?: Filter,
: Promise<Customers[]> {
if (limit > 100) limit = 100; // your logic
return await this.CustomersRepository.find(filter);
}
the type of code I want is,
Execute(dataset, sqlstatement); //is that available?
Support for executing raw database queries was added recently by the pull request #2681, this functionality is provided by Repository method execute.
Please note that they query format is connector-specific. For example, if you are using MySQL database, then you can write the following query:
await this.CustomersRepository.execute(
// the query template, use "?" for variables to AVOID SQL INJECTIONS ATTACKS!
'SELECT TOP ? * FROM CUSTOMERS',
// the values to use for variables
[limit]);
I am creating a webapp for users to query data from mongodb. I have front end using react and backend with nodejs hooking up mongodb.
I have included all relevant matching conditions under aggregate function. However I would like to have the effect of commenting out certain key-value pair, (by this I mean nullifying the effect of the query field condition). For example,
router.get('/getmongo', function(req, res) {
MongoClient.connect(process.env.DB_CONN, function(err, db) {
if (err) throw err;
db.aggregate([
{
$match:{
'employmentStatus':/employed|unemployed/i,
'gender':{$in:["male","female"]},
'age':{$gte:20, $lte:30}
}
},
{ "$group": {
"_id": "$employmentStatus",
"count": { "$sum": 1 }
}}
]).limit(4).toArray(function(err, docs) {
if (err) throw err;
res.send(JSON.stringify(docs))
db.close();
})
})
Let say on front end, the user does not select the option of age, which means there is no specification from the user's end on the age criteria. Usually for this case, users would comment out or remove the whole line of age query on MongoDB GUI, leaving only employmentStatus and gender as the only two criteria to query. I was looking into mongoAPI but can't find any useful tool to replicate that effect. Any idea or suggestion?
(I am using Mongo v2.3.3 by the way)
Instead of adding conditions in the Query at MongoDB side, you should build your query conditionally based on user input.
Whenever user selects the columns, build the $match clause using those columns and values. In that way, it will be quite dynamic.
Example PseudoCode:
var queryObject = {};
if(age.selected)
queryObject.add(ageCondition)
if(employmentStatus.selected)
queryObject.add(employmentStatusCondition)
if(gender.selected)
queryObject.add(genderCondition)
.....
......
You can build some logic like this in Node.js.
I developed an API using Loopback framework, in that i have to insert or update to a table.
My table looks like below:
userid bbid saved id(PK)
1 5 1000 1
So when next time if(bbid = 5) it should update the above row, if bbid =5 is not there it should insert into the above table.
I tried the following:
app.models.modelname.upsert({
bbid: bb.id,
saved: Saved,
userid: 1
}, function(err, res) {});
EDIT for COMPOSITE ID:
app.models.modelname.upsert({
bbid: vvvv.bbid,
userid: 1,
saved: amountSaved
}, function(err, res) {});
Also gone through Loopback doc it says
upsert - checks if the instance (record) exists, based on the designated
ID property, which must have a unique value; if the instance already exists,
the method updates that instance. Otherwise, it inserts a new instance.
But in my case it should check for bbid not id
But its inserting everytime. Please share your ideas. Thanks in advance
EDIT FOR UPSERT:
My process is as follows:
Fetch from table1 and loop that response and calculate the saved and upsert it into another table(table2 (this is where upsert should happen)). Also the fetching from table1 will happen frequently so suppose consider if is happens 2nd time it should update the already present bbid..
You can use the findOrCreate method as follows:
app.models.modelname({where: {bbid:bb.id} }, {bbid:bb.id, saved: Saved, userid:1}, function(err, instance) {
if (err){
cb(null, err);
}
cb(null, instance);
});
There are two methods in loopback one is simple upsert and second is upsertWithWhere.To insert or update based on where condition you must use upsertWithWhere method upsert only inserts the data.
you are using
app.models.modelname.upsert({bbid:bb.id, saved: Saved, userid:1}
,function(err, res){});
instead use
app.models.modelname.upsertWithWhere({bbid:bb.id, saved: Saved, userid:1}
,function(err, res){});
this will solve your problem.
note: this only updates single instance.If multiple instances are returned in where condition result it will throw an error.
I am working through a MEAN stack tutorial. It contains the following code as a route in index.js. The name of my Mongo collection is brandcollection.
/* GET Brand Complaints page. */
router.get('/brands', function(req, res) {
var db = req.db;
var collection = db.get('brandcollection');
collection.find({},{},function(e,docs){
res.render('brands', {
"brands" : docs
});
});
});
I would like to modify this code but I don't fully understand how the .find method is being invoked. Specifically, I have the following questions:
What objects are being passed to function(e, docs) as its arguments?
Is function(e, docs) part of the MongoDB syntax? I have looked at the docs on Mongo CRUD operations and couldn't find a reference to it. And it seems like the standard syntax for a Mongo .find operation is collection.find({},{}).someCursorLimit(). I have not seen a reference to a third parameter in the .find operation, so why is one allowed here?
If function(e, docs) is not a MongoDB operation, is it part of the Monk API?
It is clear from the tutorial that this block of code returns all of the documents in the collection and places them in an object as an attribute called "brands." However, what role specifically does function(e, docs) play in that process?
Any clarification would be much appreciated!
The first parameter is the query.
The second parameter(which is optional) is the projection i.e if you want to restrict the contents of the matched documents
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 },function(e,docs){})
would mean to get only the item and qty fields in the matched documents
The third parameter is the callback function which is called after the query is complete. function(e, docs) is the mongodb driver for node.js syntax. The 1st parameter e is the error. docs is the array of matched documents. If an error occurs it is given in e. If the query is successful the matched documents are given in the 2nd parameter docs(the name can be anything you want).
The cursor has various methods which can be used to manipulate the matched documents before mongoDB returns them.
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 })
is a cursor you can do various operations on it.
collection.find( { qty: { $gt: 25 } }, { item: 1, qty: 1 }).skip(10).limit(5).toArray(function(e,docs){
...
})
meaning you will skip the first 10 matched documents and then return a maximum of 5 documents.
All this stuff is given in the docs. I think it's better to use mongoose instead of the native driver because of the features and the popularity.
I have two models in my app: Item and Comment. An Item can have many Comments, and a Comment instance contains a reference to an Item instance with key 'comment', to keep track of the relationship.
Now I have to send a JSON list of all Items with their Comment count when user requests on a particular URL.
function(req, res){
return Item.find()
.exec(function(err, items) {
return res.send(items);
});
};
I am not sure how can I "populate" comment count to the items. This seems to be a common problem and I tend to think there should be some nicer way of doing this job than brute force.
So please share your thoughts. How would you "populate" the Comment count to the Items?
check the MongoDB documentation and look for the method findAndModify() -- with it you can atomically update a document, e.g. add a comment and increment the document counter at the same time.
findAndModify
The findAndModify command atomically modifies and returns a single document. By default, the returned document does not include the modifications made on the update. To return the document with the modifications made on the update, use the new option.
Example
Use the update option, with update operators $inc for the counter, and $addToSet for adding the actual comment to an embedded array of comments.
db.runCommand(
{
findAndModify: "item",
query: { name: "MyItem", state: "active", rating: { $gt: 10 } },
sort: { rating: 1 },
update: { $inc: { commentCount: 1 },
$addToSet: {comments: new_comment} }
}
)
See:
MongoDB: findAndModify
MongoDB: Update Operators
I did some research on this issue and came up with following results. First, MongoDB docs suggest:
In general, use embedded data models when:
you have “contains” relationships between entities.
you have one-to-many relationships where the “many” objects always appear with or are viewed in the context of their parent documents.
So in my situation, it makes much more sense if Comments are embedded into Items, instead of having independent existence.
Nevertheless, I was curious to know the solution without changing my data model. As mentioned in MongoDB docs:
Referencing provides more flexibility than embedding; however, to
resolve the references, client-side applications must issue follow-up
queries. In other words, using references requires more roundtrips to
the server.
As multiple roundtrips are kosher now, I came up with following solution:
var showList = function(req, res){
// first DB roundtrip: fetch all items
return Item.find()
.exec(function(err, items) {
// second DB roundtrip: fetch comment counts grouped by item ids
Comment.aggregate({
$group: {
_id: '$item',
count: {
$sum: 1
}
}
}, function(err, agg){
// iterate over comment count groups (yes, that little dash is underscore.js)
_.each(agg, function( itr ){
// for each aggregated group, search for corresponding item and put commentCount in it
var item = _.find(items, function( item ){
return item._id.toString() == itr._id.toString();
});
if ( item ) {
item.set('commentCount', itr.count);
}
});
// send items to the client in JSON format
return res.send(items);
})
});
};
Agree? Disagree? Please enlighten me with your comments!
If you have a better answer, please post here, I'll accept it if I find it worthy.