How to permanently unset an attribute of ArangoDB document? - arangodb

I want to remove an attribute from a document in ArangoDB.
I thought the correct method for this was with the function UNSET(doc, attributeName1, ..., attributeNameN). However, with this alone, nothing is changed in the database.
Example:
let target_key = "42"
FOR doc IN myCollection
FILTER doc._key == target_key
RETURN UNSET(doc, "myAttribute")
The example returns the original document without the attribute myAttribute, but the new version is not saved to the database, so it seems this is only a projected copy.

Alternatively, you can set the attribute you want to remove to null and use the keepNull option:
LET target_key = "42"
FOR doc IN myCollection
FILTER doc._key == target_key
UPDATE doc WITH { myAttribute: null } IN myCollection
OPTIONS { keepNull: false }
RETURN NEW
Attributes with an explicit null value in the document are kept. Only the attributes specified after WITH are touched.
Note: if you explain the query, you will see nullMeansRemove: true under Write query options instead of keepNull: false.

The simple answer to this is to combine UNSET with the REPLACE function.
UNSET works on attributes in a single document, and REPLACE works on entire documents in a collection.
let target_key = "42"
FOR doc IN myCollection
FILTER doc._key == target_key
REPLACE UNSET(doc, "myAttribute") IN myCollection
RETURN NEW
This example returns the new document where myAttribute is removed with UNSET that is saved to the collection with REPLACE.
The NEW and OLD keywords can be used as with UPDATE and UPSERT.
I figured this out after I read this issue. I felt this was a too simple task to get stuck on, but I hope this is of use to people after me.

Related

How to query field exist some document in firebase

I using firebase, nodejs and i have a question how to query field exist some document.
Example :
Collections : users => document : A with field {
....
is_correct: true
}
document : B with field {
.....
}
In my above example , i have two document in collection users. On document A i have field is_correct: true and on document B field is_correct not exist.
My collection users about 15.000 document and it have 50 document contain field is_correct: true
When i write code look like :
await firestore.collection('users').where('is_correct', '==', true).get();
it can get correct me 50 document. But i don't understand it can using index on field is_correct or not ? And it can query best performance ? I understand firebase can't get document if field undefined. It impart in case ? Please help ? Thanks you
For a simple query like
firestore.collection('users').where('is_correct', '==', true)
you don't need to configure any index, Firestore does it automatically.
As you mentioned, only documents where the given field exists can match the query.
And this is the case also for Not-equal (!=) and not-in queries: they exclude documents where the given field does not exist, as explained in the documentation.
Also, note that a field exists when it's set to any value, including an empty string (""), null, and NaN (not a number).
So, in conclusion, if you want to query for documents where is_correct is not true, you need to create this field in these documents with a value different than true.

Firestore delete ALL fields but ONE

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.

How to convert a Hit into a Document with elasticsearch-dsl?

Consider the following mapping for a document in ES.
class MyDoc(elasticseach_dsl.Document):
id_info = Object(IdInfo)
class IdInfo(elasticseach_dsl.InnerDoc):
id = Keyword()
type = Keyword()
Using elasticsearch-dsl, there are 2 ways of retrieving a document (that I am interested in):
Using MyDoc.search().query().execute(), that yields Hit objects
Using MyDoc.get(), that yields a MyDoc object
Here is the issue I am experiencing:
When I retrieve the same document from ES, and that document is missing, for example, the type field, I get different behaviours:
When using search(): doc being a Hit object, accessing doc.type raises a KeyError
When using get(): doc being a MyDoc object, accessing doc.type simply returns None
To workaround this discrepancy, I would like to convert a Hit instance to a MyDoc instance, so that I can always use the doc.type syntax without any errors being raised.
How can I do that?
Alternatively, is there a way that I could access Hit instances with the same behaviour as MyDoc instances?
dict_hit = hit.to_dict()
doc = YourDocument(**dict_hit)
doc.property1 # you can access the property here
I know it is a bit awkward and annoying, it used to work with versions below 6.
I found a workaround, if you take the dictionary coming out from elasticsearch response you can then ask the document class to interpret it like the following.
query = MyDoc.search()
response = query.execute()
my_doc = MyDoc.from_es(response.hits.hits[0])
We were facing this situation. In our case, is was due to the index name in the Index subclass to configure Document indices. Our model looked more or les like this:
class MyDoc(Document):
my_field = Keyword()
class Index:
name = "my-doc-v1-*"
This way, when querying for documents in indexes that match that name (for example "my-doc-v1-2022-07"), hits are automatically instantianted as MyDoc objects.
Now we have started to generate 'v2' indices, named like "my-doc-v2--000001", and then hits were not being populated as MyDoc objects.
For that to happen, we had to change Index.name to my-doc-*. That way, documents from both 'v1' and 'v2' indices are always populated automatically by the library, since they match the Index.name expression.

Can't modify/remove a field from an ActivityNode using sbt

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"

insert and return document in a single AQL query

When I insert a document into a collection with AQL it returns an empty list.
arangosh [test]> db._query('INSERT #document INTO vertices', {document: {name: "bar"}}).toArray()
[ ]
Is there a way to insert a document and get the complete document back in a single AQL query?
What I am hoping to get back is:
{
"_id": "vertices/641272433780",
"_key": "641272433780",
"_rev": "641272433780",
"name": "bar"
}
This is possible since ArangoDB 2.4. To return the created document, the original query
INSERT #document INTO vertices
has to be changed into
INSERT #document INTO vertices LET result = NEW RETURN result
This will also work for multi-document INSERTs and also for UPDATE/REPLACE and REMOVE. The following quotes from the 2.4 documentation describe the syntax:
To return documents from a data-modification query, the INSERT, REMOVE, UPDATE or REPLACE statement must be immediately followed by a LET statement that assigns either the pseudo-value NEW or OLD to a user-defined variable. The LET statement must be followed by a RETURN statement that returns the variable introduced by LET.
NEW refers to the inserted or modified document revision, and OLD refers to the document revision before update or removal. INSERT statements can only refer to the NEW pseudo-value, and REMOVE operations only to OLD. UPDATE and REPLACE can refer to either.
Once the feature is implemented, its going to look like that:
INSERT expression IN|INTO collection [ OPTIONS expression ] WITH NEW INTO variable RETURN variable;
REMOVE expression IN|INTO collection [ OPTIONS expression ] WITH OLD INTO variable RETURN variable;
UPDATE expression IN|INTO collection [ OPTIONS expression ] WITH OLD|NEW INTO variable RETURN variable
UPDATE expression WITH expression IN|INTO collection [ OPTIONS expression ] WITH OLD|NEW INTO variable RETURN variable
`

Resources