Calling Values from Functions with PouchDB using Find() within a loop - javascript

I am trying to loop through the results from a Find() Mango Query and make a all to another function to get extra data to use in my report.
I am looping a list of patient documents from a Find() query but I want to pull in the "last visit" from another list of "visit" documents by calling a function that performs a query but I am having problems.
I can call the function "Get_Static_Value()" and it will return a value however when I send the Patient_ID to the function "Get_Last_Visit(Patient_ID)" then the value comes back as "undefined" although the function is called and will write the "Vist_Date" to the console.
I believe my issue is caused because the promise in the query is not resolving but I am unsure of the syntax to get the value back into my loop once the function has processed.
I read the documement https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html and in the section "Rookie mistake #2: WTF, how do I use forEach() with promises?" I think it identifies my problem with the syntax:
db.allDocs({include_docs: true}).then(function (result) {
return Promise.all(result.rows.map(function (row) {
return db.remove(row.doc);
}));
}).then(function (arrayOfResults) {
// All docs have really been removed() now!
});
However the code above is for alldocs and not find() so I am a little bit stuck on how I process the same method on results from a Find() query.
I have created a JSfiddle to show my code and demonstrate my issue.
https://jsfiddle.net/movitico/gkb89uyf/
// Create the Database
var db = new PouchDB('patient_test');
// Add Patient Documents
function Add_Patients() {
db.bulkDocs([{
_id: '1',
type: 'patient',
Patient_Name: 'Patient 1',
Patient_Status: 'Active'
},
{
_id: '2',
type: 'patient',
Patient_Name: 'Patient 2',
Patient_Status: 'Active'
},
{
_id: '3',
type: 'patient',
Patient_Name: 'Patient 3',
Patient_Status: 'Active'
}
]);
}
function Add_Visits() {
// Add Visit Documents
db.bulkDocs([{
_id: 'v1',
type: 'visit',
Patient_ID: '1',
Visit_Date: "06/01/2018"
},
{
_id: 'v2',
type: 'visit',
Patient_ID: '1',
Visit_Date: "05/01/2018"
},
{
_id: 'v3',
type: 'visit',
Patient_ID: '1',
Visit_Date: "02/22/2018"
},
{
_id: 'v4',
type: 'visit',
Patient_ID: '2',
Visit_Date: "02/22/2014"
},
{
_id: 'v5',
type: 'visit',
Patient_ID: '2',
Visit_Date: "02/22/2000"
},
{
_id: 'v6',
type: 'visit',
Patient_ID: '2',
Visit_Date: "02/22/1987"
}
]);
}
function Load_Patients() {
$('#patient_list').empty();
db.createIndex({
index: {
fields: ['Patient_Name', 'type', 'Patient_Status']
}
}).then(function(result) {
db.find({
selector: {
Patient_Name: {
$gt: true
},
type: {
$eq: 'patient'
},
Patient_Status: {
$eq: 'Active'
}
},
sort: [{
"Patient_Name": "asc"
}]
}, function(error, response) {
console.log(response);
for (i in response.docs) {
var Static_Value = Get_Static_Value();
var Last_Visit = Get_Last_Visit(response.docs[i]._id);
$('#patient_list').append('<li>' + response.docs[i]._id + ' ' + response.docs[i].Patient_Name + ' [' + Static_Value + ']' + ' ' + Last_Visit + '</li>');
}
})
});
}
Add_Patients();
Add_Visits();
$('#button_load_patients').unbind().click(function(e) {
Load_Patients();
});
function Get_Static_Value() {
return 'I am static';
}
function Get_Last_Visit(Patient_ID) {
db.createIndex({
index: {
fields: ["Visit_Date", "type"]
}
}).then(function(result) {
db.find({
selector: {
Visit_Date: {
$gt: true
},
type: {
$eq: 'visit'
},
Patient_ID: {
$eq: Patient_ID
}
},
sort: [{
"Visit_Date": "desc"
}],
limit: 1
}).then(function(response) {
console.log(response);
if (response.docs.length > 0) {
Visit_Date = response.docs[0].Visit_Date;
} else {
Visit_Date = 'Never';
}
console.log(Visit_Date);
return Visit_Date;
});
})
}
Once I have returned the "Visit_Date" value then I would manipulate it using MomentJS and include or exclude it from the results that are appended to the div.
I would appreciate any advice on what I am doing wrong.

