So I will be constantly retrieving an object with the following format:
student: {
"student_id": "12345",
"location": "below",
},
]
},
]
Thank you and will accept answer and upvote!
Something like this should do the trick:
var students = [];
function addStudent(student) {
// Check if we already know about this student.
var existingRecord = students.find(function (s) {
return s.student_id === student.student_id;
});
var classInfo = {
class_number: student.class_number,
location: student.location
};
if (!existingRecord) {
// This is the first record for this student so we construct
// the complete record and add it.
students.push({
student_id: student.student_id,
classes: [classInfo]
});
return;
}
// Add to the existing student's classes.
existingRecord.classes.push(classInfo);
}
You would then invoke it as follows:
addStudent({
"student_id": "67890",
"class_number": "abcd",
"location": "below",
});
Runnable JSBin example available here.
More available on Array.prototype.find at MDN.
This problem can be solved using indexing by student_id. For example:
var sourceArray = [{...}, {...}, ...];
var result = {};
sourceArray.forEach(function(student){
var classInfo = {
class_number: student.class_number,
location : student.location
};
if(result[student.student_id]){
result[student.student_id].classes.push(classInfo);
} else {
result[student.student_id] = {
student_id : student.student_id,
classes : [classInfo]
}
}
});
// Strip keys: convert to plain array
var resultArray = [];
for (key in result) {
resultArray.push(result[key]);
}
You can use also result format that contains objects, indexed by student_id or plain array resultArray.
Related
I'm trying to return only values where john is found from a DynamoDB database.
I'm able to return values where it contains name: john from a mapped list, however the problem am having is that it appears to also be returning other values as well.
Running select: 'count' returns 1 match which is correct but it doesn't return anything when used.
I'm assuming that count just returns a number and not a specific select where john is matched.
I'm writing this in NodeJS; am hoping someone can help me figure this out.
I know that the value I only want shown are json elements where name: john, anything else I want omitted from being shown.
Here's my result as of right now:
{
"Department": [
{
"employees": [
{
"name": "john"
},
{
"name": "sally"
}
]
}
],
"Count": 1
}
My code:
const AWS = require('aws-sdk'); // eslint-disable-line import/no-extraneous-dependencies
const dc = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context, callback) => {
// Construct the params for filtering data through dynamodb
const params = {
FilterExpression: "contains(#department, :employees)",
ExpressionAttributeNames: {
"#department": "employees",
},
ExpressionAttributeValues: {
":employees": {
"name":"john"
}
},
ProjectionExpression: "#department",
TableName: 'mytable',
//Select: 'COUNT'
};
const resultDC = await dc.scan(params).promise();
var items = resultDC.Items;
var count = resultDC.Count;
var returned_list = {
'Department' : items,
'Count' : count,
};
// create a response
const response = {
statusCode: 200,
body: JSON.stringify(returned_list),
};
callback(null, response);
};
I suggest you to use a Local Secondary Index.
Take a look here.
I want to search in a collection with two parameters and there is no guarantee that both parameters will be available anyone of them can be missing I want to ignore it and search only with one parameter.
I also want to search in two fields with the second parameter using $or.
My Code
NodeJs Express Mongoose
colec.find({
$and: [{
'address.zip': req.query.p,
$or: [{ 'name': req.query.n }, { 'tags': req.query.n }]
}]
}, function (err, foundProfiles) {
//Some Code
})
my code before tags search
var terms = {};
if (req.query.q) {
var name = req.query.q;
}
if (req.query.p) {
terms['address.zip'] = req.query.p;
}
colec.find(terms, function(err, foundProfiles){
//some code
})
I got this after searching for a long time.
var dbQueries = [];
if (req.query.q) {
var search = req.query.q;
dbQueries.push({
$or: [
{ name: search },
{ tags: search },
]
});
}
if (req.query.p) {
dbQueries.push({ 'address.zip': req.query.p });
}
dbQueries = { $and: dbQueries }
Collection.find(dbQueries, function (err, foundProfiles) {
//some code
});
So here's the deal :
I have an array of objects with a child array of objects
askedAdvices
askedAdvice.replayAdvices
I'm looping trough the parent and foreach looping trough the childs and need to populate() two obejcts (I'm using sails)
The child looks like :
askedAdvices = {
replayAdvices : [{
bookEnd : "<ID>",
user : "<ID>"
}]
}
So my goal is to cycle and populate bookEnd and user with two findOne query, but I'm going mad with the callback hell.
Here's the Models code :
AskedAdvices Model
module.exports = {
schema : false,
attributes: {
bookStart : {
model : 'book'
},
replayAdvices : {
collection: 'replybookend'
},
user : {
model : 'user',
required : true
},
text : {
type : "text"
}
}
};
ReplyBookEnd Model
module.exports = {
schema : false,
attributes: {
bookEnd : {
model : 'book'
},
user : {
model : 'user',
required : true
},
text : {
type : "text"
}
}
};
Here's the Method code :
getAskedAdvices : function(req, res) {
var queryAskedAdvices = AskedAdvices.find()
.populate("replayAdvices")
.populate("user")
.populate("bookStart")
queryAskedAdvices.exec(function callBack(err,askedAdvices){
if (!err) {
askedAdvices.forEach(function(askedAdvice, i){
askedAdvice.replayAdvices.forEach(function(reply, i){
async.parallel([
function(callback) {
var queryBook = Book.findOne(reply.bookEnd);
queryBook.exec(function callBack(err,bookEndFound) {
if (!err) {
reply.bookEnd = bookEndFound;
callback();
}
})
},
function(callback) {
var queryUser = User.findOne(reply.user)
queryUser.exec(function callBack(err,userFound){
if (!err) {
reply.user = userFound;
callback();
}
})
}
], function(err){
if (err) return next(err);
return res.json(200, reply);
})
})
})
} else {
return res.json(401, {err:err})
}
})
}
I can use the async library but need suggestions
Thanks folks!
As pointed out in the comments, Waterline doesn't have deep population yet, but you can use async.auto to get out of callback hell. The trick is to gather up the IDs of all the children you need to find, find them with single queries, and then map them back onto the parents. The code would look something like below.
async.auto({
// Get the askedAdvices
getAskedAdvices: function(cb) {
queryAskedAdvices.exec(cb);
},
// Get the IDs of all child records we need to query.
// Note the dependence on the `getAskedAdvices` task
getChildIds: ['getAskedAdvices', function(cb, results) {
// Set up an object to hold all the child IDs
var childIds = {bookEndIds: [], userIds: []};
// Loop through the retrieved askedAdvice objects
_.each(results.getAskedAdvices, function(askedAdvice) {
// Loop through the associated replayAdvice objects
_.each(askedAdvice.replayAdvices, function(replayAdvice) {
childIds.bookEndIds.push(replayAdvice.bookEnd);
childIds.userIds.push(replayAdvice.user);
});
});
// Get rid of duplicate IDs
childIds.bookEndIds = _.uniq(childIds.bookEndIds);
childIds.userIds = _.uniq(childIds.userIds);
// Return the list of IDs
return cb(null, childIds);
}],
// Get the associated book records. Note that this task
// relies on `getChildIds`, but will run in parallel with
// the `getUsers` task
getBookEnds: ['getChildIds', function(cb, results) {
Book.find({id: results.getChildIds.bookEndIds}).exec(cb);
}],
getUsers: ['getChildIds', function(cb, results) {
User.find({id: results.getChildIds.userIds}).exec(cb);
}]
}, function allTasksDone(err, results) {
if (err) {return res.serverError(err);
// Index the books and users by ID for easier lookups
var books = _.indexBy(results.getBookEnds, 'id');
var users = _.indexBy(results.getUsers, 'id');
// Add the book and user objects back into the `replayAdvices` objects
_.each(results.getAskedAdvices, function(askedAdvice) {
_.each(askedAdvice.replayAdvices, function(replayAdvice) {
replayAdvice.bookEnd = books[replayAdvice.bookEnd];
replayAdvice.user = users[replayAdvice.bookEnd];
});
});
});
Note that this is assuming Sails' built-in Lodash and Async instances; if you're using newer versions of those packages the usage of async.auto has changed slightly (the task function arguments are switched so that results comes before cb), and _.indexBy has been renamed to _.keyBy.
When working with nodejs, I like to use console.log to see what data is available in an object.
However, this doesn't work with inherited properties:
var Person = function () {};
Person.prototype.name = "anonymous";
var p = new Person();
console.log(['p', p]); // [ 'p', {} ]
// This doesn't even give me a hint that it's inherited from Person!
console.log(['typeof p', typeof p]); // [ 'typeof p', 'object' ]
console.log(['p.name', p.name]); // "anonymous"
Given an object, how can view all the properties I can access?
If your purpose is just for debugging, you can check the __proto__ object:
function Person() {};
Person.prototype.name = "abc";
Person.prototype.smallObj = {
name: "abc"
};
Person.prototype.deepObj = {
one: {
two: {
three: {
four: "4"
}
}
}
};
var p = new Person();
console.log(p);
// Person {}
console.log(p.__proto__);
/*
Person {
name: 'abc',
smallObj: { name: 'abc' },
deepObj: { one: { two: [Object] } }
}
*/
var util = require("util");
console.log(util.inspect(p.__proto__, {depth: null}));
/*
Person {
name: 'abc',
smallObj: { name: 'abc' },
deepObj: { one: { two: { three: { four: '4' } } } }
}
*/
On the last one, using util.inspect() with the depth option will allow you to look further into deeply nested objects.
You are assigning property to constructor function Person. It does not share properties with instances. You need to add property to Person's prototype:
Person.prototype.name = "anonymous";
To find out if your object inherited from Person you can do:
p instanceof Person; // true
You can print out all of an object's enumerable properties by performing the following:
for (var key in p) {
console.log(key);
}
Use Object.getOwnPropertyNames() to get all properties that belong to an object:
console.log(Object.getOwnPropertyNames(Person))
// [ 'length', 'name', 'arguments', 'caller', 'prototype' ]
console.log(Object.getOwnPropertyNames(Object))
// ['length','name','arguments','caller','prototype','keys','create', 'defineProperty','defineProperties','freeze','getPrototypeOf','setPrototypeOf','getOwnPropertyDescriptor','getOwnPropertyNames','is','isExtensible','isFrozen','isSealed','preventExtensions','seal','getOwnPropertySymbols','deliverChangeRecords','getNotifier','observe','unobserve','assign' ]
Also you can combine Object.getOwnPropertyNames() with walking up the prototype chain:
var getAllProperties = function (object) {
var properties = []
do {
Object.getOwnPropertyNames(object).forEach((prop) => {
if (!~properties.indexOf(prop)) {
properties.push(prop)
}
})
} while (object = Object.getPrototypeOf(object))
return properties
}
I want to add a new field with a variable name to an object in the DB : meaning, I don't know the name of the field, but it's held in a variable "newFieldName".
So what I want to do is basically this:
var newFieldName = "world";
db.bios.update(
{ _id: 3 },
{ $set: {
"hello."+newFieldName: "Amazing Grace"
}
}
)
After the update, I expect the object "hello" to have a field "world" with the value "Amazing Grace".
but this doesn't even compile, let alone work. How can I do it?
You can use an intermediary object:
var update = { $set : {} };
update.$set['hello.' + newFieldName] = 'Amazing Grace';
db.bios.update({ _id : 3 }, update, ...)
var some_object = Posts.findOne({...});
var new_value = 1337;
Posts.update(another_object._id,
{$set: {
[some_object.some_field]:new_value,
}
}
);
To answer #yossale & #robertklep, the inline version is in fact possible using an expression and the comma operator:
var newFieldName = "world", o;
db.bios.update(
{ _id: 3 },
{$set:(o = {}, o["hello."+newFieldName] = "Amazing Grace", o)}
)
Simple is that:
var newFieldName = "world";
db.bios.update(
{ _id: 3 },
{ $set: {
["hello."+newFieldName]: "Amazing Grace"
}
}
);
#Andy's answer solved my problem.