CordaRPCOps.vaultTrackBy ignores QueryCriteria - corda

Corda 4.6
Problem:
CordaRPCOps.vaultTrackBy() ignores the QueryCriteria passed into the method and instead returns all consumed/produced states of type contractStateType
Method & Description
Returns a snapshot (as per queryBy) and an observable of future
updates to the vault for the given query criteria.
vaultTrackBy(
QueryCriteria criteria,
PageSpecification paging,
Sort sorting,
java.lang.Class<? extends T> contractStateType
)
Example:
If I have 3 LinearState's:
myLinearState1
myLinearState2
myLinearState3
But I only want the Observable to report on myLinearState1:
QueryCriteria queryCriteria = new QueryCriteria.LinearStateQueryCriteria(
null,
singletonList(myLinearState1.getLinearId().getId())
);
DataFeed<Vault.Page<MyLinearState>, Vault.Update<MyLinearState>> dataFeed = cordaRPCOps.vaultTrackBy(
queryCriteria,
pageSpecification,
sorting,
MyLinearState.class
);
If I then proceed to make changes to myLinearState2 the Observable will report these changes. My understanding is it shouldn’t. Am I doing something wrong?
Note: the states returned in the snapshot (Vault.Page) will properly match the QueryCriteria
This problem was raised here and supposedly fixed.

Related

How to query a property bag entity type in EFCore5?

In EFCore5, implicit tables are saved as Dictionary<TKey, object> sets, knows as Property Bag Entity Types. However, I cannot figure out how to create a LINQ query with a Where() clause that compiles for MySQL for such a property bag entity type.
For example, this code successfully retrieves the IQueryable reference to an intermediate table (generated by EFcore5's implicity many-to-many feature) given the ISkipNavigation:
ISkipNavigation nav = // some many-to-many relationship
IQueryable<Dictionary<string, object>> intermediateTable =
context.Set<Dictionary<string, object>>(nav.JoinEntityType.Name);
And this code successfully retrieves all the entries in the intermediate table:
List<Dictionary<string, object>> joins = await intermediateTable.ToListAsync();
In the resulting List, each Dictionary has just one key/value (representing the row).
However, I cannot figure out how to add a .Where() clause to the LINQ query which will compile:
joinTable.Where(d => d.Keys.First() == "foo").ToList();
joinTable.Where(d => d.Keys.Any(k => k == "foo")).ToList();
The error is:
Translation of member 'Keys' on entity type 'MachinePartMachineProfile (Dictionary<string, object>)' failed. This commonly occurs when the specified member is unmapped. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'
I do not wish to do client-side parsing for performance reasons (the join table will be to big).
I note that the type reported by the error is MachinePartMachineProfile (Dictionary<string, object>). Some investigation showed that these types are being generated based upon the static Model.DefaultPropertyBagType (which is a Dictionary<string, object>). But despite staring at the EFCore code base, I cannot discern how to correctly query such a default property bag type.
I am using MySQL as my database, if it is relevant.
You can index the dictionary directly, with knowledge of the column name.
Working example would be:
joinTable.Where(d => d[columnName] == "foo").ToList();
And for the sake of completeness, if you have an ISkipNavigation instance, you can infer these keys as follows:
string foreignKey = nav.ForeignKey.Properties.First().GetColumnBaseName();
string localKey = nav.Inverse.ForeignKey.Properties.First().GetColumnBaseName();

Vaultquerybycriteria ignores general criteria with statestatus if custom criteria included

I have function from my RPC client that pulls the consumed history of a state. If I pass a custom criteria, it seem to ignore the general criteria with Vault StateStatus.
My general query looks like this:
QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.CONSUMED);
My custom criteria like this:
customCriteria = generalCriteria;
if (requestJSON.containsKey("linearId")) {
String linearId = requestJSON.get("linearId").toString();
FieldInfo fieldInfo = getField("linearId", POSchemaV1.POSchemaPersistence.class);
criteriaExpression = Builder.equal(fieldInfo, linearId);
customCriteria = new QueryCriteria.VaultCustomQueryCriteria(criteriaExpression);
customCriteria = generalCriteria.and(customCriteria);
}
The vault query looks as below:
Vault.Page<PurchaseOrder> orderPage = proxy.vaultQueryByCriteria(customCriteria, PurchaseOrder.class);
List<StateAndRef<PurchaseOrder>> orderStateAndRefList = orderPage.getStates();
The result seem to have unconsumed states if I pass linearId, if I ignore linearId then I get all consumed states correctly. pls let me know if I am missing something.
There is a more simple (out of the box) approach to query linear states:
QueryCriteria queryCriteria = new QueryCriteria.LinearStateQueryCriteria()
.withStatus(Vault.StateStatus.CONSUMED)
.withUuid(Collections.singletonList(linearId));
What if you don't have the linearId at hand; but have other custom attributes. It just returns UNCOSUMED states. If it is a linearState you may have to query twice, once to get UNCONSUMED state and then use that linearID to get the CONSUMED states.
How will it work if you have states that are consumed and you want to query for certain customCriteria and don't have the linearID? I don't want to query all the all states ( there can me lots of them ) and look for specific ones that match from all records that were retrieved. I assumed, VaultCustomQueryCriteria was supposed to do that.

