orientjs: upsert edge in transaction - javascript

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 :)

Related

How to transform only 2 properties but keep remaining same as is in a nested object structure?

Apologies if title is not clear.
I am using json2csv npm package to prepare csv from json object and this package allows us to add a hook to transform object before actual csv line is prepared.
I only need to manipulate two properties out of all. How can I do this effectively? My code feels too bloated.
const {
Parser: Json2csvParser,
transforms: { unwind },
} = require('json2csv');
const json2csvFields = [
{ value: 'root.filename', label: 'File Name' },
{ value: 'issue.root.priority', label: 'Priority' },
{ value: 'issue.root.url', label: 'URL' },
{ value: 'issue.root.startline', label: 'Start Line' },
{ value: 'issue.root.stopline', label: 'Stop Line' },
{ value: 'issue.root.startcolumn', label: 'Start Column' },
{ value: 'issue.root.stopcolumn', label: 'Stop Column' },
{ value: 'issue.root.issuename', label: 'Issue Name' },
{ value: 'issue.root.issuecategory', label: 'Issue Category' },
{ value: 'issue._', label: 'Issue Description' },
];
const sampleData = [
{
root: {
filename:
'/home/users/john-doe/workspace/foo-project/src/main/classes/foo.cls',
},
issue: {
root: {
priority: 1,
url: 'www.example.com',
startline: 100,
stopline: 105,
startcolumn: 20,
stopcolumn: 25,
issuename: 'blah',
issuecategory: 'Category A',
},
_: ' Fox ',
},
},
];
const json2csvOptions = {
fields: json2csvFields,
quote: '',
header: true,
transforms: [
(item) => ({
'root.filename': item.root.filename.replace(
'/home/users/john-doe/workspace/foo-project/src/main/classes/',
''
),
'issue._': `"${item.issue._.trim()}"`,
// Except for the above two, everything else doens't need any transformation.
'issue.root.priority': item.issue.root.priority,
'issue.root.url': item.issue.root.url,
'issue.root.startline': item.issue.root.startline,
'issue.root.stopline': item.issue.root.stopline,
'issue.root.startcolumn': item.issue.root.startcolumn,
'issue.root.stopcolumn': item.issue.root.stopcolumn,
'issue.root.issuename': item.issue.root.issuename,
'issue.root.issuecategory': item.issue.root.issuecategory,
}),
],
};
const json2csvParser = new Json2csvParser(json2csvOptions);
const csv = json2csvParser.parse(sampleData);
console.log(csv);
This prints below output:
File Name,Priority,URL,Start Line,Stop Line,Start Column,Stop Column,Issue Name,Issue Category,Issue Description
foo.cls,1,www.example.com,100,105,20,25,blah,Category A,"Fox"
EDIT: Updated code to a working example.
After listing the two properties with special treatment, use Object.fromEntries and Object.entries to transform all the issue.root properties to their flat structure with .s in the property names. Then that object can be spread into the returned object.
const transformsFn = ({ root, issue }) => ({
'root.filename': root.filename.replace(
'/home/users/john-doe/workspace/foo-project/src/main/classes/',
''
),
'issue._': `"${issue._.trim()}"`,
...Object.fromEntries(
Object.entries(issue.root).map(
([key, val]) => [`issue.root.${key}`, val]
)
),
});
const json2csvOptions = {
fields: json2csvFields,
quote: '',
header: true,
transforms: [transformsFn],
};

Datatables date sort vs date display

