RavenDB Patch API: updating a nested collection - ravendb

I am trying to update a nested collection using the Patch API. More specifically, consider the following example - a Posts collection:
{
"Title": "Hello RavenDB",
"Category": "RavenDB",
"Content": "This is a blog about RavenDB",
"Comments": [
{
"Title": "Unrealistic",
"Content": "This example is unrealistic"
},
{
"Title": "Nice",
"Content": "This example is nice"
}
]
}
I used the Patch API and Set-based operation docs at http://ravendb.net/docs/client-api/partial-document-updates and http://ravendb.net/docs/client-api/set-based-operations as well as several stackoverflow questions as resources to do a bulk update using set operations and a static index. A requirement is to update the "Title" of a comment only when the previous value was "Nice" and if so, update it to "Bad".
The static index "NicePosts" is defined as:
Map = posts => from post in posts
where post.Comments.Any(comment => comment.Title == "Nice")
select new {post.Title, post.Category}
The bulk patch update command is:
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new[] { new PatchRequest
{ Type = PatchCommandType.Modify,
Name = "Comments",
PrevVal = RavenJObject.Parse(#"{ ""Title"": ""Nice""}"),
Nested = new[]
{
new PatchRequest {Type = PatchCommandType.Set, Name = "Title", Value = new RavenJValue("Bad") },
} }, allowStale: true);
I have some questions regarding this:
1) Is my structure/syntax for the update command correct?
2) I would like the update to be performed on all the records in the collection. Hence I haven't defined the query filter in the IndexQuery Query because the "NicePosts" index already returns the appropriate set. However running this command doesn't update the collection.
3) If I set "allowStale:false" I get a "stale index" error. Before opening my document store session I instantiate the index class and Execute it to persist it to the ravenDB instance. Any ideas whats going wrong here?
Thanks,
EDIT:
Based on ayende's recommendation changed Patch command to:
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new[] {
new PatchRequest {
Type = PatchCommandType.Modify,
Name = "Comments",
Position = 0,
Nested = new[] {
new PatchRequest {Type = PatchCommandType.Set, Name = "Title", Value = new RavenJValue("Bad")},
}
}
}, allowStale: false);

This can now be done using the scripted patch request:
string oldTitle = "Nice";
string newTitle = "Bad";
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new ScriptedPatchRequest
{
Script = #"for (var i = 0; i < this.Comments.length; i++)
if (this.Comments[i].Title == oldTitle)
this.Comments[i].Title = newTitle;",
Values =
{
{ "oldTitle", oldTitle },
{ "newTitle", newTitle },
},
}
);

You can't use the patch command to update values based on the existing value in the array.
You need to specify the actual position.

Related

Filter products by category in a Shopware 6 Import/Export Profile

I created a Profile in shopware 6 with the api.
The body of the request is:
{
"id": "my99id2cb2784069938089a8a77ctest",
"label": "Label test",
"name": "name test",
"sourceEntity": "product",
"fileType": "text/csv",
"delimiter": ";",
"enclosure": "\"",
"createdAt": "2022-11-10T10:15:22Z",
"mapping": [{"key":"id","mappedKey":"id","position":0,"id":"someid4f7c6c478b8041cfd5fe0ae0c5"},{"key":"cover.media.url","mappedKey":"cover","position":1,"id":"someid0ffa7f48e6b135f6e754d6b93c"}],
"config": {"createEntities": false, "updateEntities": true}
}
Is there a way to specify that i want only product from certain categories? Not all of them
No, this currently isn't possible with just the profile and existing endpoints. You'll have to implement a custom api endpoint.
If you are able to do that follow the steps from this answer until you first retrieve a logId. Then instead of starting the process using the existing endpoint, request your new custom endpoint. The implementation could look similar to this:
/**
* #Route(defaults={"_routeScope"={"api"}})
*/
class CustomExportApiController extends AbstractController
{
private ImportExportFactory $importExportFactory;
public function __construct(ImportExportFactory $importExportFactory)
{
$this->importExportFactory = $importExportFactory;
}
/**
* #Route("/api/_action/custom/export/{logId}/{categoryId}", name="api.action.custom.export", methods={"POST"})
*/
public function customProductExport(string $logId, string $categoryId, Context $context): JsonResponse
{
$importExport = $this->importExportFactory->create($logId, 50, 50);
$logEntity = $importExport->getLogEntity();
if ($logEntity->getState() === Progress::STATE_ABORTED) {
return new JsonResponse(['success' => false]);
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('categoryTree', [$categoryId]));
$offset = 0;
do {
$progress = $importExport->export($context, $criteria, $offset);
$offset = $progress->getOffset();
} while (!$progress->isFinished());
return new JsonResponse(['success' => true]);
}
}
After calling your custom endpoint proceed with the steps as described in the answer linked above.

How to make component item change owner automatically with its parent?