Using VaultQuery with Sorting won't work if no other field condition

I'm using vaultQueryBy that query all Unconsumed state and sorting with some field of that state but it not working (result is completely random sorting).
Then I use the same vaultQueryBy but add some equal condition, then result is perfectly sorting.
I tried with other field also (type String and long) but it not work also, Unless using with other field condition.
Here some code
QueryCriteria queryCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
Sort.SortColumn sortCol = new Sort.SortColumn(new SortAttribute.Custom(CustomerSchema.CustomerEntity.class, "changeDate"), Sort.Direction.DESC);
List list = new ArrayList();
list.add(sortCol);
List<StateAndRef<CustomerState>> list = rpcOps().vaultQueryBy(queryCriteria, pageSpecification, new Sort(sortList), CustomerState.class).getStates();
And it's not working then I add this condition before vaultQueryBy
QueryCriteria baseCriteria = new QueryCriteria.VaultCustomQueryCriteria(Builder.notNull(getField("linearId", CustomerSchemaV01.CustomerEntity.class)));
queryCriteria = queryCriteria.and(baseCriteria);
And it' work fine get result with correct sorting.
I'm not quite sure that it's Corda's intention or just some bug, but I think it should be able to order without using any field condition.
This is probably expected behaviour since the sorting criteria is only applicable to custom queries, and hence you need to provide a custom query for it to be added to the overall query.

Possible ways to create custom queries in Corda