I am using Datatables to display a table and I am pulling a list of datestimes from a MySQL database. These date times are not standard dates and look like this:
12/30/19 # 04:17 pm
How can I sort these accurately with Datatables?
Here is my code:
getRes(function (result) { // APPLIED CALLBACK
$('#resdatatable').DataTable({
data: result, // YOUR RESULT
order: [[ 0, "desc" ]],
autoWidth: false,
responsive: true,
columns: [
{ data: 'id', title: 'ID' },
{ data: 'bookingdatetime', title: 'Booking Date' },
{ data: 'name', title: 'Name' },
{ data: 'class', title: 'Class' },
{ data: 'pickupdatetime', title: 'Pick up' },
{ data: 'duration', title: 'Duration' },
{ data: 'dropdatetime', title: 'Drop off' },
{ data: 'age', title: 'Age' },
{ data: 'coverage', title: 'Coverage' },
{ data: 'quote', title: 'Quote' },
{
data: 'status',
title: 'Status',
render: function(data, type, row) {
let isKnown = statusList.filter(function(k) { return k.id === data; }).length > 0;
if (isKnown) {
return $('<select id="resstatus'+row.id+'" onchange="changeResStatus('+row.id+')" data-previousvalue="'+row.status+'">', {
id: 'resstatus-' + row.id, // custom id
value: data
}).append(statusList.map(function(knownStatus) {
let $option = $('<option>', {
text: knownStatus.text,
value: knownStatus.id
});
if (row.status === knownStatus.id) {
$option.attr('selected', 'selected');
}
return $option;
})).on('change', function() {
changeresstatus(row.id); // Call change with row ID
}).prop('outerHTML');
} else {
return data;
}
}
}
]
});
});
/**
* jQuery plugin to convert text in a cell to a dropdown
*/
(function($) {
$.fn.createDropDown = function(items) {
let oldTxt = this.text();
let isKnown = items.filter(function(k) { return k.id === oldTxt; }).length > 0;
if (isKnown) {
this.empty().append($('<select>').append(items.map(function(item) {
let $option = $('<option>', {
text: item.text,
value: item.id
});
if (item.id === oldTxt) {
$option.attr('selected', 'selected');
}
return $option;
})));
}
return this;
};
})(jQuery);
// If you remove the renderer above and change this to true,
// you can call this, but it will run once...
if (false) {
$('#resdatatable > tbody tr').each(function(i, tr) {
$(tr).find('td').last().createDropDown(statusList);
});
}
function getStatusList() {
return [{
id: 'Confirmed',
text: 'Confirmed'
}, {
id: 'Unconfirmed',
text: 'Unconfirmed'
}, {
id: 'Communicating',
text: 'Communicating'
}, {
id: 'Open',
text: 'Open'
}, {
id: 'Closed',
text: 'Closed'
}, {
id: 'Canceled',
text: 'Canceled'
}, {
id: 'Reallocated',
text: 'Reallocated'
}, {
id: 'No Show',
text: 'No Show'
}];
}
I need to sort bookingdatetime, pickupdatetime, dropdatetime accurately (they are currently being converted into MM/DD/YY in the PHP script)
Maybe you can prepend hidden <span> elements containing the respective unix timestamps in the cells that have dates (by manually parsing the dates). Then using such columns to sort alphabetically would practically sort time-wise.

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

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/

How to search in meteor, inside of an array in a collection?

I have the following two schemas,
Schema.extraOptions = new SimpleSchema({
name: {
type: String
},
content: {
type: String
},
note: {
type: String
}
});
var wordFields = {
_id: {
type: String,
optional: true,
autoform: {
omit: true
}
},
name: {
type: String,
label: 'Name'
}
'extraOptions.$': {
type: Schema.extraOptions,
optional: true
}
};
How can I search in wordFields for name, extraOptions.name and extraOptions.content?
I tried using easySearch but it is not working as expected.
WordsIndex = new EasySearch.Index({
collection: Words,
fields: ['name', '_id', 'extraOptions' ],
selectorPerField: function (field, searchString) {
console.log("searchString " + searchString);
console.log("field " + field);
if ('extraOptions' === field) {
// return this selector if the email field is being searched
console.log("searchString " + searchString);
console.log("field " + field);
return {
extraOptions: {
$elemMatch: {
name: { '$regex' : '.*' + searchString + '.*', '$options' : 'i' },
content: { '$regex' : '.*' + searchString + '.*', '$options' : 'i' }
}
}
};
}
return this.defaultConfiguration().selectorPerField(field, searchString);
},
engine: new EasySearch.Minimongo()
});
Which way would be the best to search values in this case?
Take a look at this Link. Here says that you should access the variables with the keyword this

Extjs 4.2.1 XTemplate date format for child elements is showing NaN on IE only

