I am using the BatchInserter instance to get and set properties for nodes.
My data have multiple values in some properties.
property value
======== =========
synonym animal
synonym mammalian
I want to put this values, in the same property in the same node.
I have used the following code snippet to read and set values:
String[] values = {"animal", "mammalian"};
for (int i = 0; i < values.length(); i++) {
Map<String, Object> nodeProps = db.getNodeProperties(0); // Node 0 properties
nodeProps.put("synonym", values[i]);
db.setNodeProperties(0, nodeProps);
}
In the first iteration property synonym gets value animal. In the second iteration, the property is overridden by the value mammalian.
My question is: How can I get the previous value(s), add the new one and set the property to the node so I can get synonym=['animal', 'mammalian'] in graph?
If you want to have multiple values on a node/relationship property you need to use arrays. So you have on property synonyms with a String[] as value:
To amend an existing property:
String[] values = {"animal", "mammalian"};
Map<String, Object> nodeProps = db.getNodeProperties(0);
String[] existingValues = nodeProps.get("synonym");
// using org.apache.commons.lang.ArrayUtils from Apache Commons Lang:
String[] amendedValues = ArrayUtils.addAll(existingValues, values);
nodeProps.put("synonym", amendedValues);
db.setNodeProperties(0, nodeProps);
Related
I am attempting to retrieve the path of the immediate parent of a JToken object found via SelectToken.
grandparent
parent
object
In above structure the value of object.Path is "grandparent.parent.object" and the value of object.Parent.Path is also "grandparent.parent.object".
Is this a bug or should the path of a parent be retrieved in another way?
Below is an example that illustrates object.Path and object.Parent.Path being the same:
var input = "{'grandparent': { 'parent' : {'object' : 'value'}}}";
var jsonInput = JObject.Parse(input);
var jsonObject = jsonInput.SelectToken("..object");
var path = jsonObject.Path; //grandparent.parent.object
var parentPath = jsonObject.Parent.Path; //grandparent.parent.object (same as object)
var realParentPath = jsonObject.Parent.Parent.Path; //grandparent.parent (actual parent path)
You have stumbled on an implementation detail of Json.NET, which is that it models a JSON object with two levels of container, namely the JObject which contains a collection of JProperty items, each of which in turn contains the actual property value:
JObject // A JSON object: an unordered set of name/value pairs
-> IEnumerable<JProperty> Properties()
JProperty // A property name/value pair
-> string Name // The property name
-> JToken Value // The property value
I.e., using the diagram for an object from https://json.org/:
The JObject corresponds to the entire section between braces, and the JProperty corresponds to a specific string : value portion.
I reckon this implementation was chosen to separate the name from the value, so that JValue could be used for both array and object primitive values, without having to add in a meaningless Name property for array items. However, from the point of view of SelectToken, the existence of JProperty is a bit awkward because it doesn't correspond to anything selectable via a JSONPath query since SelectToken always returns the actual value rather than the container property. Newtonsoft chose to make JProperty.Path the same as it's value's path; possibly they could have chosen to make JProperty.Path throw an exception instead, but they did not.
To hide this implementation detail, you could introduce an extension method SelectableParent():
public static partial class JsonExtensions
{
public static JToken SelectableParent(this JToken token)
{
if (token == null)
return null;
var parent = token.Parent;
if (parent is JProperty)
parent = parent.Parent;
return parent;
}
}
Then use it as follows:
var path = jsonObject.Path; //grandparent.parent.object
var parentPath = jsonObject.SelectableParent().Path; //grandparent.parent
Demo fiddle here.
Related: Why does AddAfterSelf return 'JProperty cannot have multiple values' when used with SelectToken?.
The following practical example helped my understanding of the differences between JValue and it's JProperty parent.
var input = "{'grandparent': { 'parent' : {'object' : 'value', 'object2': 'value2'}}}";
var jsonInput = JObject.Parse(input);
var jsonObject = jsonInput.SelectToken("..object");
//value
var jsonParentObject = jsonObject.Parent;
//"object": "value"
var jsonParentParentObject = jsonObject.Parent.Parent;
//{
//"object": "value",
//"object2": "value2"
//}
var jsonParentParentParentObject = jsonObject.Parent.Parent.Parent;
//"parent": {
// "object": "value",
// "object2": "value2"
//}
I have two classes
class Phone extends Observable
{
#observable String type = '';
#observable String provider = '';
#observable String num = '';
Map<String, Map<String, String> map = {};
Phone() {}
Phone.build({ this.type,
this.provider,
this.num });
}
I have attempted to use the field values as the key in map like so
Phone phone = new Phone();
phone.map[phone.type] = {'type':'cell', 'provider':'Verizon', 'num':'1234567'};
but it does not work. How can I make the fields value the key for the map?
just remove the quotes
phone.map[phone.type] = {type:'cell', provider:'Verizon', num:'1234567'};
Because you are using strings in your example this may not apply but be aware that if you use instances of custom types as Map key...
The keys of a `HashMap` must have consistent [Object.operator==]
and [Object.hashCode] implementations. This means that the `==` operator
must define a stable equivalence relation on the keys (reflexive,
anti-symmetric, transitive, and consistent over time), and that `hashCode`
must be the same for objects that are considered equal by `==`.
Is there a generic way to retrieve PropertyInfo based on a string value alone, when deeper than one level.
I assume this is probably simple enough, but my search results are only as good as my search criteria, and I think I am having an issue articulating the proper keywords to get search results for what I am after.
I would like to be able to do something like the following (which works perfect if the key is for a direct property / one level - ie key = 'firstName'):
public static PropertyInfo (this HtmlHelper htmlHelper, string key) {
PropertyInfo pInfo = htmlHelper.ViewData.Model.GetType().GetProperty(key);
return pInfo;
}
But is there a way for me to return the PropertyInfo based on a string alone
when Key equals something more complex, such as nested classes, objects, lists, etc...:
key = "somelist[0].myproperty"
key = "Items[0].someotherlist[1].someproperty" (where Items is defined as List<Item> Items {get; set;}, someotherlist is defined similarly)
Can the method be generic enough to essentially drill down as many levels as needed (defined)?
So here is what I came up with... this is about to get wordy, and mostly 'stream of thought'
I have custom HtmlHelperExtension, and within it :
PropertyInfo[] pInfoArray = htmlHelper.ViewData.Model.GetType().GetProperties();
PropertyInfo pInfo = GetPropertyInfo(pInfoArray, key);
This GetPropertyInfo() method takes the key, and the PropertyInfo array, cycles through the properties, until the keypart (using regex to remove any indication of an array from the string, so I am left with only the property) matches the property name. On Match, determine if this is the first cycle in the loop, and if so assign the matched property to my Temp Type and PropertyInfo variables. If keyParts are remaining to loop through, subsequent loops now use previously set temp variables and the for loop index [i] to iterate / drill down the class structure. Each time setting the pInfoTemp variable, and then pTypeTemp so the next loop can use where it left off.
private static PropertyInfo GetPropertyInfo(PropertyInfo[] pInfoArray, string key)
{
PropertyInfo pInfo = null;
string[] keyParts = key.Split('.');
Regex arrayRgx = new Regex("\\[\\d*\\]");
PropertyInfo pInfoTemp = null;
Type pTypeTemp = null;
foreach (PropertyInfo prop in pInfoArray)
{
string keyPartsTrimmed = arrayRgx.Replace(keyParts[0], ""); // removes '[#]' from string
if (keyPartsTrimmed == prop.Name) // match property name
{
for (int i = 0; i < keyParts.Count(); i++)
{
if (i == 0) // initial item [0]
{
pTypeTemp = prop.PropertyType; // gets [0]'s type
pInfoTemp = prop; // assigns [0]'s property info
}
else
{
pInfoTemp = GetNestedPropertyInfo(pTypeTemp, arrayRgx.Replace(keyParts[i], "")); // gets [i]'s property info for return or next iteration
pTypeTemp = pInfoTemp.PropertyType; // gets [i]'s type for next iteration
}
}
pInfo = pInfoTemp;
break;
}
}
return pInfo;
}
This next method is invoked by the previous for grabbing nested property info, more importantly for detecting whether the passedItemType is a List (without this, it fails to work correctly as it is unable to find the property asked for in a List<> Type. I need to know what the List item Type is.
private static PropertyInfo GetNestedPropertyInfo(Type passedItemType, string passedProperty)
{
PropertyInfo pInfoOut = null;
if (passedItemType.IsGenericType && passedItemType.GetGenericTypeDefinition() == typeof(List<>))
{
Type itemType = passedItemType.GetGenericArguments()[0];
pInfoOut = itemType.GetProperty(passedProperty);
}
else
{
pInfoOut = passedItemType.GetProperty(passedProperty);
}
return pInfoOut;
}
This currently suits my requirements as they are today, and I have tested it with the following properties, lists, subclasses, subclasses with lists, etc.. to 4 levels deep, but should function properly no matter the depth:
firstName
lastName
Items[1].sprocket
subClass.subClassInt
subClass.mySubClassObj.sprocketObj
subClass.ItemsInMySubClass[1].sprocket
subClass.ItemsInMySubClass[0].mySubClassObj.widgetObj
subClass.ItemsInMySubClass[2].mySubClassObj.sprocketObj
If anyone has a better solution, or see any potential issues with what I have, I welcome the feedback.
The best way in your case is to make a parser that split that expression.
Based on this question I created a Groovy class that will have dynamic properties.
class MyDynamic {
def propertyMissing( String name, value ) {
this.metaClass."$name" = value
value
}
}
So far all good, now I can set some non-existent property with success
MyDynamic dyna = new MyDynamic()
dyna.someProp = new Date()
My problem begins when I have another instance with the same name of property, but with another type
MyDynamic dyna2 = new MyDynamic()
dyna2.someProp = "0" //GroovyCastException: Cannot cast object '0' with class 'java.lang.String' to class 'java.util.Date'
Actually I need this because I'm creating objects with the result of a query without knowing the table and the column. I get the name of the column with the ResultSetMetaData and add the property to the instance of the dynamic object. Later I will use this object to export all the properties and values. In different tables I have the same column name, but with different types.
So my question is: how can I reset this metaClass when I'm done with the instance to not conflict with other instance?
Why not a Expando, a Map or a simple container:
class Dynamic {
def properties = [:]
void setProperty( String name, value ) {
properties[name] = value
}
def getProperty(String property) { properties[property] }
}
d = new Dynamic()
d.name = "yeah"
assert d.name.class == String
d.name = new Date()
assert d.name.class == Date
I have a object with a lot of properties on it. A bunch of these large object are to be inserted in the db but with only one property on them changing. The property that will be changing is not the primary key. First time SaveChanges succeeds but the subsequent ones fail with "An object with the same key already exists in the ObjectStateManager.....". Here is the flow in code:
//create the entity and set the properties that don't change
TheLargeObject obj = new TheLargeObject();
obj.Prop1 =
obj.Prop2 =
...
obj.Prop20 =
//create a list of values that differ between each entity
List<int> validIds = new List<int>();
private static void SaveToDatabase(TheLargeObject obj, List<int> validIds)
{
foreach (int id in validIds)
{
//this is the only property that changes
obj.KeyId = id;
//make a copy - do we really need this?
TheLargeObject newobj = new TheLargeObject();
newobj = obj;
using(Entities objContext = new Entities())
{
objContext.TheLargeObjects.AddObject(newobj); //ERROR: An object with the same key already exists in the ObjectStateManager.
objContext.SaveChanges();
}
}
}
I'm just starting out with EF4, so I'm probably going about this in the wrong way.
Thanks
I'm not sure what your trying to do here. Mainly this statement confuses me:
A bunch of these large object are to be inserted in the db but with only one property on them changing.
How can a new object (ie inserted) be changing? If it's new, what is there to change?
All of the entities in your model have an EntityKey, which is usually the primary key on the database-side.
What i think your doing is doing .AddObject, when you should be doing .Attach.
Here's how you INSERT a new object:
var newFoo = new Foo();
ctx.Foos.AddObject(newFoo);
newFoo.SaveChanges();
Here's how you UPDATE an existing object:
var existingFoo = ctx.Foos.SingleOrDefault(x => x.Id == 1); // or however you want to get it
existingFoo.Name = "Changed foo";
newFoo.SaveChanges();
Or if you have a detached entity:
var existingFoo = new Foo();
existingFoo.Name = "Foo name";
ctx.Foos.Attach(existingFoo);
ctx.SaveChanges();
So, i think in your example, your code should simply be:
objContext.TheLargeObjects.Attach(newObject);
objContext.SaveChanges();
In a nutshell, if you have an entity which you are positive already exists in the database, and you have constructed the entity manually, use .Attach. If you have a brand spanking new object, use .AddObject.
However, to be safe - you could go and get the object again, make the changes you need, then do .SaveChanges().
.Attach is usually used in stateless scenarios such as web sites, where objects are not kept in the graph between requests, therefore to make changes we need to .Attach, or retrieve the object again before making the changes.
Hopefully this clears it up for you.