We have a queryablestate for storing some information when the system is initialized, these states are issued once and never consumed
class DataState implements LinearState, QueryableState {
Party partyA;
Party partyB;
String partyAId;
String partyBId;
String sharedIdentifierNumber;
DataState(Party partyA, Party partyB, String partyAId, String partyBId, String sharedIdentifierNumber) {
this.partyA = partyA;
this.partyB = partyB;
this.partyAId = partyAId;
this.partyBId = partyBId;
this.sharedIdentifierNumber = sharedIdentifierNumber;
}
}
partyA and partyAId must be related to entity A (same for partyB)
some example instances:
new DataState(party1, party2, "00001", "12345", "0001")
new DataState(party3, party1, "05432", "00001", "0022")
new DataState(party2, party1, "12345", "00001", "0123")
we want to have methods that work like a map:
String retrievePartyId(Party party){}
assertTrue(retrievePartyId(party1).equals("00001"))
Party retrieveParty(String partyId){}
assertTrue(retrieveParty("12345").equals(party2))
we have already done this by querying all the states with custom field criteria and comparing through iteration on the
List<StateAndRef<DataState>>
We would like to know if there is some efficient way of doing this, maybe with some custom querycriteria in Corda. We think this is related to sql column projections. The query interface returns list of 'states(.getStates())' OR aggregation results '(.getOtherResults())'. We were wondering if it’s possible (or planned) to get a single column from the db and then filter the duplicates through the vault interface, currently we’re doing that in java.
If your states are QueryableStates, they can be efficiently queried based on their schema's attributes.
For example, suppose in the CorDapp Example (https://github.com/corda/cordapp-example), I wanted to query the vault for IOUStates based on their value and whether I am the lender. I could do this as follows:
try {
Field value = IOUSchemaV1.PersistentIOU.class.getDeclaredField("value");
Field lender = IOUSchemaV1.PersistentIOU.class.getDeclaredField("lender");
QueryCriteria valueOver50Criteria = new QueryCriteria.VaultCustomQueryCriteria(greaterThan(value, 50));
QueryCriteria lenderIsMeCriteria = new QueryCriteria.VaultCustomQueryCriteria(equal(lender, getOurIdentity().getName().toString()));
QueryCriteria criteria = valueOver50Criteria.and(lenderIsMeCriteria);
Vault.Page<IOUState> results = getServiceHub().getVaultService().queryBy(IOUState.class, criteria);
List<StateAndRef<IOUState>> matchingStates = results.getStates();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
In your case, you would be querying based on the party's ID or the party's name. You would then take the first result and use it to map the party's name to the party's ID, or vice-versa.

Firestore: how to perform a query with inequality / not equals

I want select from Firestore collection just articles written NOT by me.
Is it really so hard?
Every article has field "owner_uid".
Thats it: I JUST want to write equivalent to "select * from articles where uid<>request.auth.uid"
TL;DR: solution found already: usages for languages/platforms: https://firebase.google.com/docs/firestore/query-data/queries#kotlin+ktx_5
EDIT Sep 18 2020
The Firebase release notes suggest there are now not-in and != queries. (Proper documentation is now available.)
not-in finds documents where a specified field’s value is not in a specified array.
!= finds documents where a specified field's value does not equal the specified value.
Neither query operator will match documents where the specified field is not present. Be sure the see the documentation for the syntax for your language.
ORIGINAL ANSWER
Firestore doesn't provide inequality checks. According to the documentation:
The where() method takes three parameters: a field to filter on, a comparison operation, and a value. The comparison can be <, <=, ==, >, or >=.
Inequality operations don't scale like other operations that use an index. Firestore indexes are good for range queries. With this type of index, for an inequality query, the backend would still have to scan every document in the collection in order to come up with results, and that's extremely bad for performance when the number of documents grows large.
If you need to filter your results to remove particular items, you can still do that locally.
You also have the option of using multiple queries to exclude a distinct value. Something like this, if you want everything except 12. Query for value < 12, then query for value > 12, then merge the results in the client.
For android it should be easy implement with Task Api.
Newbie example:
FirebaseFirestore db = FirebaseFirestore.getInstance();
Query lessQuery = db.collection("users").whereLessThan("uid", currentUid);
Query greaterQuery = db.collection("users").whereGreaterThan("uid", currentUid);
Task lessQuery Task = firstQuery.get();
Task greaterQuery = secondQuery.get();
Task combinedTask = Tasks.whenAllSuccess(lessQuery , greaterQuery)
.addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//This is the list of "users" collection without user with currentUid
}
});
Also, with this you can combine any set of queries.
For web there is rxfire
This is an example of how I solved the problem in JavaScript:
let articlesToDisplay = await db
.collection('articles')
.get()
.then((snapshot) => {
let notMyArticles = snapshot.docs.filter( (article) =>
article.data().owner_uid !== request.auth.uid
)
return notMyArticles
})
It fetches all documents and uses Array.prototype.filter() to filter out the ones you don't want. This can be run server-side or client-side.
Updating the answer of Darren G, which caused "TypeError: Converting circular structure to JSON". When we perform the filter operation, the whole firebase object was added back to the array instead of just the data. We can solve this by chaining the filter method with the map method.
let articles = []
let articlesRefs = await db.collection('articles').get();
articles = articlesRefs.docs
.filter((article) => article.data.uid !== request.auth.uid) //Get Filtered Docs
.map((article) => article.data()); //Process Docs to Data
return articles
FYI: This is an expensive operation because you will fetching all the articles from database and then filtering them locallly.
Track all user id in a single document (or two)
filter unwanted id out
Use "where in"
var mylistofidwherenotme = // code to fetch the single document where you tracked all user id, then filter yourself out
database.collection("articles").where("blogId", "in", mylistofidwherenotme)
let query = docRef.where('role','>',user_role).where('role','<',user_role).get()
This is not functioning as the "not equal" operation in firestore with string values
You can filter the array of objects within the javascript code.
var data=[Object,Object,Object] // this is your object array
var newArray = data.filter(function(el) {
return el.gender != 'Male';
});

Resources