With the help of a colleague I got the solution to this problem and will post the solution as I am sure there are people out there who are as confused as me.
The things that needed to change in my code were that the call to the "Get_Last_Visit" function needed a .then to return the value of the promise. I also needed to add "let i" to make the "i" variable a global variable and available within the function:
for (let i in response.docs) {
var Static_Value = Get_Static_Value();
Get_Last_Visit(response.docs[i]._id)
.then(function(Last_Visit) {
$('#patient_list').append('<li>' + response.docs[i]._id + ' ' + response.docs[i].Patient_Name + ' [' + Static_Value + ']' + ' ' + Last_Visit + '</li>');
})
}
Here is an updated jsfiddle.
https://jsfiddle.net/movitico/9xorLham/

Related

Is it possible to query JSON data from amazon Dynamodb using javascript SDK

I need help on amazon Dynamo. I am looking to special query in dynamodb
my JSON looks below
{
blocknumber: '20',
BusinessData: {
BASE_UNIT: 'kg',
FARMERID: 'FAINKABR0001',
FARMLOCATION: 'Farm 3927',
GAPINFO: {},
PLANTINGDATE: '2020-11-02T18:30:00.000Z',
PRODUCEQUANTITES: [
{
name: 'Priya',
qty: 200
}
],
SELECTED_UNIT: {
NAME: 'kg'
}
},
chaincodeID: 'producechaincode',
docType: 'Produce',
PRID: 'PRFAINKABR0007',
PRODUCE: 'Sweetcorn',
STATUS: 'Approved',
timestamp: '2020-12-06T13:03:08.857Z'
}
I would like to query all Data(Produce) where FARMERID is 'FAINKABR0001'.
I went through all of the examples but it seems that I can query only on hash key, sort key and using GSI.
Can we query it using Javascript SDK of AWS?
Thanks in advance
The Query operation in DynamoDB finds items based on primary key values. You can query any table or secondary index (GSI) that has a composite primary key (a partition key and a sort key).
Now for your question, you have two options:
Option 1
Make FARMERID as your GSI
Option 2
Use Scan method and filter the result
Now you will need to do cost evaluation based on your need. Each method has it's own pros and cons.
PFB some references:
Scan-JS SDK
Query-DDB
Based on comment, one approach could be
var data =
[
{
blocknumber: '20',
BusinessData: {
BASE_UNIT: 'kg',
FARMERID: 'FAINKABR0001',
FARMLOCATION: 'Farm 3927',
GAPINFO: {},
PLANTINGDATE: '2020-11-02T18:30:00.000Z',
PRODUCEQUANTITES: [
{
name: 'Priya',
qty: 200
}
],
SELECTED_UNIT: {
NAME: 'kg'
}
},
chaincodeID: 'producechaincode',
docType: 'Produce',
PRID: 'PRFAINKABR0007',
PRODUCE: 'Sweetcorn',
STATUS: 'Approved',
timestamp: '2020-12-06T13:03:08.857Z'
},
{
blocknumber: '20',
BusinessData: {
BASE_UNIT: 'kg',
FARMERID: 'FAINKABR0002',
FARMLOCATION: 'Farm 3927',
GAPINFO: {},
PLANTINGDATE: '2020-11-02T18:30:00.000Z',
PRODUCEQUANTITES: [
{
name: 'Priya',
qty: 200
}
],
SELECTED_UNIT: {
NAME: 'kg'
}
},
chaincodeID: 'producechaincode',
docType: 'Produce',
PRID: 'PRFAINKABR0007',
PRODUCE: 'Sweetcorn',
STATUS: 'Approved',
timestamp: '2020-12-06T13:03:08.857Z'
},
{
blocknumber: '20',
BusinessData: {
BASE_UNIT: 'kg',
FARMERID: 'FAINKABR0001',
FARMLOCATION: 'Farm 3927',
GAPINFO: {},
PLANTINGDATE: '2020-11-02T18:30:00.000Z',
PRODUCEQUANTITES: [
{
name: 'Priya',
qty: 200
}
],
SELECTED_UNIT: {
NAME: 'kg'
}
},
chaincodeID: 'producechaincode',
docType: 'Produce',
PRID: 'PRFAINKABR0007',
PRODUCE: 'Sweetcorn',
STATUS: 'Approved',
timestamp: '2020-12-06T13:03:08.857Z'
}
];
function filterResponse(data, id) {
for(var i = 0; i < data.length; i++) {
if(data[i].BusinessData.FARMERID === id ) {
console.log(data[i]);
}
}
}
filterResponse(data, "FAINKABR0001");
Thanks #Atul Kumar for Help I have also added my whole code my be in future somebody face same issue
Here FilterExpression as FilterExpression: "BusinessData.FARMERID = :farmeridvalue"
Here we need to give FilterExpression value which attribute we want to query i.e BusinessData.FARMERID and give one name as I give farmeridvalue now you have set ExpressionAttributeValues as search value for me as FAINKABR0001
see whole scan code as below
var params = {
TableName: "Your_tableName",
FilterExpression: "BusinessData.FARMERID = :farmeridvalue",
ExpressionAttributeValues: {
":farmeridvalue" :"FAINKABR0001"
}
};
docClient.scan(params, onScan);
function onScan(err, data) {
if (err) {
console.error("Unable to scan the table. Error JSON:", JSON.stringify(err, null, 2));
} else {
// print all the movies
console.log("Scan succeeded.", data);
data.Items.forEach(function(Block) {
console.log( "result",
Block.docType + ": ",
Block.timestamp, "- rating:", Block.BusinessData.FARMERID);
});
// continue scanning if we have more movies, because
// scan can retrieve a maximum of 1MB of data
if (typeof data.LastEvaluatedKey != "undefined") {
console.log("Scanning for more...");
params.ExclusiveStartKey = data.LastEvaluatedKey;
docClient.scan(params, onScan);
}
}
}

