Skip to content

Conversation

@changev
Copy link
Member

@changev changev commented May 26, 2017

  1. Modfied findMongo, findAndModifyMongo, findOneMongo to return docs in normal format.
  2. Add updateAndFindMongo to return modified doc(s) after update.
  3. Add
addFieldIfNotExistById,
addFieldIfNotExistByIdentifier, 
addListItemsIfNotExistById,
addListItemsIfNotExistByIdentifier, 

For support some data consistency needs.

@changev changev force-pushed the bugfix/enclosure-update-data-consistency branch 2 times, most recently from 0824a77 to 1604615 Compare May 26, 2017 12:02

return Promise.resolve(cursor.toArray()).then(function(docs){
return _.map(docs, self.deSerialize);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it only be "return _.map(cursor.toArray, self.deSerialize)"? I assume "then" will always return a promise, and if toArray() returns an empty array, _.map() will return as it is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Lyne, and also if Promise required, return Promise.map(cursor.toArray, self.deSerialize) should be more clean.

Copy link
Member Author

@changev changev Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cursor.toArray() may be null
Promise.map(null, ....) will be rejected, but here when find nothing we just want to return [].
And also cursor.toArray() only can be executed only once and then cursor will be exhausted.
So also can't use

if(cursor.toArray()){
    Promise.map(cursor.toArray, self.deSerialize) 
}

return self.runNativeMongo('findAndModify', _arguments);
return self.runNativeMongo('findAndModify', _arguments).then(function(doc){
return doc ? self.deSerialize(doc) : doc;
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it be possible to change doc null validation to deSerialize()? that would save the efforts every time when calling it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!Excellent idea, thanks!

if (!update.$set) {
update.$set = {};
}
//this.addWaterlineUpdatedAtToObject(update, '$set');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this line unnecessary here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sry, I forgot why I add // before this line.

}
// If filter point to multiple doc and multi !== true
// Update will be set on a random node.
return self.findOneMongo(filter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will findOne() and update({options:{multi:false}}) operate on the same random doc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They truly have a chance to operate on the same random doc.
So are you recommended here to throw error or some thing else to prevent possible mistake?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any use case that user would only update a random doc? If not, I would prefer to remove this option for the upper layer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean if filter points to multi docs and mult !== true, just throw error?

*/
addListItemsIfNotExistById: function (id, values, signs) {
var self = this;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lodash has already been required using DI at the beginning of this file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sry this is just for local debug. I'll remove it

var transformedValues = require("lodash")
.transform(values, function(result, array, key) {
var withSign = {};
if (signs && signs.length !== 0){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above


var update = { $set: {} };
update.$set[field] = values;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is "IfNotExist" necessary here? Can this function do the following things: 1) if the field doesn't exist, add it; 2) if it is already in the doc, return the original doc? In this case the callers don't need to check the field's existence by themselves in the upper layer.

Copy link
Member Author

@changev changev Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Using this function the callers don't need to check the field's existence by themselves in the upper layer. If it doesn't exist, the function will create the field with vaule. If it already exist, the function will return nothing. This function can make sure the field indeed exist after it is called.

  2. "IfNotExist" must be contained in mongo query, otherwise the data consistency can be promised.

  3. About the two following things above: if the field doesn't exist, add it how can we judge it exist or not? FindbyID and then check the obj is not atomic. See 2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean could we just remove "IfNotExist" in the function name for a shorter one, similar as https://github.com/RackHD/on-core/blob/master/lib/common/model.js#L549 to keep it consistent

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If user want to add {fieldA: valueA} to a doc but fieldA already exists, then the vaule of fieldA won't be changed to valueA.

Without "IfNotExist", the method looks more likely to change the value to valueA.

* from a list of this document, the values provided specifies the list and items.
* The signs is an object list, each sign related to one or more object items in [value].
* The sign means primer key of object for determining if the object exist in list already.
* Returns a waterline (bluebird) promise with the document find and modify
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please elaborate more on this description? or using a simple example? It is a little difficult to understand.


return Promise.resolve(cursor.toArray()).then(function(docs){
return _.map(docs, self.deSerialize);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Lyne, and also if Promise required, return Promise.map(cursor.toArray, self.deSerialize) should be more clean.

}
return cursor.toArray();

return Promise.resolve(cursor.toArray()).then(function(docs){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a style suggestion, change a new line before .then and all the other same lines below.

return Promise.fromNode(self.validate.bind(this, update))
.then(function() {
return self.runNativeMongo('findAndModify', _arguments);
return self.runNativeMongo('findAndModify', _arguments).then(function(doc){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, you can move all the then() to be after above then() not inside it.

if (!update.$set) {
update.$set = {};
}
//this.addWaterlineUpdatedAtToObject(update, '$set');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question, I expect to use this $addToSet in Warnado story.

return self.findMongo(filter);
}
// If filter point to multiple doc and multi !== true
// Update will be set on a random node.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update on a random node sounds dangerous, you should confirm if it is harmless, otherwise you should forbid this random operation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope to discuss this issue with you if necessary

* @param {Object} values {field: [value]}
* @param {Object} signs [{sign}]
* @returns {Promise}
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method looks too complicated, why just use $addToSet or $push?

*
* @param {string} identifier
* @param {Object} values {field: [value]}
* @param {Object} signs [{sign}]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param {Array} signs

* Returns a waterline (bluebird) promise with the document find and modify
*
* @param {string} identifier
* @param {Object} values {field: [value]}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param {Array} signs

* Using an Id, find document and add one or more items
* from a list of this document, the values provided specifies the list and items.
* The signs is an object list, each sign related to one or more object items in [value].
* The sign means primer key of object for determining if the object exist in list already.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found and modified

* Returns a waterline (bluebird) promise with the document find and modify
*
* @param {string} identifier
* @param {Object} values {field: [value]}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without example, I can't understand how sign works and what it looks like. If we heavily depends on sign, suggest to create a new function dedicated for it.

@changev changev force-pushed the bugfix/enclosure-update-data-consistency branch 9 times, most recently from 040463a to bac49f3 Compare June 7, 2017 10:24
Copy link
Member

@iceiilin iceiilin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complicated work here. Some personal thoughts. No hurry to adjust the code, I will need more time to think about addListItemsIfNotExistById.

* we don't want to add another obj of type A,
* how can we know the item of type: A is already exist? the para sign help to do this,
* set sign = [{type:A}] and it can prevent to add another obj of type A.
* Instead you can use value = {list[0].value: 2} to add value 2 into type A obj.
Copy link
Member

@iceiilin iceiilin Jun 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the following format might be more readable for users than literal description. Actually the example in your test case is a great example.
"suppose before modifying, the document is:

{
    ...,
    list: [
        {a:1}
    ],
    ...
}

If users would like to add "item1", {a:1, b:2} and {c:3} as items in the "list",
, the function's parameter "value (field: [item])" can be set as

value = {list: ['item1', {a:1, b:2}, {c:3}]}

If you just want to modify the first item in the list, "list.0" can be used as field.
To prevent from recreating the item {a:1, b:2} if {a:1} is already in the original field, "signs" can be

signs = [{a:1}]

in this case, the document will remain the same as the original one without adding anything.
"
you could modify the whole example to be more general like stated in the original description, but please make sure it is clear and concrete enough for users.

});


describe('by Id with addListItemsIfNotExistById()', function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to modify the description as a complete sentence for easier understanding

Copy link
Member Author

@changev changev Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the text of nested descript will be joined together in the unit test log.
So this describe + parent describe is a complete sentence.

Otherwise the sentence in log will be duplicated


describe('remove list items by bad identifier with removeListItemsByIdentifier()',
describe('by bad value with addListItemsIfNotExistById()',
function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line can be moved one line above


var update = { $set: {} };
update.$set[field] = values;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean could we just remove "IfNotExist" in the function name for a shorter one, similar as https://github.com/RackHD/on-core/blob/master/lib/common/model.js#L549 to keep it consistent

return self.updateAndFindMongo(query, update, options);
}).then(function(modifiedDocs){
return _.last(modifiedDocs);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A general question, if no modification to the document is made after a function in this file, is there a common return value, I mean, a promise of [] or the original document?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the findAndModifyMongo is executed, though no modification the original doc will be return.

If query can't find any doc , a Promise.resolve() will be return.

If parameters is invalid, a Promise.reject() will be return.

$addToSet: transformedValue
};
return self.findAndModifyMongo(query, {}, update, options);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using findAndModify and update separately here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query in "add list items of value type" doesn't need to know the information of current list.
So it's no ploblem without doc lock.

But "add list items of obj type" need to check the sign in exists list items, it must know the information of current list.

So a doc lock must be applied when query, findAndModifyMongo can helo us to achieve this.

if (transformedValues.vaulesWithSign){
transformedValues = transformedValues.vaulesWithSign
.concat(transformedValues.vaulesWithoutSign);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more readable if you could help to elaborate some comments above, even the sample value of these local variables will help future coders to make some code adjustment.

@changev changev force-pushed the bugfix/enclosure-update-data-consistency branch 2 times, most recently from c52e62f to bb32b50 Compare June 14, 2017 10:48
@RackHD RackHD deleted a comment from changev Jun 15, 2017
@changev changev force-pushed the bugfix/enclosure-update-data-consistency branch 4 times, most recently from 642f4ad to f39e32e Compare June 15, 2017 15:43
* the values provided specifies the list and items, suppose relation is a obj list,
* targets is a list in obj item of relations, then
* values = {"relations.0.targets", ["some string", {obj}, 123]}
* The signs is an object list, each sign related to one or more object items in [value].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above description is a little duplicate with examples below.

signs = [{a:1}]
* in this case,
* the document will remain the same as the original one without adding anything.
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls. help to remove extra empty line or space in this paragraph.

@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from houndci-bot Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@RackHD RackHD deleted a comment from changev Jun 20, 2017
@changev changev force-pushed the bugfix/enclosure-update-data-consistency branch from f39e32e to 6c28ec6 Compare June 20, 2017 06:04
}
return cursor.toArray();

return Promise.resolve(cursor.toArray());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return cursor.toArray() equals return Promise.resolve but shorter.

@iceiilin iceiilin merged commit 77328d1 into RackHD:master Jun 21, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants