illegal document key in ArangoDB graph - arangodb

I am starting in ArangoDB, through an access in a Java application. In the project, I need to create a collection of employees and a dynamic number of vertices of stockings, with connections between employees and these stockings through edges, as shown in the figure.
In the codes below I am creating only one stocking (just use a loop to create the others). The problem is how to create the edge.
I can already create the Capacity and Employee vertices. These collections must be connected through the edge Vacation, but I am not able to create the edges.
Initially, I used Solution1, based on this code. With this code, the three collections are created as a vertex, but the edges are not created interconnecting the Employees and Stockings.
Adapting the code for Solution 2. based on the following script, “Error: 1221 - illegal document key” was generated when executing createGraph() (see marked line).
I ask the experts for help in identifying how to create the graph correctly.
Solution1
ArangoDB arangoUtil = new ArangoDB.Builder()
.serializer(new ArangoJack())
.host(ARANGODB_HOST, ARANGODB_PORT)
.user(ARANGODB_USER)
.password(ARANGODB_PASSWORD)
.useProtocol(Protocol.HTTP_JSON)
.build();
registerStockings();
//each employee has more than one vacation record per year
for(Vacation v : listVacations){
registerEmployees(v);
registerVacations(v);
}
private registerStockings() throws ArangoDBException{
BaseDocument bDoc;
for(Stocking s : list){
bDoc = new BaseDocument();
bDoc.setKey(s.getUorg());
bDoc.addAttribute("nomeLotacao", s.getNome());
bDoc.addAttribute("descricao", s.getDescricao());
arangoUtil.db(ARANGODB_DATABASE)
.collection(“Stock”)
.insertDocument(bDoc);
}
}
private void registerEmployees(Vacation v) throws ArangoDBException{
BaseDocument bDoc = new BaseDocument();
bDoc = new BaseDocument();
bDoc.setKey(v.getSiape());
bDoc.addAttribute("nomeServidor", v.getNomeServidor());
arangoUtil.db(ARANGODB_DATABASE)
.collection("Employees")
.insertDocument(bDoc);
}
private void registerVacations(Vacation v) throws ArangoDBException{
BaseEdgeDocument bDoc = createEdgeValue();
bDoc.setKey(sb.append(v.getSiape()).append("_").append(v.getDataInicio().getTime()).toString());
bDoc.addAttribute("exercicio", v.getExercicio());
bDoc.addAttribute("duracao", v.getDuracao());
bDoc.addAttribute("dataInicio", v.getDataInicio());
bDoc.addAttribute("dataFim", v.getDataFim());
bDoc.setFrom(“Employees”);
bDoc.setTo(“Stockings”);
arangoUtil.db(ARANGODB_DATABASE)
.graph(“MyGraph”)
.edgeCollection(“Vacations”)
.insertEdge(bDoc);
}
private static BaseEdgeDocument createEdgeValue(){
VertexEntity v1 = arangoUtil.db(ARANGODB_DATABASE)
.graph(“MyGraph”)
.vertexCollection(“Employees”)
.insertVertex(new BaseDocument(), null);
VertexEntity v2 = arangoUtil.db(ARANGODB_DATABASE)
.graph(“MyGraph”)
.vertexCollection(“Stockings”)
.insertVertex(new BaseDocument(), null);
BaseEdgeDocument bed = new BaseEdgeDocument();
bed.setFrom(v1.getId());
bed.setTo(v2.getId());
return bed;
}
Solution2
ArangoDB arangoUtil = new ArangoDB.Builder()
.serializer(new ArangoJack())
.host(ARANGODB_HOST, ARANGODB_PORT)
.user(ARANGODB_USER)
.password(ARANGODB_PASSWORD)
.useProtocol(Protocol.HTTP_JSON)
.build();
createCollections();
registerStockings();
//each employee has more than one vacation record per year
for(Vacation v : listVacations){
registerEmployees(v);
registerVacations(v);
}
private void createCollections() throws ArangoDBException{
arangoUtil.db(ARANGODB_DATABASE).createCollection(“Employees”,
new CollectionCreateOptions().type(CollectionType.DOCUMENT));
arangoUtil.db(ARANGODB_DATABASE).createCollection(“Stockings”,
new CollectionCreateOptions().type(CollectionType.DOCUMENT));
arangoUtil.db(ARANGODB_DATABASE).createCollection(“Vacations”,
new CollectionCreateOptions().type(CollectionType.EDGES));
List<EdgeDefinition> listEdgeDef = new ArrayList<EdgeDefinition>();
EdgeDefinition edgeDef = new EdgeDefinition();
edgeDef.collection(“Vacations”);
edgeDef.from(“Employees”);
edgeDef.to(“Stockings”);
listEdgeDef.add(edgeDef);
arangoUtil.db(ARANGODB_DATABASE).createGraph(“MyGraph”, listEdgeDef, null); **** error ****
}

