Is there a simpler way to perform auto increment for the GET api with the use of hook? Trying to implement something like view count, where it auto increment the count after a GET.
const portfoliosHooks = {
after: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
}
This should do the job:
const portfoliosHooks = {
after: {
all: [],
find: [],
get: [
hook => hook.service.patch(hook.id, { views: hook.result.views + 1 })
],
create: [],
update: [],
patch: [],
remove: []
},
}
hook.service is the service the hook is running on. This assumes you have a POJO result. If not, you'd have to flatten it first (toObject() or toJSON() depending on your db).
Related
I am using PostgreSQL as database and Sequelize JS to Query my database. One of the APIs has below code
var video_attributes = [[sequelize.cast(sequelize.fn("like_count", Sequelize.col("video_id")), 'integer'), "total_likes"]];
if (request.user) {
video_attributes.push([sequelize.fn("has_liked", request.user.id, Sequelize.col("user_bookmark.video_id")), "is_liked"]);
video_attributes.push([sequelize.fn("has_bookmarked", request.user.id, Sequelize.col("user_bookmark.video_id")), "is_bookmarked"]);
}
mod_idx.user_bookmark.findAll({
where: {"user_id": request.user.id},
include : [{
model:mod_idx.video, as: "video",
attributes: {include: video_attributes}
}],
attributes: {exclude: ["user_id", "video_id", "id"]},
order: [[
"id",
"desc"
]],
}).then(video_list => {
let r = { "data": video_list, "success": true }
response.status(200).json(r)
}).catch(function (err) {
console.log(err)
});
It returns below response:
{
"data": [
{
"video": {
"id": 189,
"total_likes": 0,
"is_liked": false,
"is_bookmarked": true
}
},
{
"video": {
"id": 261,
"total_likes": 0,
"is_liked": false,
"is_bookmarked": true
}
}
],
"success": true
}
Expected result:
{
"data": [
{
"id": 189,
"total_likes": 0,
"is_liked": false,
"is_bookmarked": true
},
{
"id": 261,
"total_likes": 0,
"is_liked": false,
"is_bookmarked": true
}
],
"success": true
}
I tried by making "raw" as true but it returns column names as 'video.column_name' (https://stackoverflow.com/a/53612698/1030951).
I don't want to use map function as it may slowdown while processing large number of records.
It is not pretty but you can do this by adding all attributes at top level.
mod_idx.user_bookmark.findAll({
where: {"user_id": request.user.id},
include : [{
model:mod_idx.video, as: "video",
attributes: [] // Do not include anything in nested object
}],
attributes: [
[sequelize.cast(sequelize.fn("like_count", Sequelize.col("video.video_id")), 'integer'), "total_likes"]
// Add more here
],
order: [[
"id",
"desc"
]],
})
================================================================
Update
You can do something like this. One thing to consider here is that you must avoid the column name conflicts across multiple tables (as you are putting all attributes at top level), so add exclude or rename to make sure you handle the conflicts.
I demonstrate here for a scenario that I want to include all video attributes except certain columns to avoid conflicts.
const videoExcludeList = ['createdAt', 'updatedAt'];
// Get all video attributes minus exclude list.
const videoAttrs = Object.keys(mod_idx.video.rawAttributes)
.filter((v) => !videoExcludeList.includes(v))
.map((v) => Sequelize.col(`video.${v}`));
// Custom attributes that uses fn/rename/etc
const customAttrs = [
[sequelize.cast(sequelize.fn("like_count", Sequelize.col("video.video_id")), 'integer'), "total_likes"],
// Add more here.
]
You can also combine with top-level exclude to exclude columns from user_bookmark.
mod_idx.user_bookmark.findAll({
where: {"user_id": request.user.id},
include : [{
model:mod_idx.video, as: "video",
attributes: [] // Do not include anything in nested object
}],
attributes: {
exclude: ['id'], // exclude these columns from user_bookmark
include: [...videoAttrs, ...customAttrs],
},
order: [[
"id",
"desc"
]],
raw: true // You need this.
})
I have a graph with some vertices that have edges towards each other (parent and child). Based off this question
This is what my code looks like
const showAllRelationships = async (target)=> {
var result = await g.V(target).bothE().otherV().path().by(__.valueMap(true));
console.log(result);
return result;
};
And this is the result I get.
GraphTraversal { graph: Graph {}, traversalStrategies: TraversalStrategies { strategies: [ [RemoteStrategy]
] }, bytecode: Bytecode { sourceInstructions: [], stepInstructions: [ [Array], [Array], [Array], [Array], [Array] ] }, traversers: null, sideEffects: null, _traversalStrategiesPromise: null, _traversersIteratorIndex: 0 }
What is wrong with this code that it isn't returning the edges?
You'll need to add a Terminal Step to the end of your query in order for the query to be sent to the server: https://tinkerpop.apache.org/docs/3.4.9/reference/#terminal-steps
in this case i want to find the total prepare time for each order according the orderId, but when i write like this it only shows 1 result which is the first one,
let prepareOrder = await OrderItem.findAll({
where: {
options: null,
},
attributes: ["orderId"],
include: [
{
model: Item,
attributes: [
[
sequelize.fn("sum", sequelize.col("prepareTime")),
"totalPrepareTime",
],
],
},
],
});
You need to rum SUM() on the outer query. When you run it on the inner query it is returning a single row and then doing a JOIN which is why you only get one row.
const prepareOrder = await OrderItem.findAll({
attributes: [
"orderId",
// get the summary at the OrderItem for each related Item
[
sequelize.fn("sum", sequelize.col("item.prepareTime")),
"totalPrepareTime",
],
],
// include Item but no attributes for performance
include: {
model: Item,
attributes: [],
},
where: {
options: null,
},
});
How can I create a hook in FeathersJS that returns the JWT token with the user object in the JSON response after the user is created in the users service?
I've tried in users.hooks.js:
after: {
all: [
// Make sure the password field is never sent to the client
// Always must be the last hook
protect('password')
],
find: [],
get: [],
create: [
function(hook) {
const data = {
email: hook.data.email,
password: hook.data.password,
strategy: 'local'
};
return hook.app.service('authentication').create(data).then(page => {
page.user = hook.result;
hook.result = page;
return Promise.resolve(hook);
});
}
],
update: [],
patch: [],
remove: []
},
But the JSON response stays the same. Is it possible to edit the FeathersJS authentication modules?
I've had a similar issue when trying to manipulate context.result (hook.result in your case. You should name it context though).
In my understanding, the hook.result is a reference to the actual object that gets returned from the service. So, you can manipulate that object as you please.
However in your case, you didn't change the actual result object, you took another object - page, manipulated it, then changed hook.result to reference that new object page. The actual result that hasn't changed.
hook.result = page;
The line above is your mistake. What you should do is:
hook.result.accessToken = page.accessToken
That way you manipulate the actual response object.
I would write your the function like this:
async function (context) {
const data = {
email: context.data.email,
password: context.data.password,
strategy: 'local'
};
const auth = await context.app.service('authentication').create(data);
context.result.accessToken = auth.accessToken;
return context;
}
The response JSON would then have the accessToken property on it.
To create a user and add a token there are a few things to consider:
hook.data.password will be the hashed password. The best way to maintain the original login information is to add a hook before hashPassword that adds a params.login property:
const setLogin = options => {
return context => {
const { data } = context;
context.params.login = {
strategy: 'local',
email: data.email,
password: data.password
};
return context;
};
};
Then you can use params.login to create a login. The authentication service also requires the params from the original request:
const addAccessToken = () => {
return async context => {
const { login } = context.params;
const jwt = await context.app.service('authentication')
.create(login, context.params);
context.result.accessToken = jwt.accessToken;
return context;
};
};
Then your users.hooks.js file looks similar to this:
module.exports = {
before: {
all: [],
find: [ authenticate('jwt') ],
get: [ authenticate('jwt') ],
create: [ setLogin(), hashPassword(), gravatar() ],
update: [ hashPassword(), authenticate('jwt') ],
patch: [ hashPassword(), authenticate('jwt') ],
remove: [ authenticate('jwt') ]
},
after: {
all: [
// Make sure the password field is never sent to the client
// Always must be the last hook
protect('password')
],
find: [],
get: [],
create: [ addAccessToken() ],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
I am trying to write to write an update to a Mongo document using the Mongoose findOneAndUpdate function. Essentially, I have a document that has an array of another Schema in it, and when I attempt to append more of those schema type, I get the following error:
[Error: Invalid atomic update value for $__. Expected an object, received object]
I'm having a hard time figuring out what this error even means, much less what its source is.
The data I'm attempting to update is as follows:
{ section_id: 51e427ac550dabbb0900000d,
version_id: 7,
last_editor_id: 51ca0c4b5b0669307000000e,
changelog: 'Added modules via merge function.',
committed: true,
_id: 51e45c559b85903d0f00000a,
__v: 0,
modules:
[ { orderId: 0,
type: 'test',
tags: [],
data: [],
images: [],
content: ["Some Content Here"] },
{ orderId: 1,
type: 'test',
tags: [],
data: [],
images: [],
content: ["Some Content Here"] },
{ orderId: 2,
type: 'test',
tags: [],
data: [],
images: [],
content: ["Some Content Here"] },
{ orderId: 3,
type: 'test',
tags: [],
data: [],
images: [],
content: ["Some Content Here"] },
{ orderId: 4,
type: 'test',
tags: [],
data: [],
images: [],
content: ["Some Content Here"] },
{ orderId: 5,
type: 'test',
tags: [],
data: [],
images: [],
content: ["Some Content Here"] } ] }
The only difference is that when I retrieve it, there are three fewer modules, and I append some new ones to the array.
Would love to hear any thoughts, at least as to what the error means!
This is probably because the updated object is still a Mongoose object.
Try to convert it to a JS object before the findOneAndUpdate
object = object.toString()
And delete any potential ID attribute
delete object._id
Or simply
object = object.toObject();
I had the same problem and it turned out I was using $push incorrectly. I was doing
{$push:thing_to_push}
But it needed to be
{$push:{list_to_push_into:thing_to_push}}
#Magrelo and #plus led me to an answer that worked. Something like:
MyModel.findOneAndUpdate({ section_id: '51e427ac550dabbb0900000d' }, mongooseObject.toObject(), { upsert: true }, function(err, results) {
//...
});
Try passing update parameter value as string instead of mongoose model object. I was getting same error when I use to pass model object. Below is the code difference.
Code that was having issue:
updateUser: function(req, res) {
**var updatedUserModel = new Users(req.body);**
Users.findOneAndUpdate({tpx_id:req.params.id}, {$set:**updatedUserModel**}, function(err, User){
...
}
}
Working code:
updateUser: function(req, res) {
Users.findOneAndUpdate({tpx_id:req.params.id}, {$set:**req.body**}, function(err, User) {
...
}
I had the same issue. I ended up by using finOne() method
create a new one if no found, update the existing one if found.
I know there are two operations. but I just haven't find any way to do it in one step.
Another thing to check is if you are sending passing an array of changes to $set. The error message that I received when calling something like this:
db.products.update( { sku: "abc123" },
{ $set: [
{quantity: 500},
{instock: true}
]
}
)
Gave me the [Error: Invalid atomic update value for $set. Expected an object, received object]
Changing it to an object worked.
db.products.update( { sku: "abc123" },
{ $set: {
quantity: 500,
instock: true
}
}
)