I'm learning hyperledger composer and I don't know how to modify chaincode to make following model work:
This is my cto:
asset Item identified by itemId{
o String itemId
o String name
o String idgId
o String serialNumber
o String comment
--> BU owner
--> Item [] items optional
}
abstract participant BU identified by buId{
o String buId
o String name
o String country
o String city
}
participant Manufacturer extends BU{
}
participant Assembler extends BU{
}
And chaincode:
function tradeCommodity(trade) {
trade.item.owner = trade.newOwner;
return getAssetRegistry('org.dps.track.Item')
.then(function (assetRegistry) {
var tradeNotification = getFactory().newEvent('org.dps.track', 'TradeNotification');
tradeNotification.item = trade.item;
emit(tradeNotification);
// persist the state of the commodity
return assetRegistry.update(trade.item);
});
}
then i make two items: I1 and I2 - which will be components of third item I3
like this:
{
"$class": "org.dps.track.Item",
"itemId": "I1",
"name": "c1",
"idgId": "123",
"serialNumber": "123",
"comment": "component1",
"owner": "resource:org.dps.track.Assembler#BU2"
},
{
"$class": "org.dps.track.Item",
"itemId": "I2",
"name": "c2",
"idgId": "456",
"serialNumber": "456",
"comment": "component2",
"owner": "resource:org.dps.track.Assembler#BU2"
},
{
"$class": "org.dps.track.Item",
"itemId": "I3",
"name": "complex",
"idgId": "789",
"serialNumber": "789",
"comment": "item consists of items",
"owner": "resource:org.dps.track.Assembler#BU2",
"items": [
"resource:org.dps.track.Item#I1",
"resource:org.dps.track.Item#I2"
]
}
Then if i make a transaction I3 changes owner but its components have still previous owner. How can i make I1 and I2 change owner automatically when I3 changes the owner. Is it possible to achieve this? I will be thankful for any help or guidance.
something like this (note that this is sample code, there may be more efficient ways to do it eg. aggregate all the 'related Items' that are affected by change of ownership, and then do one single updateAll() call to update the Item registry - example here) - also note I used async / await (available in Node 8 as its far more readable).
note my transaction model is:
transaction Trade {
--> Item item
--> Assembler newOwner
}
UPDATED: you ALSO need your asset Item to have --> Assembler owner as the resource class (to build a relationship instance with) and not -->BU owner` which is an abstract class.
in the model file (ie Assembler is a participant resource)
/**
* Sample transaction processor function.
* #param {org.dps.track.Trade } trade The sample transaction instance.
* #transaction
*/
async function tradeCommodity(trade) {
const factory = getFactory();
trade.item.owner = trade.newOwner;
var list = [];
if (trade.item.items && trade.item.items.length > 0) {
trade.item.items.forEach((asset) => {
list.push(asset);
});
}
const assetRegistry = await getAssetRegistry('org.dps.track.Item');
// persist the state of the current ITEM
await assetRegistry.update(trade.item);
for (var i = 0; i < list.length; ++i) {
let res = await assetRegistry.get(list[i].getIdentifier());
res.owner = factory.newRelationship('org.dps.track', 'Assembler', trade.newOwner.getIdentifier());
// persist the state of the ITEM with new owner as a relationship
await assetRegistry.update(res);
}
}

Issue with update query in titanium alloy

I'm new to titanium and alloy framework. I have created the models to store some data. I am able to insert and retrieve the data. I don't know How to update the row.
As suggest I did as follows,
var userLang = Alloy.Collections.userLanguage;
var models = Alloy.createModel("userLanguage");
models.fetch({id: 1});
models.save({
languageID: langID,
languageText: lang
});
The above code s not showing any error, But when I try to select a row from the table,
var userLang = Alloy.createModel('userLanguage');
userLang.fetch({
query: {
statement: "SELECT * FROM userLanguage WHERE id = ?;",
params: [ 1 ]
}
});
alert("Updated value : "+userLang.get("languageText"));
Model
exports.definition = {
config : {
"columns": {
"id": "integer",
"languageID": "integer",
"languageText": "text"
},
"adapter": {
"type": "sql",
"collection_name": "userLanguage"
}
}
};
The selected row is not updated as expected
...I need titanium query...
I guess that you are talking about Backbone. If so then below you can see an example how to update a model.
You can find more informations here: http://docs.appcelerator.com/titanium/latest/#!/guide/Alloy_Sync_Adapters_and_Migrations
var model = Alloy.createModel("userLanguage");
model.fetch({id: 1});
model.save({
languageID: langID,
languageText: lang
});
to update a model you should first get it from the collection
var userLang = Alloy.Collections.userLanguage;
userLang.fetch();
var langModel=userLang.get({
id:1
});
then use set to modify model's properties
langModel.set({
languageText:'new value'
});
then save it
langModel.save();
You should not update the id if you put it as the primary key ...
You should fetch data before getting any model & after updating them that's it.

NetSuite: obtaining invoice balance

I search hi and lo but could not find any decent reference how to get paid and outstanding amounts for the invoice in NetSuite using SuiteTalk API.
Documentation is non-existent and so are the samples so I'm poking in the dark here.
When invoice is retrieved using get method, I kind of expected these to be specified (fields amountPaid and amountRemaining) but they are not.
The next port of call, unfortunately, is to search for customer payments that have been applied to the target invoice. Search works but, to make the matter worse, each payment is returned without any apply details so extra call is required to get all apply details and figure out the total payment amount applied to the invoice.
Is that REALLY the only way to do it?!
(And I probably would have to consider deposits and deposit applications as they are separate record types. Sigh)
Any help is appreciated
George
UPDATE: Working code sample
long internalInvoiceId = 42;
TransactionSearchAdvanced tsa = new TransactionSearchAdvanced()
{
columns = new TransactionSearchRow()
{
basic = new TransactionSearchRowBasic()
{
total = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amount = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amountPaid = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amountRemaining = new SearchColumnDoubleField[] { new SearchColumnDoubleField() }
}
},
criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
mainLine = new SearchBooleanField()
{
searchValue = true,
searchValueSpecified = true
},
type = new SearchEnumMultiSelectField()
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new string[] { "_invoice" }
},
internalIdNumber = new SearchLongField()
{
#operator = SearchLongFieldOperator.equalTo,
operatorSpecified = true,
searchValue = internalInvoiceId,
searchValueSpecified = true
}
}
}
};
SearchResult sr = nss.search(tsa);
Do a transaction search.
Criteria:
Main Line: Yes
Type: Invoice
(Optional) Internal ID: "The internal id of your Invoice"
Result/Columns:
Amount Paid
Amount Remaining
Translate the above to SuiteTalk API (TransactionSearchAdvanced) and you should be able to get what you want.
Here's a function using the NetSuite ruby bindings that retrieves the amount remaining (due) on an invoice:
def amount_due_for_invoice(ns_invoice)
search = NetSuite::Records::Invoice.search(
criteria: {
basic: [
{
field: 'type',
operator: 'anyOf',
value: %w(_invoice)
},
{
field: 'mainLine',
value: true
},
{
field: 'internalIdNumber',
operator: 'equalTo',
value: ns_invoice.internal_id
}
]
},
columns: {
'tranSales:basic' => {
'platformCommon:internalId/' => {},
'platformCommon:amountRemaining' => {}
}
}
)
if search.results.size > 1
fail "invoice search on internalId should never return more than a single result"
end
search.results.first.attributes[:amount_remaining].to_f
end
An alternative solution here is to create a custom transaction body formula field which pulls the value of the invoice's amountremainingtotalbox field.

How to add (remove) new items to an existing dataSource in Dashcode?

I have a question concerning DashCode and dataSources: I have defined an JSON object in javascript file, linked it to a dataSource and connected the company names to the user interface (a 'list' element). The JSON object looks like:
{
items: [
{ company:'A', product:'a1', color:'red' },
{ company:'B', product:'b2', color:'blue' },
{ company:'C', product:'c3', color:'white' }
]
}
How do I programmatically add (or remove) an additional "item" to the existing dataSource? I have used the following approach:
function addElement() {
var newElement = [{company:'D', product:'d4', color:'yellow' }];
var ds = dashcode.getDataSource('list');
ds.arrangedObjects.addObject(newElement);
}
and
function delElement()
{
var ds = dashcode.getDataSource('list');
if (ds.hasSelection()) {
var index = ds.selectionIndex();
ds.arrangedObjects.removeObjectAtIndex(index);
}
}
This piece of code indeed adds (removes) an additional item to the dataSource. However, when I use the list.filterpredicate to search the list for the new item, the new item is ignored.
What is the 'correct' approach to add (or remove) an item to an existing dataSource programmatically?
Your help is being appreciated!
Here is an answer to the question:
1) Define a KVO object in main.js. This step is important to make the object bindable:
anObj = Class.create(DC.KVO, {
constructor: function(company, product, color) {
this.company = company;
this.product = product;
this.color = color;
}
});
2) The updated version of the function 'addElement' is:
function addElement() {
var newElement = new anObj('D', 'd4', 'yellow');
var ds = dashcode.getDataSource('list');
var inventory = ds.valueForKeyPath('content');
inventory.addObject(newElement);
}
3) The updated version of the function 'removeElement' looks similar:
function delElement()
{
var ds = dashcode.getDataSource('list');
if (ds.hasSelection()) {
var index = ds.selectionIndex();
var inventory = ds.valueForKeyPath('content');
inventory.removeObjectAtIndex(index);
}
}
I hope this information helps!