I need to call a function declared outside my pipeline to test some data in the query.
This is my code so far:
let requestMatcher = {
$expr: {
$function: {
body: function (modelType) {
testFunction()
return name === 'chiller'
},
args: ['$name'],
lang: 'js'
}
}
}
let pipeline = [{ $match: requestMatcher }, { $limit: 10 }]
const data = await Model.aggregate(pipeline)
const total = await Model.countDocuments(requestMatcher)
return res.status(200).send({
body: {
data,
total
}
})
testFunction() is just a console.log nothing fancy.
Aside from that testFunction(), everything else works in my pipeline.
When this code is run, I get "testFunction is not defined" which I know it's not defined inside mongo, but I would like to use some functions and variables to validate data while being queried
Am I doing something wrong here? Or $function isn't supposed to work like that?
Is there another way that I can achieve something similar with mongoose?
Related
I'm new to both Strapi and Mongoose, so I apologise if this is a stupid question.
Following the docs (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html) I'm trying to create a custom query in Strapi in which I want to return the whole collection called people sorted by name desc. But when I hit the endpoint I get a 500 error and checking the terminal the error message is CastError: Cast to ObjectId failed for value "alldesc" at path "_id" for model "people".
Here's my code:
services/people.js
module.exports = {
findByNameDesc() {
const result = strapi
.query("people")
.model.find()
.sort({ name: "descending" });
return result.map((entry) => entry.toObject());
},
};
controllers/people.js
module.exports = {
async alldesc(ctx) {
const entities = await strapi.services.people.findByNameDesc(ctx);
return entities.map((entity) =>
sanitizeEntity(entity, { model: strapi.models.people })
);
},
};
config/routes.json
{
"routes": [
...
{
"method": "GET",
"path": "/people/alldesc",
"handler": "people.alldesc",
"config": {
"policies": []
}
}
]
}
What am I doing wrong?
UPDATE: even when removing .sort({ name: "descending" }); from the query, the error is still there, so I'm thinking that maybe there's something wrong in the way I use the service in the controller?
The problem was in routes.json. Basically seems like Strapi doesn't like the slash / so instead of /people/alldesc I tried /people-alldesc and it worked.
Also in the service there's no need for return result.map((entry) => entry.toObject());, that causes anther error, simply doing return result works.
I'm working with Nuxt in SSR mode and want to build out a dynamic sitemap for multiple routes/ data sets.
The issue I'm facing now is that the async/ await function only allows 'data' as the variable. The same function with 'post' as the variable leads to a "map function does not exist"
This is what's in my Nuxt.config.js file
sitemap: {
hostname: "https://example.com",
routes: async () => {
let { data } = await axios.get('https://api.example.com/api/v1/locations');
data = data.data.map((loc) => `/locations/${loc.slug}`);
console.log(data);
let { posts } = await axios.get('https://cms.example.com/wp-json/wp/v2/posts');
posts = posts.map((post) => `/posts/${post.slug}`);
console.log(posts);
data.concat(posts);
return data
},
path: '/sitemap.xml'
}
The resulting output I'm looking for should be formatted like this:
[
'/locations/location-one',
'/locations/location-two',
'/locations/location-three',
'/posts/post-one',
'/posts/post-two',
'/posts/post-three',
]
The error I'm getting:
Cannot read property 'map' of undefined
and it's occuring on this line:
posts = posts.map((post) => `/posts/${post.slug}`)
so it appears to me that it's not accepting 'posts' as a valid variable for its own await function.
That call works fine when the first call is commented out and 'data' is used instead of 'posts'
your destructured response is wrong:
replace:
let { posts } = ...
by:
let { data: posts } = ...
Because Axios always returns a "data" attribute, so you just have to rename it as "posts".
Your array concatenation must be like that:
data.concat(posts);
(see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)
The push() method is only to push one item, but not an array of item.
(see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push)
I have a helper.js that loads before my tests:
before(async function() {
this.timeout(30000)
global.db = require(`${process.cwd()}/models`)()
...
Then in my test, I have:
describe.only('Phone Library', () => {
let updateCallSpy
beforeEach(() => {
sinon.stub(twilioClient.calls, 'create').resolves({})
updateCallSpy = sinon.stub(global.db.models.Call, 'update').resolves(true)
// sinon.stub(global.db.models.Conversation, 'update').resolves({})
})
The twilioClient.calls.create stubs properly. But the global.db.models.Call.update does not.
In my actual code, I'm using it like:
await global.db.models.Call.update({ status: newCallStatus }, { where: { id: foundConversation.Call.id } })
When I console.log(global.db.models.Call), it simply outputs Call. However, the .update function is there and does what it's supposed to do (a Sequelize model that updates).
I'm sure It's something terribly obvious, so any pointers would be greatly appreciated. Thanks.
The sequelize model methods are defined as prototype by the sequelize core.
The following should work
updateCallSpy = sinon.stub(global.db.models.Call.prototype, 'update').resolves(true)
You can also create a stub instance:
updateCallStubInstance = sinon.createStubInstance(global.db.models.Call)
newbie here.
I am trying to understand how I need to structure asynchronous calls within my controller to fit my specific use case:
Consider the following code snippet from an Angular Module in "service.js" within my project:
function getSearchObjects(projectName, title) {
var payload = JSON.stringify({
"title": title
});
var request = $http({
method: 'post',
url: URL + '/search/' + projectName,
data: payload
});
return request.then(handleSuccess, handleError);
};
function runQuery(projectName, fromDate, toDate, sort, direction, columns) {
var from = Date.parse(fromDate);
var to = Date.parse(toDate);
var payload = JSON.stringify({
"fromDate": from,
"toDate": to,
"sort": sort,
"direction": direction,
"columns": columns
});
console.log(payload);
var request = $http({
method: 'post',
url: URL + '/query/' + projectName,
data: payload
});
return request.then(handleSuccess, handleError);
}
function handleSuccess(response) {
return response.data;
};
function handleError(response) {
if (!angular.isObject( response.data ) || !response.data.error) {
return( $q.reject( "An unknown error occurred." ) );
}
return $q.reject( response.data.error );
};
});
Within my controller, I am trying to troubleshoot the following function:
$scope.submit = function() {
var objectProperties = exportsStorageService.getSearchObjects($scope.selected.project.name, $scope.selected.search)
.then(function(result) {
exportsStorageService.runQuery($scope.selected.project.name, $scope.selected.start_date, $scope.selected.end_date, objectProperties.sort, objectProperties.direction, objectProperties.columns)
},
function(error) {
console.log(error);
});
};
getSearchObjects matches a title ($scope.selected.search) selected in my UI and grabs the following more detailed object from an API call:
{ title: 'Duplication Example',
sort: '#_traac-timestamp',
direction: 'desc',
columns: [ '#_traac-remote_ip', 'c-platform-m-distinct-id_s', '_type' ] }
I am trying to grab the properties returned from getSearchObjects and pass them along with a few user selected values from my UI to runQuery, which then returns data from a database to the user, but when I check the values passed to runQuery using the above logic in my controller, I get the following values. All of the objectProperties values I am attempting to pass to runQuery are undefined:
project_name: "Example Project"
start_date: 1499770800000
end_date: 1499943600000
sort: undefined
direction: undefined
columns: undefined
I have been trying to debug this, but I am too new to using Angular and asynchronous calls to really understand what I am doing wrong. My best guess currently is that I am calling runQuery before the values retrieved from getSearchObjects are attached to objectProperties. Either that or I am incorrectly referencing the properties within the objectProperties variable.
Could someone help me troubleshoot this issue, and better understand what I am doing wrong?
Thank you in advance for your help!
When you do this:
var objectProperties = some async function...
You are assigning the promise of the async function to the variable, not the result of it.
The result is coming in the .then, like you declared:
.then(function(result) { ... }
So, instead of objectProperties.sort, objectProperties.direction, objectProperties.columns, try using result.sort, result.direction, result.columns :)
If you are new to Promises, take a look at this simple, but great tutorial.
EDIT
Based on your comment, you are receiving, inside the response.data, the following object:
{"objectMatch": {
"title": "doc-event",
"sort": "#_traac-timestamp",
"direction": "desc",
"columns": [
"m-doc-name_s",
"m-user_s",
"m-full-action-type_s",
"m-event-action-descriptor_s"
]}
}
So you have: response > data > objectMatch > properties you want.
The response.data you are extracting on your handleSuccess function:
function handleSuccess(response) {
return response.data;
};
So here, your result is response.data, containing the property objectMatch.
$scope.submit = function() {
var objectProperties = exportsStorageService.getSearchObjects($scope.selected.project.name, $scope.selected.search)
.then(function(result) {
...
},
...
If all of that is correct, you should be able to access the values you want using result.objectMatch.<sort, direction or columns>, like:
exportsStorageService.runQuery($scope.selected.project.name, $scope.selected.start_date, $scope.selected.end_date,
result.objectMatch.sort, result.objectMatch.direction, result.objectMatch.columns)
I have a sample code that goes like this:
Client Helper:
getUsername: function (userId) {
Meteor.call("getUsername", userId, function (err, result) {
if(!err) {
Session.set("setUsername", result);
else {
console.log(err);
}
});
return Session.get("setUsername");
}
Server
Meteor.methods({
"getUsername": function (userId) {
var x = Meteor.users.find({_id: userId}, {fields: {username:1}}).fetch()[0];
return x.username;
}
});
The result of this code is an infinite loop of username passing to the client. Is there a way to stop the loop and pass only the data that is needed on the client? I believe the reactivity is causing the data to loop infinitely and I am not sure how to stop it. I tried using "reactive":false on my query in the server but it does not work.
If you want to access username everywhere in client templates (so thats why you put it into session), I would not set it in template helper. I would set it on startup and get username from session in template helpers (without calling server method)
If you need username just in one template, so you want to return its value from your template helper, do not put it into session, just return it in your server method callback.
Based on your sample code, I assume, you have a set of posts and you are retrieving user name based on user id for each post. Then instead of doing it this way, you should use publish composite package to publish related users as well.
Meteor.publishComposite('getPosts', function (postIds) {
return [{
find: function() {
return Posts.find({ _id: { $in: postIds }});
// you can also do -> return Posts.find();
// or -> return Posts.find({ /* or what ever your selector is to get the posts you need*/ });
},
children: [{
find: function(post) {
return Meteor.users.find({
id: post.userId //or the correct field in your post document to get user id
}, {
fields: {
"profile": 1
}
});
}
}}
}]
});
This way your publication will take care of publishing related users along with posts. You don't need to use methods and call them each time.