Related

How to update Cost for a stock item

Is possible to call Update Cost process, in the stock item maintenance screen? In my case, my custom code creates BOM records, then executes BOM cost roll. Last step is to execute Update Cost, in order to push the pending cost data. I notice the Update Cost process is actually a method in the base graph, not the stock item maintenance graph. I am unsure how to execute the action button in this case.
Because the Update Cost action on Stock Items takes an additional parameter we need to figure out how to access an action with the following:
public PXAction<InventoryItem> action;
[PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
[PXButton(SpecialType = PXSpecialButtonType.ActionsFolder)]
protected virtual IEnumerable Action(PXAdapter adapter,
[PXInt]
[PXIntList(new int[] { 1, 2, 3 }, new string[]
{
"Update Price",
"Update Cost",
"View Restriction Group"
})]
int? actionID
)
{
// button action code here...
}
Looking at examples in Acumatica I was able to find out how to provide parameters to a PXAction that takes more than just the adapter. In our case we need to provide actionID a value.
To get this working we need to work with a new PXAdapter instance and pass this to the action. Here is a working sample:
var itemMaint = CreateInstance<InventoryItemMaint>();
var inventoryID = 151;
itemMaint.Item.Current = itemMaint.Item.Search<InventoryItem.inventoryID>(inventoryID);
// Must use this dummy view - actual view will not work
var view = PXView.Dummy.For<InventoryItem>(itemMaint);
var itemAdapter = new PXAdapter(view)
{
Arguments = new Dictionary<string, object> { { "actionID", 2 } }
};
itemMaint.action.PressButton(itemAdapter);
Putting it all together which is what I used to test the example we end up with something like this...
Coll BOM Cost (cost roll)
Update pending cost (cost roll)
Update Cost (stock item)
Working Example:
PXLongOperation.StartOperation(this, () =>
{
var costRoll = CreateInstance<BOMCostRoll>();
costRoll.Settings.Current.SnglMlti = RollupSettings.SelectOptSM.Multi;
costRoll.Settings.Current.BOMID = "00000000000041";
costRoll.Settings.Current.RevisionID = "A";
// ROLL COSTS
costRoll.start.Press();
var inventoryIds = new HashSet<int>();
foreach (AMBomCost costRollRec in costRoll.BomCostRecs.Select())
{
inventoryIds.Add(costRollRec.InventoryID.GetValueOrDefault());
}
// UPDATE PENDING
costRoll.updpnd.Press();
var itemMaint = CreateInstance<InventoryItemMaint>();
foreach (var inventoryId in inventoryIds)
{
itemMaint.Clear();
itemMaint.Item.Current = itemMaint.Item.Search<InventoryItem.inventoryID>(inventoryId);
if (itemMaint.Item.Current == null)
{
continue;
}
// Must use this dummy view - actual view will not work
var view = PXView.Dummy.For<InventoryItem>(itemMaint);
var itemAdapter = new PXAdapter(view)
{
Arguments = new Dictionary<string, object> {{"actionID", 2}}
};
// UPDATE COST
itemMaint.action.PressButton(itemAdapter);
}
});

Call action within another graph, how to pass the adapter

My goal is, from a given screen :
- Add lines to the Adjustments tab of the payment & application graph
- Release
I tried to do this :
override public void createLettering(List<ARRegister> lines)
{
string refNbr = "";
foreach (ARRegister line in lines)
{
if (line.DocType == "PMT") refNbr = line.RefNbr;
}
// Get the paymententry graph, and add the invoice
ARPaymentEntry graphPmt = PXGraph.CreateInstance<ARPaymentEntry>();
ARPayment pmt = PXSelect<ARPayment, Where<ARPayment.refNbr, Equal<Required<ARPayment.refNbr>>,
And<ARPayment.docType, Equal<Required<ARPayment.docType>>>>>
.Select(this,refNbr, "PMT");
graphPmt.Document.Current = pmt;
if (pmt == null) throw new PXException(Constantes.errNotFound);
//pmt.CuryOrigDocAmt = 0m;
//graphPmt.Document.Update(pmt);
ARAdjust adj = new ARAdjust();
foreach(ARRegister line in lines)
{
if (line.DocType == "INV")
{
adj = new ARAdjust();
adj.AdjdDocType = line.DocType;
adj.AdjdRefNbr = line.RefNbr;
graphPmt.Adjustments.Insert(adj);
}
}
PXAdapter adapter = new PXAdapter(new PXView(graphPmt,true, graphPmt.Document.View.BqlSelect));
graphPmt.Persist();
graphPmt.Release(adapter);
}
My problem is I think my adapter gets every single ARPayment in it and thus tries to release them all. (The output of this function is : long processing time and then tells me 'PaymentMethod can't be null', but the paymentMethod of my graphPmt.Document is not null when I check in debug).
so How do I pass a correct PXAdapter to the Release(PXAdapter adapter) method of the PaymentEntry graph, from another custom graph of mine ?
I would think you should be able to call the action such as...
graphPmt.release.Press();
I have not tested this but I recall doing something like this for other actions in the past.

Draw states in mapControl

I'm writing an application for windows Phone and I'm using a MapControl.
I'd like to be able to paint the US States in different colors.
For example, CA in Red, NV in blue, etc
I Thought about doing Shapes and Polilines, but I can't find the coordinates to use in the shapes to get the different States.
I also tried using the
var found = await MapLocationFinder.FindLocationsAsync("California", new Geopoint(new BasicGeoposition()));
but it doesn't work for finding States.
The best way is to download GeoJSON files from this public repository
https://github.com/johan/world.geo.json/tree/master/countries/USA
Parse the JSON and Create MapPolygon object and add it to map.
public async void RenderState() {
HttpClient client = new HttpClient();
HttpResponseMessage response=await client.GetAsync(new Uri("https://raw.githubusercontent.com/johan/world.geo.json/master/countries/USA/CO.geo.json"));
string json=response.Content.ToString();
JObject obj = JObject.Parse(json);
JObject poly = (JObject)obj["features"][0]["geometry"];
JArray coords = (JArray)poly["coordinates"][0];
MapPolygon polygon = new MapPolygon();
List<BasicGeoposition> points = new List<BasicGeoposition>();
foreach (JArray arr in coords) {
points.Add(new BasicGeoposition() { Latitude = (double)arr[1], Longitude = (double)arr[0] });
}
//Remove last point as it is a duplicate
if (points.Count > 1) {
points.RemoveAt(points.Count - 1);
}
polygon.Path = new Geopath(points);
polygon.StrokeColor = Colors.Red;
polygon.FillColor = Colors.Blue;
this.mMap.MapElements.Add(polygon);
}
This code will render the state of colarado

QueryExpression with no results in Dynamics CRM plugin

I wrote the following function to get the SharePointDocumentLocation records regarding an account or contact. However, even though I provide an id which most definitely has got a SPDL record associated the result of a count on the EntityCollection that is returned is alway 0. Why does my query not return SPDL records?
internal static EntityCollection GetSPDocumentLocation(IOrganizationService service, Guid id)
{
SharePointDocumentLocation spd = new SharePointDocumentLocation();
QueryExpression query = new QueryExpression
{
EntityName = "sharepointdocumentlocation",
ColumnSet = new ColumnSet("sharepointdocumentlocationid"),
Criteria = new FilterExpression
{
Conditions =
{
new ConditionExpression
{
AttributeName = "regardingobjectid",
Operator = ConditionOperator.Equal,
Values = { id }
}
}
}
};
return service.RetrieveMultiple(query);
}
The following code does work
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using System.ServiceModel.Description;
using System.Net;
using Microsoft.Xrm.Sdk.Query;
namespace CRMConsoleTests
{
class Program
{
static void Main(string[] args)
{
ClientCredentials credentials = new ClientCredentials();
credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
Uri orgUri = new Uri("http://localhost/CRMDEV2/XRMServices/2011/Organization.svc");
Uri homeRealmUri = null;
using (OrganizationServiceProxy service = new OrganizationServiceProxy(orgUri, homeRealmUri, credentials, null))
{
//ConditionExpression ce = new ConditionExpression("regardingobjectid", ConditionOperator.Equal, new Guid(""));
QueryExpression qe = new QueryExpression("sharepointdocumentlocation");
qe.ColumnSet = new ColumnSet(new String[] { "sharepointdocumentlocationid", "regardingobjectid" });
//qe.Criteria.AddCondition(ce);
EntityCollection result = service.RetrieveMultiple(qe);
foreach (Entity entity in result.Entities)
{
Console.WriteLine("Results for the first record: ");
SharePointDocumentLocation spd = entity.ToEntity<SharePointDocumentLocation>();
if (spd.RegardingObjectId != null)
{
Console.WriteLine("Id: " + spd.SharePointDocumentLocationId.ToString() + " with RoId: " + spd.RegardingObjectId.Id.ToString());
}
}
Console.ReadLine();
}
}
}
}
It retrieves 4 records, and when I debug the plugincode above it retrieves 3 records.
Everything looks good with your QueryExpression, although I'd write it a little more concise (something like this):
var qe = new QueryExpression(SharePointDocumentLocation.EntityLogicalName){
ColmnSet = new ColumnSet("sharepointdocumentlocationid"),
};
qe.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, id);
Because I don't see anything wrong with the QueryExpression that leads me with two guesses.
You're using impersonation on the IOrganizationService and the impersonated user doesn't have rights to the SharePointDocumentLocation. You won't get an error, you just won't get any records returned.
The id you're passing in is incorrect.
I'd remove the Criteria and see how many records you get back. If you don't get all of the records back, you know your issue is with guess #1.
If you get all records, add the regardingobjectid to the ColumnSet and retrieve the first record without any Criteria in the QueryExpression, then call this method passing in the id of the regardingobject you returned. If nothing is received when adding the regardingobjectid constraint, then something else is wrong.
Update
Since this is executing within the delete of the plugin, it must be performing its cascade deletes before your plugin is firing. You can try the Pre-Validation.
Now that I think of it, it must perform the deletion of the cascading entities in the Validation stage, because if one of them is unable to be deleted, the entity itself can't be deleted.

While implementing IEnumerable<T>, pointer misdirection is observed

This is an interesting error I've come across while implementing IEnumerable on a class. It appears to be similar to an "access to modified closure" issue, but I'm at a loss as to how to fix it.
Here is a simple example that demonstrates the issue:
void Main()
{
var nodeCollection = new NodeCollection();
nodeCollection.MyItems = new List<string>() { "a", "b", "c" };
foreach (var node in nodeCollection)
{
node.Dump();
}
}
public class NodeCollection : IEnumerable<Node>
{
public List<string> MyItems;
public IEnumerator<Node> GetEnumerator()
{
// This isn't necessary, but it should prove that it's not an "access to modified closure" issue.
var items = MyItems;
for (var i = 0; i < 3; i++)
{
var node = new Node();
// I want the node to contains the items in MyItems.
node.Items = items;
// Plus an additional item. Note that I am adding the item to the node, NOT to MyItems.
node.Items.Add(string.Format("iteration: {0}", i));
yield return node;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class Node
{
public List<string> Items;
}
As you can see from the Dump() statement, I'm running this in LINQPad, but the issue will present itself in any IDE.
When I run the snippet, I get the following output:
Because I am adding the item to Items in the newly instantiated Node, I would NOT expect the item to be added to MyItems, but this is obviously what is occurring.
It seems that Items in Node is pointing to MyItems in NodeCollection.
Can anyone tell me:
Why this is happening?
How to make it not happen?
You are creating new nodes each iteration, but then setting the same items instance to the Items property of each node. Then you are adding the iteration string to the items instance stored in the Items collection (which is always the same instance), resulting in each subsequent node having more and more "iteration" entries. If you kept all of the nodes, you'd find that all of them have exactly the same Items value.
I think the basic misunderstanding here was that you were assuming that setting the Items property of the Node (node.Items = items;) would copy the items list into the node. In fact, all it does is set node.Items to point to the already-existing list that you call items.
This should give you an idea where you went wrong:
// This same instance of items is being reused each time
var items = MyItems;
for (var i = 0; i < 3; i++)
{
var node = new Node();
// I want the node to contains the items in MyItems.
// Assuming node.Items is a List<String>
node.Items = new List<String>();
node.Items.AddRange(items);
node.Items.Add(string.Format("iteration: {0}", i));
yield return node;
}
node.Items = items; sets node.Items to be a reference to the items list. There is just one list, with several references to it.
I suppose that what you want is to have a separate list in each node and that you want to copy the elements in items into that list. To do that, create a new list wich contains all of the elements from items.
node.Items = new List<string>(items);
When you do:
var item = MyItems;
you just create a reference to MyItems and store it in the variable item. Then when you do:
node.Items = items;
you just take the same reference and store it in node.Items. If you need node.Items to be a new list (point to a different memory location) initialize it again.
node.Items = new List();

Resources