I have a NodeJS server running express with a get method at '/api/jobs/'. When called I get some querystring parameters from the url and they make a query against the MongoDB database to get jobs.
Example Url: '/api/jobs/?Groups=1,2&Statuses=3,4'
The MongoDB query i am trying to construct is this:
{ $or: [{"Group.Id" : 1}, {"Group.Id" : 2}], $or: [{"Status.Id": 3}, {"Status.Id": 4}]}
If I run this directly against the database I get the results I need but I can't think of way of constructing the query dynamically in JavaScript. Any attempt i've made gives me an object like this as the $or property can only exist once in a proper JSON object.
{$or : [{"Group.Id" : 1}, {"Group.Id" : 2}]}
Any ideas on how to do this either using JavaScript or thorugh the MongoDB Node API?
Firstly get the query properties, which will be strings:
const groupString = req.query.Groups; // === '1,2'
const statusString = req.query.Statuses; // === '3,4'
Split at the commas and parse as integers:
const groupNums = groupString.split(',').map(parseInt); // === [1, 2]
const statusNums = statusString.split(',').map(parseInt); // === [3, 4]
Then search for any of the group IDs. Your query should look like this:
const query = {"Group.Id": { $in: groupNums }, "Status.Id": { $in: statusNums } };
Do the same for the status IDs.
You can use this
app.get('/', function(req, res, next) {
var queryArry = [];
for (var i in req.query) {
var sampleObject = { value: [] };
sampleObject.name = i;
req.query[i].split(',').forEach(function(each) {
sampleObject.value.push(parseInt(each));
})
queryArry.push(sampleObject);
}
var mongoquery = {};
for (var i in queryArry) {
mongoquery[queryArry[i].name] = queryArry[i].value;
}
res.send(mongoquery);
});
But in this case, you have to send same name field of MongoDB and query parameter key. If you did this it is fully dynamic query builder.
Related
I am using nodejs for the server.
Currently I have a Json in my project folder.
name.json
{
"name_English": "Apple",
"name_German": "Apfel",
"name_French": "Pomme"
}
When I send request to server, it returns:
GET http://localhost:3030/name
{
"name_English": "Apple",
"name_German": "Apfel",
"name_French": "Pomme"
}
But I found it is not convenient for frontend development.
Is there any way to do something like below?
GET http://localhost:3030/name?lang=en
{
"name": "Apple"
}
GET http://localhost:3030/name?lang=fr
{
"name": "Apfel"
}
Edit 1
The code of getting the Json in Feathers.js
name.class.js
const nameLists = require('../name.json')
exports.Test = class Test {
constructor (options) {
this.options = options || {};
}
async find (params) {
return nameLists
}
};
Edit 2
Is it possible to make name.json like this?
{
"name": ${name}
}
Edit 3
The reason I want to achieve above because I have to return whole Json file.
For the internationalization library, it seems needed to handle outside the JSON and I don't know what is the best practise to do so.
Here's a full demonstration with just express. (Hope that's ok.)
const express = require('express');
const app = express();
const port = 3030;
const nameLists = require('./name.json');
const unabbreviateLanguage = {
en: 'English',
de: 'German',
fr: 'French'
}
function filterByLanguage(obj, abbr) {
let language = unabbreviateLanguage[abbr];
let suffix = '_' + language;
let result = {};
for (let key in obj) {
if (key.endsWith(suffix)) {
let newkey = key.slice(0, -suffix.length);
result[newkey] = obj[key];
}
}
return result;
}
app.get('/name', (req, res) => {
res.json(filterByLanguage(nameLists, req.query.lang));
});
app.listen(port);
e.g.:
curl http://localhost:3030/name?lang=de
output:
{"name":"Apfel"}
The idea is to iterate over the keys of the input object and prepare an output object that only has keys that match the language suffix (then strip off that suffix). You'll likely want to either have a mapping of en -> English, or just use the key names that match the parameter e.g., rename name_English to name_en.
In FeathersJS, the params object of find will store the query string of the passed in URL. So if you call http://localhost:3030/name?lang=en, the params object will be :-
{
query: {
lang: 'en'
}
}
You can then use this information to determine which result from the JSON to return.
https://docs.feathersjs.com/guides/basics/services.html#service-methods
Your question appears to be two parts: handling queries, and handling the internationalization.
Handling the query:
Feathers presents queries through the context object at the following location:
context.params.query
Handling Internationalization:
There are many solid packages available for handling internationalization:
https://openbase.com/categories/js/best-nodejs-internationalization-libraries?orderBy=RECOMMENDED&
This is my ObjectIds array -
obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
I am using these objectids to serach whether they exist in the db or not and based on that I want to send the response to server.
This is the code I am trying but I dont know how to populate the array and send it to the server.
var msg = [];
obj_ids.map((ele) => {
Lead.find({ _id: ele._id }, async function (error, docs) {
if (docs.length) {
msg.push(
`Lead already exist for Lead id - ${ele._id} assgined to ${docs[0].salesPerson}`
);
} else {
msg.push(`Lead doesn't exist for Lead id: ${ele._id}`);
const newDuty = new AssignedDuty({
duty: ele._id,
salesPerson: req.body.salesPerson,
});
await newDuty.save();
}
});
});
res.json(msg);
By doing this approach I am getting an empty array. I cannot put res.json(msg) inside the loop. If it is possible by using async-await, please guide me through.
You don't need to make multiple queries to find whether given object ids exist in the database.
Using $in operator, you can make one query that will return all the documents where the _id is equal to one of the object id in the list.
const docs = await Lead.find({
_id: {
$in: [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
}
});
After this query, you can check which object id is present in the docs array and which is absent.
For details on $in operator, see $in comparison operator
Your code can be simplified as shown below:
const obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
];
const docs = await Lead.find({
_id: { $in: obj_ids }
});
const msg = [];
obj_ids.forEach(async (id) => {
const doc = docs.find(d => d._id == id);
if (doc) {
msg.push(
`Lead already exist for Lead id - ${doc._id} assgined to ${doc.salesPerson}`
);
}
else {
msg.push(`Lead doesn't exist for Lead id: ${id}`);
const newDuty = new AssignedDuty({
duty: id,
salesPerson: req.body.salesPerson
});
await newDuty.save();
}
});
res.json(msg);
I have a table with three fields.
--------------------------------
id_ram | value | um
id_ram is primary key.
I'm designing an Express API with sequelize. I am not able to update multiple rows of this table.
I am passing json array like below.
[
{"id_ram":"54","value":"11","um":"GB"},
{"id_ram":"34","value":"22","um":"GB"},
{"id_ram":"70","value":"33","um":"GB"}
]
This is what I have tried so far.
router.post('/update',function (req, res) {
var api_name = middleware_name + " /update";
// - Check if the input array is passed
if(req.body.array == undefined) {
var jsonErrorResponse = api_manager.PrepareJSONResponse(api_name, "", "value of input parameter [array] is undefined");
api_manager.WriteErrorLogFile(req,api_name,jsonErrorResponse,jsonErrorResponse);
res.send(jsonErrorResponse);
return;
}
else {
var create_values_array = "";
try {
//Parse INPUT JSON Array
create_values_array = JSON.parse(req.body.array);
}
catch (err) {
//Raise SyntaxError
var jsonErrorResponse = api_manager.PrepareJSONResponse(api_name,"",err.message);
var jsonInternalError = api_manager.PrepareJSONResponse(api_name,"",err);
api_manager.WriteErrorLogFile(req,api_name,jsonErrorResponse,jsonInternalError);
//Send error Response
res.send(jsonErrorResponse);
}
ObjectModel.bulkCreate(
create_values_array
, {updateOnDuplicate: ["id_ram"]})
.then(created_objects => { // Notice: There are no arguments here, as of right now you'll have to...
//Send Response and Log Action
var jsonData = api_manager.PrepareJSONResponse(api_name,created_objects,"");
api_manager.WriteInfoLogFile(req,api_name,jsonData);
res.send(jsonData);
}).catch (function (err) {
//Write Error Log
var jsonErrorResponse = api_manager.PrepareJSONResponse(api_name,"",err.message);
var jsonInternalError = api_manager.PrepareJSONResponse(api_name,"",err);
api_manager.WriteErrorLogFile(req,api_name,jsonErrorResponse,jsonInternalError);
//Send error Response
res.send(jsonErrorResponse);
});
}
});
How can we implement bulkUpdate like bulkCreate in sequelize orm for MSSQL ?
For bulk create you do it this way.
//array of object to be inserted
const data = [
{field1: "value1"}, {field2: "value2"}...
]
Model.bulkCreate(data, {returning: true}) //if you don't pass returning true it will not return the data
bulkCreate() can also be used for updating as well.
bulkCreate(data , {updateOnDuplicate : true })
Hello {updateOnDuplicate : true } raise an error because SQL Server doesn't support this feature.
Is It possible to take another way ?
You could achieve this by writing a function that calls the Sequelize upsert function in a loop like so:
const records = [
{ field1: 'value1', field2: 'value2' },
...
];
async function bulkUpsert(records) {
return Promise.all(
records.map((record) {
return Model.upsert(record);
})
);
}
I'm receiving params from my get request that looks like this:
{ location: 'Venice', weather: 'Dry', what: 'Yoga', who: 'Bob' }
I then query a mongodb database that loops through each of the key and value pairs and queries for their union in the database.
I then save the returned values to outputCaption and then use a callback to pass the outputCaption back.
The problem is the callback gets called as many times as their key-value pairs looped over.
I'm forced to do this because I need the callback inside the db.Phrase.find call but I call that multiple times...
So I've fixed it using the code in app.get (I wait until all the keys have defined values in outputCaption before doing anything)
It works, but I can't imagine it's the best way to do it so I'm hoping there's a less hackish way?
Thanks
server.js
var express = require('express');
var db = require('./modules/db')
var logic = require('./modules/logic')
...
app.get('/phrase', function(req, res){
logic(req.query, function(outputCaption){
var flag = true
for (key in outputCaption){
if (outputCaption[key] === null){
console.log('Incomplete')
var flag = false;
}
}
if (flag === true) {
console.log(outputCaption);
};
});
})
...
logic.js
var db = require('./db')
var logic = function(params, callback){
var outputCaption = {
who: null,
what: null,
location: null,
weather: null
};
for (key in params){
category = key.toLowerCase();
option = params[key].toLowerCase();
db.Phrase.find({$and: [
{category: category},
{option: option}
]}, function(err, phrases){
if (err) return console.error(err);
var options = Object.keys(phrases).length
var idxOfOptionPicked = Math.floor(Math.random() * options)
outputCaption[phrases[idxOfOptionPicked].category] = phrases[idxOfOptionPicked].phrase
callback(outputCaption)
})
}
}
module.exports = logic;
Instead of firing multiple queries and performing a union of the result at the client side, make use of the query operators to allow MongoDB to do the union for you.
That way you:
Avoid multiple hits to the database.
Avoid multiple callback handlers.
Can post process the results in a single callback handler.
You can modify your code to prepare a query object from the request parameter,
var params = {location: 'Venice', weather: 'Dry', what: 'Yoga', who: 'Bob' };
var query = {};
var conditions = [];
Object.keys(params).forEach(function(key){
var $and = {};
$and["category"] = key.toLowerCase();
$and["option"] = params[key];
conditions.push($and);
});
(conditions.length > 1)?(query["$or"] = conditions):(query = conditions[0]);
Now the constructed query looks like:
{ '$or':
[ { category: 'location', option: 'Venice' },
{ category: 'weather', option: 'Dry' },
{ category: 'what', option: 'Yoga' },
{ category: 'who', option: 'Bob' }
]
}
You can pass this object to the find() method, to get the results in a single hit:
db.Phrase.find(query,callback);
This way your code remains cleaner and easier to understand.
I have two collections:
users:
{
_id: ObjectId('123...'),
docs: [
ObjectId('512d5793abb900bf3e000002'),
ObjectId('512d5793abb900bf3e000001')
]
}
docs:
{
_id: ObjectId('512d5793abb900bf3e000002'),
name: 'qwe',
...
}
{
_id: ObjectId('512d5793abb900bf3e000001'),
name: 'qwe2',
...
}
I want to get docs from ids. I try this solution, but I get this message:
{ db: { domain: null,
_events: {},
_maxListeners: 10,
databaseName: 'test', ...
Your message looks like a mongodb cursor returned from find by native mongodb driver.
To get actual data you should use toArray function of the cursor:
var ObjectID = require('mongodb').ObjectID;
// you shall wrap each id in ObjectID
var idsProjects = [
ObjectID('512d5793abb900bf3e000002'),
ObjectID('512d5793abb900bf3e000001')
];
collectionProjects.find({
_id: { $in: idsProjects }
},{
_id: -1, // use -1 to skip a field
name: 1
}).toArray(function (err, docs) {
// docs array here contains all queried docs
if (err) throw err;
console.log(docs);
});
But I recommend you to switch from native mongodb driver to some wrapper around it like monk.
If you care about the order of the list, the answer of Mr.Leonid may not work as expected to do.
That's because find gets the docs that have _id equals to any _ids $in the list so the output docs will be ordered by the main order of the collection itself not the order of the input list.
To solve that you can just use the normal findOne with a for loop to the list.
The code will look like:
var ObjectID = require('mongodb').ObjectID;
var idsProjects = [
'512d5793abb900bf3e000002',
'512d5793abb900bf3e000001'
];
let usersList = new Array();
for (let index = 0; index < idsProjects.length; index++) {
const myID = idsProjects[index];
const query = { _id: ObjectID(myID) };
const options = {
projection: {name: 1 };
var user= await collectionProjects.findOne(query,options);
usersList.push(user);
}
// that's it,
// here we have a list of users 'usersList'
//with same order of the input ids' list.
console.log(usersList);