Is it possible to delete All fields in a Firestore document apart from ONE field in a SINGLE database write (Without pior reading it)
I know I have a document with some proprieties but I don't know all of them. I want to delete all of these properties except one that I know.
The one that I know is keep .
{
keep: 'keep',
remove1: 'remove',
remove2: 'remove',
remove3: 'remove',
}
The doc after the transaction should be:
{
keep: 'keep',
}
I could have used firebase.firestore.FieldValue.delete() on each of the keys t
If you know the name and the value of the field you want to keep, you can just overwrite the document with an object that only contains the property you know:
const keepValue = ...;
db.collection('mycollection').doc('mydoc').
set(
{ keep: keepValue }
);
Since we use the set() method without the merge option, all the fields in the document will be overwritten with the object passed to the set() method.
If you don't know the value (or the name) of the field you want to keep, you will need to read the document, in order to find this value or name.
Related
I'm trying to set some new fields in a nested dict within a Firestore document, which results in the data being overwritten.
Here's where I write the first part of the info I need:
upd = {
"idOffer": {
<offerId> : {
"ref" : <ref>,
"value" : <value>
}
}
}
<documentRef>.update(upd)
So output here is something like:
<documentid>:{idOffer:{<offerId>:{ref:<ref>, value:<value>}}}
Then I use this code to add some fields to the current <offerId> nested data:
approval = {
"isApproved" : <bool>,
"dateApproved" : <date>,
"fullApproval" : <bool>
}
<documentRef>.update({
"idOffer.<offerId>" : approval
})
From which I expect to get:
<documentid>:{idOffer:{<offerId>:{ref:<ref>, value:<value>, isApproved:<bool>,dateApproved:<date>,fullApproval:<bool>}}}
But I end up with:
<documentid>:{idOffer:{<offerId>:{isApproved:<bool>,dateApproved:<date>,fullApproval:<bool>}}}
Note: I use <> to refer to dynamic data, like document Ids or References.
When you call update with a dictionary (or map, or object, or whatever key/value pair structure used in other languages), the entire set of data behind the given top-level keys are going to be replaced. So, if you call update with a key of idOffer.<offerId>, then everything under that key is going to be replaced, while every other child key of the idOffer level will remain unchanged.
If you don't want to replace the entire object behind the key, then be more specific about which children you'd like to update. In your example, instead of updating a single idOffer.<offerId> key, specify three keys for the nested children:
idOffer.<offerId>.isApproved
idOffer.<offerId>.dateApproved
idOffer.<offerId>.fullApproval
That is to say, the dictionary you pass should have three keyed entries like this at the top level, rather than a single key of idOffer.<offerId>.
I have some data i want to insert via insertGraph
ala ModelName
.query(trx)
.insertGraph(data)
problem is I have a guard with allowInsert that specifies which columns should be populated. I have a column holding a foreign key to another table. I don't want this column to be populated. I keep getting trying to upsert an unallowed relation. I'm at a loss on how to specify that foreignId shouldn't be populated.
My code looks like this with the allowInsert guard
ModelName
.query(trx)
.allowInsert([subrelation2.[columnToPopulate1, columnToPopulate2]])
.insertGraph(data)
P.s. I've tried specifying foreignId in the allowInsert condition to no avail. Specifying relation2.* allows the insertion. But I want to retain the sanity checks
Seems like you are specifying columns of the relation2 model, instead of subrelations. Is columnToPopulate1 a subrelation of the Model of relation2? By the name looks like a column of the model, which is wrong.
I think that you want to use a relations to insert two columns in the 'relation2' model. Something like:
let data = {
modelNameColumn: 'value',
relation2: {
columnToPopulate1: 'value',
columnToPopulate2: 'value',
}
}
await ModelName
.query(trx)
.allowInsert('[relation2]')
.insertGraph(data)
In allowInsert method you can only specify which relations are allowed to be inserted, but cant define which columns.
In case you want to remove the posibility of a column to be updated, you can use a beforeUpdate() trigger:
class Model2 extends Model {
async $beforeUpdate(opt, queryContext) {
await super.$beforeUpdate(opt, queryContext);
if (this.columnName) throw new Error('columnName shouldnt be updated')
}
}
i am using the Graph library in order to save my data correctly.
I was wondering, if there's a way to update an existing Entity, without duplicate the entity 2 times: user will be allowed to update just two values of the entity.
Also, i would like to know if there's a proper save method
I give you an example
let person = Entity(type: "Person")
person["name"] = //not editable
person["work"] = //editable
person["age"] = //editable
graph.sync() //or something like graph.update
Writing this, i am just creating a new entity, which is not what i want. Maybe, i have to search for the entity, delete that every time, and insert the new one?Hope not.
Thank you for any help you could give!
yes, you can update Entities.
You need to search for the one you want to update first. For example:
let graph = Graph()
let search = Search<Entity>(graph: graph).for(types: "Person")
for entity in search.sync() {
// do something
}
Once you have the entity you want to update, you can set any property, group, or tag as per usual.
entity["name"] = "Daniel"
From there you need to call graph.sync() or graph.async() so that all is saved.
Thats it :)
I created an ActivityNode (an Entry) and I can add custom fields with the
setFields(List<Field> newListField)
fonction.
BUT
I am unable to modify these fields. (In this case I try to modify the value of the field named LIBENTITE)
FieldList list = myEntry.getTextFields();
List<Field> updatedList = new ArrayList<Field>();
//I add each old field in the new list, but I modify the field LIBENTITE
for(Field myField : list){
if(myField.getName().equals("LIBENTITE")){
((TextField)myField).setTextSummary("New value");
}
updatedList.add(myField);
}
myEntry.setFields(updatedList);
activityService.updateActivityNode(myEntry);
This code should replace the old list of fields with the new one, but I can't see any change in the custom field LIBENTITE of myEntry in IBM connections.
So I tried to create a new list of fields, not modifying my field but adding a new one :
for(Field myField:list){
if(!myField.getName().equals("LIBENTITE")){
updatedList.add(myField);
}
}
Field newTextField = new TextField("New Value");
newTextField .setFieldName("LIBENTITE");
updatedList.add(newTextField );
And this code is just adding the new field in myEntry. What I see is that the other custom fields did not change and I have now two custom fields named LIBENTITE, one with the old value and the second with the new value, in myEntry.
So I though that maybe if I clear the old list of Fields, and then I add the new one, it would work.
I tried the two fonctions
myEntry.clearFieldsMap();
and
myEntry.remove("LIBENTITE");
but none of them seems to work, I still can't remove a custom field from myEntry using SBT.
Any suggestions ?
I have two suggestions, as I had (or have) similar problems:
If you want to update an existing text field in an activity node, you have to call node.setField(fld) to update the field in the node object.
Code snippet from my working application, where I'm updating a text field containing a (computed) start time:
ActivityNode node = activityService.getActivityNode(id);
node.setTitle(formatTitle()); // add/update start and end time in title
boolean startFound = false;
// ...
FieldList textfields =node.getTextFields();
Iterator<Field> iterFields = textfields.iterator();
while (iterFields.hasNext()) {
TextField fld = (TextField) iterFields.next();
if (fld.getName().equals(Constants.FIELDNAME_STARTTIME)) {
fld.setTextSummary(this.getStartTimeString()); // NOTE: .setFieldValue does *not* work
node.setField(fld); // write updated field back. This seems to be the only way updating fields works
startFound=true;
}
}
If there is no field with that name, I create a new one (that's the reason I'm using the startFound boolean variable).
I think that the node.setField(fld) should do the trick. If not, there might be a way to sidestep the problem:
You have access to the underlying DOM object which was parsed in. You can use this to tweak the DOM object, which finally will be written back to Connections.
I had to use this as there seems to be another nasty bug in the SBT SDK: If you read in a text field which has no value, and write it back, an error will be thrown. Looks like the DOM object misses some required nodes, so you have to create them yourself to avoid the error.
Some code to demonstrate this:
// ....
} else if (null == fld.getTextSummary()) { // a text field without any contents. Which is BAD!
// there is a bug in the SBT API: if we read a field which has no value
// and try to write the node back (even without touching the field) a NullPointerException
// will be thrown. It seems that there is no value node set for the field. We
// can't set a value with fld.setTextSummary(), the error will still be thrown.
// therefore we have to remove the field, and - optionally - we set a defined "empty" value
// to avoid the problem.
// node.remove(fld.getName()); // remove the field -- this does *not* work! At least not for empty fields
// so we have to do it the hard way: we delete the node of the field in the cached dom structure
String fieldName = fld.getName();
DeferredElementNSImpl fldData = (DeferredElementNSImpl) fld.getDataHandler().getData();
fldData.getParentNode().removeChild(fldData); // remove the field from the cached dom structure, therefore delete it
// and create it again, but with a substitute value
Field newEmptyField = new TextField (Constants.FIELD_TEXTFIELD_EMPTY_VALUE); // create a field with a placeholder value
newEmptyField.setFieldName(fieldName);
node.setField(newEmptyField);
}
Hope that helps.
Just so that post does not stay unanswered I write the answer that was in a comment of the initial question :
"currently, there is no solution to this issue, the TextFields are read-only map. we have the issue recorded on github.com/OpenNTF/SocialSDK/issues/1657"
I have tried to insert many records into a table, and this table has a unique constraint, so when if one user try to add a new record with the same unique value, I get the dbUpdateException.
But I would like to know how to skipt this error and try to add the remaining records that the first user are trying to add to the table.
How can do that?
Thanks.
One approach could be to catch the DbUpdateException, and use its Entries property to remove the duplicate entities from the context.
You could then retry the save - rinse and repeat - and eventually all the non-duplicate entities will be saved.
E.g.
var duplicates = new List<MyEntity>();
...
catch(DbUpdateException ex)
{
ex.Entries.Each(e => DbContext.Entry(e).State = EntityState.Detached;
duplicates.Add(ex.Entries);
ReTrySave(); // do whatever you need todo to re-enter your saving code
}
...
// Report to user the duplicate entities
ReportToUser(duplicates);
NOTE - treat as pseudo code as I haven't attempted to compile this snippet.