How to use .each to build an array

So we're working in this system and buildig our own page. We built a form to insert timeline data using a .xwd file. We use javascript to retrieve the data and fill it in a variable to store it. The main page (title:) just has single values, but the actual events should be in an array.
I'm want to use V to fill the array.
$(x_currentPageXML).children().each(function(index, elem){
});
Right now what I have is this and I want to fill the "events" array using the foreach I showed above. Putting the .each inside in the var didn't work and I wouldn't know how else to do it.
var SictTimeline = new function() {
this.loadJS = function () {
$.getScript(x_templateLocation + 'common_html5/js/timeline.js')
.done(function (script, textStatus) {
var make_the_json = $(x_currentPageXML).children().map(function (element) {
return {
title: {
media: {
url: element.getAttribute("url"),
caption: element.getAttribute("tip"),
},
text: {
headline: element.getAttribute("name"),
text: '<p>' + element.getAttribute("text") + '</p>'
}
},
events: {
media: {
url: element.getAttribute("url"),
caption: element.getAttribute("tip"),
},
start_date: {
month: '8',
day: '9',
year: '1963'
},
text: {
headline: element.getAttribute("name"),
text: element.getAttribute("text")
}
}
}
})
var timeline_json = make_the_json; // replace make_the_json() with the JSON object you created
// two arguments: the id of the Timeline container (no '#')
// and the JSON object or an instance of TL.TimelineConfig created from
// a suitable JSON object
window.timeline = new TL.Timeline('timeline-embed', timeline_json);
})
.fail(function (jqxhr, settings, exception) {
console.log('Failed to load the script for the timeline');
});
}
// function called every time the page is viewed after it has initially loaded
this.pageChanged = function() {
}
// function called every time the size of the LO is changed
this.sizeChanged = function() {
}
this.init = function() {
this.loadJS();
// call this function in every model once everything's loaded
x_pageLoaded();
}
}
An example of the xml-file with the values
<?xml version="1.0"?>
<learningObject editorVersion="3" targetFolder="Nottingham" name="Learning Object Title" language="en-GB" navigation="Linear" textSize="12" theme="default" displayMode="fill window" responsive="true">
<SictTimeline linkID="PG1592486441661" name="My page" media="SictTimeline" text="<p>Text for my page</p>
" url="FileLocation + 'media/https___images.genius.com_53c4575fa3f97a8d4b18d69a600afaf0.900x900x1.jpg'" tip="Description for Image 1"></SictTimeline></learningObject>
I guess what you are trying to achieve is to generate an array of objects based on the number (and properties) of elements inside $(x_currentPageXML). For that purpose you need to use the .map() method:
events: $(x_currentPageXML).children().map(function (index, element) {
return {
media: {
url: element.getAttribute("url"),
caption: element.getAttribute("tip"),
},
start_date: {
month: '8',
day: '9',
year: '1963'
},
text: {
headline: element.getAttribute("name"),
text: element.getAttribute("text")
}
}
}).get()
I'm not sure I completely understand the question, but you want to extract stuff from each element in the loop right?
Replace x_currentPageXML with elem inside the loop
var result = []
$(x_currentPageXML).children().each(function (index, elem) {
var make_the_json = {
title: {
media: {
url: elem.getAttribute("url"),
caption: elem.getAttribute("tip"),
},
text: {
headline: elem.getAttribute("name"),
text: '<p>' + elem.getAttribute("text") + '</p>'
}
},
events: [
{
media: {
url: elem.getAttribute("url"),
caption: elem.getAttribute("tip"),
},
start_date: {
month: '8',
day: '9',
year: '1963'
},
text: {
headline: elem.getAttribute("name"),
text: elem.getAttribute("text")
}
}
]
};
result.push(make_the_json)
})