Here is the test data:
[{
id: 1,
isActive: true,
documentIdentifier: '00012345',
sourceSiteName: 'Aviation Industry Ltd.',
targetSiteName: 'VendorName',
createDate: '2013-03-06T14:12:03.2341054+02:00',
archiveEvent: 'Rejected',
archive: 'PurchaseOrder',
previousWhatsNewEvents: [{
id: 2,
isActive: true,
documentIdentifier: '00012345',
sourceSiteName: 'Aviation Industry Ltd.',
targetSiteName: 'Vendor Name',
createDate: '2013-03-06T14:12:03.2341054+02:00',
archiveEvent: 'Approved',
archive: 'PurchaseOrder',
isPin: true,
IsDocumentReadByMe: false,
IsDocumentReadByOthers: true,
documentYear: 2013,
businessDirection: 1
}],
isPin:true,
IsDocumentReadByMe:false,
IsDocumentReadByOthers:true,
documentYear:2013,
businessDirection:1
}
Here is the template:
tpl: [
'<div class="n-row-title">',
'<div class="n-doc-status n-doc-status-{archiveEvent:this.toLower} n-float-left"> </div>',
'<span class="n-hmargin-10">{archiveEvent}</span>',
'</div>',
'<div class="n-clear"></div>',
'<div class="n-row-sub-title">{createDate:date("m/d/Y H:i")}</div>',
'<div class="n-whatsnew-previous-events">',
'<tpl for="previousWhatsNewEvents">',
'<div class="n-row-title">',
'<div class="n-doc-status n-doc-status-{archiveEvent:this.toLower} n-float-left"> </div>',
'<span class="n-hmargin-10">{archiveEvent}</span>',
'</div>',
'<div class="n-clear"></div>',
'<div class="n-row-sub-title">{createDate:date("m/d/Y H:i")}</div>',
'</tpl>',
'</div>',
{
toLower: function (value) {
return value.toLowerCase();
}
}
]
Here is how Chrome is rendering the template:
Here is how IE8 is rendering it:
Any one know of a workaround?
UPDATE
Here is my module :
Ext.define('XX.model.WhatsNew', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'isActive', type: 'boolean' },
{ name: 'documentIdentifier', type: 'string' },
{ name: 'sourceSiteName', type: 'string' },
{ name: 'targetSiteName', type: 'string' },
{ name: 'createDate', type: 'date', dateFormat: 'c' },
{ name: 'archiveEvent', type: 'string' },
{ name: 'archive', type: 'string' },
{ name: 'previousWhatsNewEvents' },
{ name: 'isPin', type: 'boolean' },
{ name: 'IsDocumentReadByMe', type: 'boolean' },
{ name: 'IsDocumentReadByOthers', type: 'boolean' },
{ name: 'documentYear', type: 'int' },
{ name: 'businessDirection', type:'int'}
],
hasMany: {
model: 'auxClasses.notifications.WhatsNew',
name: 'previousWhatsNewEvents'
},
proxy: {
type: 'rest',
url: 'api/WhatsNew/'
}
});
The template could not read the date format from the inner previousWhatsNewEvents childs... that made the dates go wrong!!
Your association is overridden by the field with the same name. Even if that was not the case, you association would not load the child nodes from the data because its associationKey is not configured (see the class description for an example of what it does).
Then again, if you got there, the data view component probably don't support associations... Update: That's wrong, see bellow.
So your fast way out of this is to replace the stock date format function with your own, that casts date strings using Ext.Date.format instead of new Date over which you have no control and that may vary between browsers, as pointed out by Evan.
The trick is that you need a date input format to use Ext.Date.format. In your template, you'll know the expected format:
tpl: [
'{createDate:this.date}',
{
date: function(value) {
var readFormat = 'c',
displayFormat = 'm/d/Y H:i',
date = Ext.Date.parse(value, readFormat);
return Ext.Date.format(date, displayFormat);
}
}
]
And you can even think of replacing the Ext.util.Format.date function by adding an argument to it:
Ext.util.Format.date = function(value, outFormat, inFormat) {
var date;
if (value) {
if (value instanceof Date) {
date = value;
} else if (inFormat) {
date = Ext.Date.parse(value, inFormat);
} else {
// backward compatibility
date = new Date(value);
}
return Ext.Date.format(date, outFormat);
} else {
return '';
}
};
Update
After further investigation, it appears that data view do in fact support model associations: the view's prepareData method calls getAssociatedData on the model. So, as pointed by Even again, the cleanest solution is probably to configure your association correctly.
That means removing this field:
fields: [
...
{ name: 'previousWhatsNewEvents' },
...
]
Adding the associationKey option:
hasMany: {
model: 'auxClasses.notifications.WhatsNew',
name: 'previousWhatsNewEvents',
associationKey: 'previousWhatsNewEvents'
}
And finally you have to ensure that your data is loaded through the Reader for the nested records to be read -- simply put, that means through a Proxy. So with the load method of a store, it will work while with any other way it will probably not (for example the loadData method of the store doesn't use the proxy).

Categories