I've been looking at Event Store for a little while now and one thing that stumped me is when should one consider writing a projection? Is it common practice to create a flattened projection?
The setup here is using a stream per aggregate
For example, lets say I have 2 events that look like this:
public class OrderPlaced
{
public OrderPlaced(string reference, Currency currency, Amount amount)
{
...
}
public string Reference { get; }
public Currency Currency { get; } //Custom type
public Amount Amount { get; } //Custom type
}
public class OrderCompleted
{
public OrderCompleted(string reference)
{
this.Reference = reference;
}
public string Reference { get; }
}
I have the following query:
fromCategory('MyCategory')
.whenAny(function(s, e) {
linkTo("Foo", e);
})
The above doesn't do a great deal and only aggregates all the streams into a singular. Is it possible to project a view that is more flat, for example into something like this? Perhaps I got my wires crossed but apparently using emit can achieve this?
{
string Reference;
string CurrencyCode;
decimal PayingAmount;
}
My thinking is, once I have written to the stream I can guarantee the aggregate is in a valid state and thus for any interested parties I should only expose the fields these processes require. Is projecting a simple model (a de-nomarlized view) the correct thing to do..
The above is a trivial example, but you can imagine an aggregate being a little more complicated.
If I have missed anything or further clarification is needed then please comment and I can add.
You are looking for a standard event category projection.
It emits linked events to steam that are called ´$ce-´. The category there is your object type.
For example, your aggregate type is Order, and you write events OrderCreated, OrderLineAdded, etc to streams with names Order-1, Order-2, where 1 and 3 are your aggregate root ids. Then, the $ce-Order stream will contain all events for all aggregates of that type.
Have a look at the standard projections documentation.
Usually this is exactly the way to create read-side projections - by creating a catch-up subscription on category streams and updating the read model accordingly.
In order to run projections, you need to use --run-projections=all –-start-standard-projections=true to see it working.
Related
I'm using Realm in my React Native app.
There is a search bar to filter data by ID, and that ID is int in my database.
If user searches for 418, I need to return all IDs like 41878, 41835.
Here is some code:
findAll({ search }) {
let items = this._realm.objects<Data>(this._schema.name)
if (search.length)
items = items.filtered(`id = '${search}'`)
return items
}
As expected, this code only returns the item that have the exact ID 418.
I tried something like
items.filtered(`id CONTAINS '${search}'`)
But gives me:
Error: Unsupported comparison operator 'contains' against type 'int', right side must be a string or binary type
I can't use Array's filter because it will make the Realm execute the query and slow down the code.
How can I achieve this without triggering the Realm to run the query?
CONTAINS is not supported on Int types.
There are a few solutions:
One is to keep two properties in your model, one an Int and one a String version of that Int. You could implement that pretty easily through an init function within the object that assigns the Int and then makes a string version for the string property
Another is to store all of the values as Strings. You'll just need to Int them if sorting or doing calculations. You can then use CONTAINS
Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm
I'm refactoring some TypeScript and have come across something that has me stuck.
In the code below, how does the "as" keyword convert a Map<number, Trip> into a "Trip" object? If it isn't then what is it doing?
public mergeBookings(bookingData: Immutable.Map<number, Booking>) {
// merge bookings
const bookings: Immutable.Map<number, Booking> = this.mergeBookingData(bookingData);
// sort bookings
const sortedBookings: Immutable.Map<number, Booking> = SortingService.sortBookings(bookings);
// add bookings to trip
const trip: Map<number, Trip> = this.setBookingData(sortedBookings);
return trip as Trip;
}
An explanation on the keyword, how it works for maps and other objects and some documentation on it would be good but any information would help in maintaining this code base.
In particular I wanted to update the function definition to include the return type but need to be confident that it is indeed a "Trip" object. Example desired function definition below:
public mergeBookings(bookingData: Immutable.Map<number, Booking>): Trip {...}
I've looked around StackOverflow answers to this for TypeScript and a long google session for documentation hasn't turned up any help. As per tags this code is using:
TypeScript
Angular
Immutable.js
as is just a cast operator. It doesn't do any conversion. It doesn't change trip in any way whatsoever. It's only purpose is to ask the typescript compiler to trust the programer and not produce an error message.
I'm having issues passing parameters to a view through the node js couchbase module. The main issue is I don't know what question to ask google in order to get the answer I'm looking for. I would like to get from the view a specific object based on a key that I'm receiving from a UI.
For example, I have a list of stores with store numbers 111, 222, and 333. If the user gives me the store number 222, I'd like to only return that one, instead of return it all then filter inside my node js code.
The node js code looks like this:
var query = ViewQuery.from('dev_store', 'store').key(storeNum);
myBucket.query(query, function (err, results) {...};
I got that from the ViewQuery api mixed with this question. However I cannot figure out how to then access that key parameter once I'm in the view in order to filter down my results to just that one store.
I've tested my view and it works fine, so long as I just get a list of all the stores. I've read about reductions but I haven't seen where those actually get written/called.
I've tried accessing it by doing doc.key or adding a key to the view function, but I think my limited understanding of View construction is hurting me here.
Question: Given a key, how do I return from a view only the row that pertains to that key?
EDIT: Here is my view:
function (doc, key, meta) {
doc.midLevel.forEach( function ( reg ) {
reg.midLowerLevel.forEach( function ( dis ) {
dis.lowestLevel.forEach( function ( store ) {
emit( store.store_nbr, null);
})
})
})
}
In this view it emits every store at the lowest level. As seen in the node js code, I've passed a key to it. I would like to use that key in order to create a condition on what gets emitted.
For example, if there are stores numbered 1-100, and the node js passes the number 45, is it possible for me to access the '45' in the view in order to create that condition statement?
Question: Given a key, how do I return from a view only the row that pertains to that key?
On the face of it, this question is not relevant to a view. Views are absolutely not meant to return objects based on the object key. Rather, a view is meant to index certain properties of stored objects such that the objects can be pulled together into a list of some sort.
To return a document based on the key, you would perform a simple get operation. A view query in this scenario actually degrades performance, since the view must be accessed from disk. An object get from a view as opposed to from RAM is an order of magnitude or more slower than a simple read.
Answer: use a get, not a view.
Given the following object
Friend Class GetLocationsResult
Public Property X1 As String
Public Property X2 As String
Public Property X3 As String
Public Property X4 As String
Public Property X5 As String
Public Property X6 As Double
Public Property X7 As Double
End Class
And it is declared and instantiated thusly:
Dim objList as List(of GetLocationsResults) = new List(of GetLocationsResults)
And objList is populated via an iterator that churns through a collection of objects/aggregate classes. The iterator just shoves values into a new GetLocationsResult object and then adds it to the list.
And given the NewtonSoft JSONConvert.SerializeObject(objList) results:
{"d":"[{\"X1\":\"Store Name\",\"X2\":\"Address\",\"X3\":\"City\",\"X4\":\"State\",\"X5\":\"Zip\",\"X6\":Coord1,\"X7\":Coord2}]"}
This point has been addressed and is no longer an issue
There are several issues with this result set. First, for whatever odd
reason, the object being named "d" is not acceptable.
How can I specify something other than "d" for the "name" of the json array?
When I attempt to JSON.parse the response, it needs to be in the following format in order to actually get to the data:
resultSet = JSON.parse(data.d);
console.warn(resultSet[0].X1);
Having to say resultSet[0] is, of course, not acceptable.
How do I cause the 'JSONConvert.Serialize' method to not wrap the response in such a way that I have to refer to the first index of the
resulting JSON data so that I can say resultSet.X1 instead of
resultSet[0].X1?
As requested, here is some more detailed information that may be relevant to the issue at hand.
The data is being returned from a WCF service. The exposed method is decorated thusly:
<WebInvoke(Method:="GET", ResponseFormat:=WebMessageFormat.Json)>
and returns type String. The service is being consumed by a desktop application and a mobile platform. The desktop website can have its own method and, in fact, does because we don't want to deal with X1, X2, etc while the mobile platform devs have declared that as necessary. As noted, the method returns a number of results in the form of a custom, aggregate class which is then shoved into a class object which is only a collection of properties. The return statement is thus:
Return JsonConvert.SerializeObject(retVal)
where retVal is a List(of GetLocationsResult)
So while having to access the data via index may be fine for the website, it is not acceptable for the mobile platform. Because each will run their own methods, it is possible to have a unique solution for both if needed.
Three things.
It sounds like you're returning that back through an ASP.NET ASMX
ScriptService or ASPX [WebMethod]. When using one of those
endpoints, you don't need to (and shouldn't) manually serialize the
object into JSON. ASP.NET will do that for you automatically. You don't need to use JSONConvert at all; just return your object as the result. That's why you're seeing all that escaped data after the .d. It's JSON serialized twice.
As WhiteHat mentioned, the .d is a good thing in some situations. It's introduced by default in these ASP.NET JSON services and can't easily be removed on the server-side, but is easy to account for when you parse the response on the client-side.
You are returning a List(of GetLocationResults), which maps to a JavaScript array. That's why you need to use resultSet[0].X1 to access a value in the result. If you want to access that value via resultSet.X1, you need to be sure to return only a single GetLocationResults object instead of a List of them.
Does that help? If you could update your question with a more complete example of your server-side and client-side code, I could give you an example of how to address the preceding three issues.