Return fields from different collection Mongoose

Imagine a function that finds users by their name and returns them.
User.aggregate(
[
{ $sort: { userFirstName: 1, userLastName: 1 } },
{
$addFields: {
firstLastName: { $concat: ['$userFirstName', ' ', '$userLastName'] },
lastFirstName: { $concat: ['$userLastName', ' ', '$userFirstName'] }
}
},
{
$match: $match // Set from above with match crit
},
{
$group: {
_id: null,
total: { $sum: 1 },
data: {
$push: {
'_id': '$_id',
'userFirstName': '$userFirstName',
'userLastName': '$userLastName',
'userProfileImage': '$userProfileImage',
'userVihorCategory': '$userVihorCategory'
}
}
}
},
{
$project: {
total: 1,
data: { $slice: ['$data', start, limit] }
}
}
]
).exec((errAgg, results) => {...
This works, it splices them and returns them correctly.
There is another collection that tracks user connections.
{
user: { type: Schema.Types.ObjectId, ref: 'User' },
userConnection: { type: Schema.Types.ObjectId, ref: 'User' },
userConnectionStatus: {
type: String,
enum: ['following', 'blocked', 'requested']
}
}
Eg User: me, userConnection: 'someone', userConnectionStatus: 'following'
What I am trying to achive is to return 2 more fields,
1. My userConnectionStatus to him
2. His userConnectionStatus to me
And not to return users who have blocked me.
What is the best approach when it comes to this DB structure.
Thank you for your time
Preventing blocked users was solved by selecting all blocked users, and adding $nin in match inside aggregate.
For connection status, I have resolved the problem by adding 2 virtual fields to User.
UserMongoSchema.virtual('userConnectionStatus', {
ref: 'UserConnection',
localField: '_id',
foreignField: 'user',
justOne: true
});
UserMongoSchema.virtual('connectionStatus', {
ref: 'UserConnection',
localField: '_id',
foreignField: 'userConnection',
justOne: true
});
And populating them on results
...
.exec((errAgg, results) => {
User.populate(results[0].data, [
{ path: 'userConnectionStatus', match: { userConnection: req.userCode }, select: 'userConnectionStatus' },
{ path: 'connectionStatus', match: { user: req.userCode }, select: 'userConnectionStatus' },
], (errPop, populateResponse) => {
if (errPop) { return next(errPop); }
populateResponse = populateResponse.map((row) => {
row['userConnectionStatus'] = row.userConnectionStatus ? row.userConnectionStatus.userConnectionStatus : null;
row['connectionStatus'] = row.connectionStatus ? row.connectionStatus.userConnectionStatus : null;
return row;
});
...
Looking at the order of actions, I think this won't affect performance since I am running populate only on those matched top X (max 100) results.
I won't mark this as Answer yet. If you have any opinion about if this is bad practice or if there is a better way of doing it, feel free to comment.

orientjs: upsert edge in transaction

How can I upsert an edge in a transaction using orientjs? My current implementation upserts two vertices and always creates a new edge:
function add(db, from, edge, to, cb) {
cb = cb || function() {};
log(
'[' + from.clazz + ']' + JSON.stringify(from.attributes) + ' ' +
'-[' + edge.clazz + ']' + JSON.stringify(edge.attributes) + '> ' +
'[' + to.clazz + ']' + JSON.stringify(to.attributes)
);
db.let('source', function(s) {
s.update(from.clazz)
.set(from.attributes)
.upsert()
.where(from.attributes)
.return('after #this');
})
.let('destination', function(d) {
d.update(to.clazz)
.set(to.attributes)
.upsert()
.where(to.attributes)
.return('after #this');
})
.let('edge', function(e) {
e.create('EDGE', edge.clazz)
.from('$source')
.to('$destination')
.set(edge.attributes);
})
.commit()
.return('$edge')
.all()
.then(cb);
}
I've not found any upsert method for an edge in OrientJS, but you can prevent creation of edges twice between the same source and destination. You need to just
Create a UNIQUE index while creating an edge migration.
Here is the migration code for creating an edge with a unique index :
exports.up = (db) => {
return db.class.create('HasApplied', 'E')
.then((hasApplied) => {
return hasApplied.property.create(
[{
name: 'out',
type: 'link',
linkedClass: 'Consultant',
mandatory: true
}, {
name: 'in',
type: 'link',
linkedClass: 'Job',
mandatory: true
}, {
name: 'technicalQuestions',
type: 'embedded'
}, {
name: 'technicalAnswers',
type: 'embedded'
}, {
name: 'behavioralQuestions',
type: 'embedded'
}, {
name: 'behavioralAnswers',
type: 'embedded'
}, {
name: 'artifacts',
type: 'embeddedset'
}, {
name: 'comments',
type: 'string',
}, {
name: 'createdAt',
type: 'datetime'
}, {
name: 'updatedAt',
type: 'datetime'
}]
);
})
.then(() => db.query('CREATE INDEX HasApplied.out_in ON HasApplied (out, in) UNIQUE'));
};
Then when your code trying to run transaction containing let block :
.let('edge', function(e) {
e.create('EDGE', edge.HasApplied)
.from('$source')
.to('$destination')
.set(edge.attributes);
})
will throw db level error if found already exist edge between the same $source and $destination .
I hope this will definitely help you :)

node js - multi tasking for each item in array

I am trying to implement a way to upload files asynchronously.
I have a process I want to apply to every item of my array.
I am taking the name of each item, call a API to get additinal information about it, then I am sending it to a text to speech utility, and upload the resultingwav file to a S3 instance.
I can't find a way to do this asynchronously, and wait for all of them to finish.
I can do it in serie, but it take lots of time (12 minutes for 30 files (2mb each file)).
I tried to implement a asynchronous way, which takes around 5 minutes (7 minutes less), but I fear the problem is on the net line?
Function to apply to each item:
function doAll(c, lan, country, fileName, callback){
getNews(c, lan)
.then(function(newsResults){
getWavFile(newsResults, lan, fileName)
.then(function(wavResults){
uploadToS3(country,lan,fileName)
.then(function(s3Results){
return callback("done");
}, function(s3err){
console.log('s3 error: ',s3err);
return callback("done");
})
}, function(waverr){
console.log('wav error: ',waverr);
})
}, function(newserr){
console.log('news error: ',newserr);
})
}
Array example :
var arr = [
{
_id: '5769369ba2d42fd82ca4d851',
Name: 'Sports',
Order: 1,
Color: 'White',
Description: 'ספורט',
UpdatedDate: '2016-07-28T07:44:47.906Z',
CreatedDate: '2016-06-21T12:44:11.468Z',
Country: 'IL',
Langs: [
{
Name: 'Sports',
IsoCode: 'en',
Url: 'SportsJSON',
_id: '576b93486c7a9ff025275836'
},
{
Name: 'ספורט',
IsoCode: 'iw',
Url: 'HebrewSportsJSON',
_id: '576be6ad56126ccc25852613'
}
]
},
{
_id: '576bf4eb28176a3e5ce15afa',
Name: 'Top Stories',
Description: 'הכותרות',
Color: 'ww',
Order: 1,
UpdatedDate: '2016-07-10T12:01:26.713Z',
CreatedDate: '2016-06-23T14:40:43.435Z',
Country: 'IL',
Langs: [
{
Name: 'כותרות',
Url: 'HebrewTopStoriesJSON',
IsoCode: 'iw',
_id: '576bf52228176a3e5ce15afb'
},
{
Name: 'Top Stories',
IsoCode: 'en',
Url: 'TopStoriesJSON',
_id: '576bf94d28176a3e5ce15afd'
}
]
},
{
_id: '5756d5d6c4a3dfe478b16aa2',
Description: 'Nation Channel',
Order: 1,
Color: 'blue',
Code: 'Nation',
Name: 'Nation',
UpdatedDate: '2016-06-24T22:23:07.198Z',
CreatedDate: '2016-06-07T14:10:30.699Z',
Country: 'US',
Langs: [
{
Name: 'Nation',
IsoCode: 'en',
Url: 'NationJson',
_id: '576db2cb28176a3e5ce15b02'
}
]
}
]
My asynchronous way:
var array = [] // see the example how array look like
var newArray= [];
console.log('start uploading files time:', new Date());
for (var i = 0; i < array.length; i++) {
var list = array[i].Langs;
for (var j= 0; j < list.length; j++) {
var c = list[j];
var lan = convertIsoCode(c.IsoCode);
var fileName = array[i].Name + "_" + lan;
var country = array[i].Country;
doAll(c,lan,country,fileName, function(){
newArray.push(array[i]);
if (array.length == newArray.length) {
console.log('done');
defer.resolve('done');
}
})
}
}
EDIT:
I tried to do it with async.each and async.parallel, but didn't succeed, can anyone show me the right way to implement it?
Removed newArray since you don't need it for anything useful, it was wasting CPU time and was a horrendous way of tracking what was done. A simple counter would have done the tricks.
Gone ES6 since it's 2016. Also added semi colon because you were using them inconstitently.
Also, doAll is not a meaningful name.
'use strict';
const async = require('async');
let array = [/*data*/];
console.log('START ' + (new Date()));
//Asynchronously iterate throught the array
async.each(array, (item, callback) => {
//item is your array[i]
async.each(item.Langs, (lang, callback) => {
//lang is your array[i].Langs[j]
let lan = convertIsoCode(item.IsoCode),
fileName = item.Name + '_' + lan,
country = item.Country;
//Apply your functions
getNews(c, lan).then((newsResults) => {
getWavFile(newsResults, lan, fileName).then((wavResults) => {
uploadToS3(country,lan,fileName).then((s3Results) => {
//Everything is OK, callback without error
callback();
}, (s3err) => {
//Raise the error
callback(s3err);
});
}, (waverr) => {
console.log('wav error: ',waverr);
//Raise the error
callback(waverr);
});
}, (newserr) => {
console.log('news error: ',newserr);
//Raise the error
callback(newserr);
});
}, (error) => {
callback(error);
});
}, (error) => {
//If a error was raised, everything pending will be aborted and the error will be displayed
if(error) {
console.log(error);
//Else, just report it did fine
} else {
console.log('OK');
}
});

Categories