In my hyperledger composer app I want to write a named query that returns all persons with a certain hobby.
The model for "Person" is the following:
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
--> Hobby[] hobbies optional
}
The model for "Hobby" is the following:
asset Hobby identified by name {
o String name
}
The named query has the following structure:
query selectPersonsByHobby {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE //don't know what to put here//
}
I don't know what to put after the "WHERE" operator in order to achieve what I want.
I want something like the following:
query selectPersonsByHobby {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies.contains(_$hobby))
}
Is this even possible ?
The short answer is that this query should work:
query selectConsultantsBySkill {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies CONTAINS _$targetHobby)
}
But note that because your hobbies is an array of Relationships the targetHobby parameter will have to be something like resource:org.acme.Hobby#cycling . In a production scenario you would be 'calling' the query from a UI program so you could pre-pend the relationship syntax.
I guess this is just a test example, but I wonder if Hobby needs to be a relationship? It would be easier if not.
You could alternatively use a concept (and even an enumerated type within the concept!). Here is an example of a modified model and query with a concept:
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
o Interest[] passTime
}
concept Interest {
o String name
o String description
}
query selectConsultantsBySkill {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE (passTime CONTAINS (name == _$targetHobby ))
}
Related
I'm new in golang and Gorm
Here is my struct
type SonModel struct {
ID int64
Age int
Name string
FatherID int64
Father FaterModel `gorm:"foreignKey:ID;references:FatherID"`
}
type FaterModel struct {
ID int64
Name string
GrandID int64
Grand GrandModel `gorm:"foreignKey:ID;references:GrandID"`
}
type GrandModel struct {
ID int64
Name string
}
in raw sql what i want is
select son.id,son.name,to_json(father.*) father from son join father on father.id = son.father_id where (son.name like '%name%' or father.name like '%name%') and son.age = 15
i want join and filter with father
in gorm what i'm doing is
db = db.Joins("Father").Preload("Father.Grand")
db = db.Joins("left join father on father.id = son.id left join grand on grand.id = father.grand_id")
db = db.Where("age = ?",15)
db = db.Where("father.name like ? or son.name like ? or grand.name like ?",name)
and i found it left join father and grand twice
first join Father as Father to get father's column
send is my custom left join
how can i Joins("Father") only one time and use its column to filter
Assuming you want to stick with these struct names, there are a couple of things that need to be done.
First, by convention, GORM determines a table name from the struct name. If you want to use different names than that, you need to implement the Tabler interface for each of your models. Something like this:
func (SonModel) Table() string {
return "son"
}
func (FaterModel) Table() string {
return "father"
}
func (GrandModel) Table() string {
return "grand"
}
After this is done, you can write your query like this:
var sons []SonModel
name = fmt.Sprintf("%%%s%%", name) //for example, output in the resulting query should be %John%
err := db.Preload("Father.Grand").
Joins("left join father on father.id = son.father_id").
Joins("left join grand on grand.id = father.grand_id").
Where("sone.age = ?", 15).
Where("son.name like ? or father.name like ? or grand.name like ?", name, name, name).
Find(&sons).Error
I try this code
sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Model(&SonModel{}).Select("son.id, son.name, father.*").Joins("left join father on father.id = son.id").Where("son.name LIKE ?", "%name%").Where("father.name LIKE ?", "%name%").Where("age = ?", 15).Scan(&SonModel{})
})
fmt.Println(sql)
And the result
SELECT son.id, son.name, father.* FROM "son_models" left join father on father.id = son.id WHERE son.name LIKE '%name%' AND father.name LIKE '%name%' AND age = 15
Is this solve your problem?
Issue
I’m using an existing code first approach in order to acquire data from an existing database. This project is encapsulated in a .NET C# project and contains the model as well as the configuration descriptions.
My aim is to use the existing database context implementation for a F# project. I have been testing many times the database access with a XUnit test project which is also written in F#.
My problem is an unexpected behaviour, when I try to select the last name and the person’s id.
In order to explain my implementation and illustrate the issue I implemented four test functions of which only the first two are executed successfully. The last two test functions fail.
The following error appears
System.InvalidOperationException : The LINQ expression 'LastName' could not
be translated. 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 understand the error because I am not actually using any client specific implementation for the query evaluation. So normally the LINQ-Provider should convert it into a database appropriate SQL definition. For the tests I am using the Entity Framework memory database instance. On top of that, please note that the error also exists on the real database.
Another issue that I do not understand is why the second test works while the third one fails. I changed only the last name select with the id select.
However, I also added a F# query expression since this is actually recommended from the documentation, but with no success.
Is the main problem the usage of the Entity Framework context? If so, how can I then reuse the implementation of the EF database context?
Test and evaluation with LINQPad 6
I tested the behaviour with LINQPad in order to make the use case more simple. Therefore, I used the DemoDB which should be available for everyone.
Apart from that I’m trying to make it reproduceable for a larger community. Unfortunately, the outcome of my test is the same. So, I created a simple database query and changed the order of the named selections. If I change the alphabetical order of the column names, the error appears. Therefore, why is the alphabetical order important in order to have a valid select statement?
I found another closed issue on stackoverflow which describes the usage of anonymous records but the different order is not treated (F# Query Expression / select operator / changing column headings in result).
// successful query
query {
for c in this.Categories do
select {| A = c.CategoryID; B = c.CategoryName; |}
}
// failed query
query {
for c in this.Categories do
select {| B = c.CategoryID; A = c.CategoryName; |}
}
The argument 'value' was the wrong type. Expected
'System.Func`2[System.Int32,<>f__AnonymousType1383943985`2[System.String,System.Int32]]'.
Actual '<>f__AnonymousType1383943985`2[System.String,System.Int32]'.
Test and evaluation with a F# unit test project
Test result summary
I tested the behaviour with .NET 3.1 and .NET 5.0 (projects as well as LINQPad 6). Furthermore, all dependencies have been adjusted accordingly (e.g. Entity Framework 5.0 or 3.1).
Test
Result
A anonymous record
successful
B anonymous record
successful
C anonymous record
failed
D anonymous record
failed
E partial person type
failed
F partial person type
successful
G partial person type
successful
H partial person type
failed
I partial person type
failed
Test outcome
System.InvalidOperationException : The LINQ expression 'LastName' could not be translated. 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'
Entity Framework Core "5.0.3" initialized
'"TestContext"' using provider '"Microsoft.EntityFrameworkCore.InMemory"'
EF core code first database .NET 5 project
public class Person
{
public int Id { get; set; }
public string LastName { get; set; }
}
...
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.ToTable("PERSON");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasColumnName("ID")
.ValueGeneratedNever();
builder.Property(x => x.LastName)
.HasColumnName("LASTNAME")
.HasMaxLength(512)
.IsUnicode(false);
}
...
public class TestContext : DbContext
{
public DbSet<Person> Persons { get; private set; }
public TestContext(DbContextOptions<TestContext> options) : base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new PersonConfig());
}
}
F# xunit test project in order to evaluate the EF core database context access
type PartialPerson = { LastName: string; ID : int; }
type ``success database execution queries`` (output: ITestOutputHelper) =
let sLogger =
LoggerConfiguration()
.MinimumLevel.Verbose()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.TestOutput(output, Events.LogEventLevel.Verbose)
.WriteTo.Debug()
.CreateLogger()
let loggerFactory =
(new LoggerFactory())
.AddSerilog(sLogger)
let options = DbContextOptionsBuilder<TestContext>()
.EnableSensitiveDataLogging(true)
.UseLoggerFactory(loggerFactory)
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
let context = new TestContext(options)
[<Fact>]
let ``success person select lastname Test A`` () =
let rs =
context.Persons.Select(
fun person -> {| Name = person.LastName |} )
rs |> should be Empty // successful
[<Fact>]
let ``success person select id and lastname Test B`` () =
let rs =
context.Persons.Select(
fun person ->
{| ID = person.Id
LastName = person.LastName |})
rs |> should be Empty // successful
[<Fact>]
let ``success person select id and lastname Test C`` () =
let rs =
context.Persons.Select(
fun person ->
{| LastName = person.LastName
ID = person.Id |} )
rs |> should be Empty // failed
[<Fact>]
let ``success person select id and lastname Test D`` () =
let rs =
query {
for person in context.Persons do
select
{| LastName = person.LastName
ID = person.Id |}
}
rs |> should be Empty // failed
// avoid anonymous record and use the partial person type
// type PartialPerson = { LastName: string; ID : int; }
[<Fact>]
let ``success partial person select id and lastname Test E`` () =
let rs =
context.Persons.Select(
fun person ->
{ ID = person.Id
LastName = person.LastName })
rs |> should be Empty // failed
[<Fact>]
let ``success partial person select id and lastname Test F`` () =
let rs =
context.Persons.Select(
fun person ->
{ LastName = person.LastName
ID = person.Id } )
rs |> should be Empty // successful
[<Fact>]
let ``success partial person select id and lastname Test G`` () =
let rs =
query {
for person in context.Persons do
select
{ LastName = person.LastName
ID = person.Id }
}
rs |> should be Empty // successful
[<Fact>]
let ``success partial person select id and lastname Test H`` () =
let rs =
query {
for person in context.Persons do
select
{ ID = person.Id
LastName = person.LastName }
}
rs |> should be Empty // failed
[<Fact>]
let ``success partial person select id and lastname Test I`` () =
let rs =
query {
for person in context.Persons do
select
{ ID = person.Id
LastName = person.LastName }
}
rs.ToList() |> should be Empty // failed
Current findings
It seems that this issue is related to the issues 1226 and 3782. Both issues describe some problems with the order of named selections.
The dapper issue 1226 had a similar problem with the order of anonymous records for the query definition. However, thanks to Isaac Abraham (isaacabraham) who is using the CLIMutable decoration, I thought of turning off the ordering restrictions. So basically the idea was to try it for my tests since the query generation through the LINQ provider could have a positive effect. Unfortunately this was without success, maybe because of the implementation of the LINQ provider because the generation process is implemented with F# and that is the reason why the CLIMutable attribute does not affect it.
After continuing my search, I found another issue 3782 which indicates my problem. The issue has the main focus on the usage of tuples for the data selection but also the issue with records. So, I added another issue description 11131 in order to help with my current findings. Finally, I will keep track the outcome and add it to this issue.
Does this still need answering?
As you already found out, F# anonymous types order fields by name (not by source code order of declaration, as C# anonymous types would do).
When writing {| B = c.CategoryID; A = c.CategoryName; |} in a LINQ query, this will not actually pass an anonymous type, rather the compiler creates an System.Linq.Expressions.Expression that describes how to construct the anonymous type, and later on the underlying framework implementing LINQ (e.g., the Entity Framework) will try to parse that expression (and create e.g. SQL code from it).
Problem here is, c.CategoryID and c.CategoryName may have side effects, hence the compiler will evaluate them in the order specified in the source code (first ID, then Name), but assign them in the order of the anonymous type.
Long story short, the generated System.Linq.Expressions.Expression first will evaluate c.CategoryID, assign the value to a temporary variable, evaluate c.CategoryName, assign that to the anonymous type's first field, and finally assign the temporary variable to the anonymous type's second field. And the EF translator later on does not know how to handle the temporary variable (e.g., how to translate that to SQL.)
(In C#, no field reordering happens, so no temporary variables are required to mask side effects, so the expression parser will not face that problem.) (The F# anonymous type at present is not fully fit for LINQ.)
I am using Postgres with Go Lang & the Echo framework as my base, with that I am using Gorm to build my database queries.
So Here is my Profile Model,
type Profile struct {
gorm.Model
InvoiceCount uint `gorm:"-"`
CompanyName string `gorm:"size:255"`
CompanyNumber string `gorm:"size:10"`
CompanyVatNumber string `gorm:"size:10"`
DateAdded time.Time `gorm:"type:date"`
PrimaryEmail string `gorm:"size:255"`
IsActive bool
Invoice []*Invoice `gorm:"foreignkey:profile_fk" json:",omitempty"`
Address []*Address `gorm:"foreignkey:profile_fk" json:",omitempty"`
}
This is linked into my Invoice model, which I am trying to do a count with on a preload. I added the InvoiceCount uint has a means of adding the count into this model.
So this is what I have tied,
dbCon().
Preload("Invoice", func(db *gorm.DB) *gorm.DB {
return db.Count(&profile)
}).
Find(&profile).
RecordNotFound()
However, I apart from this not working, it returns the following error: (pq: zero-length delimited identifier at or near """").
I am trying to do this with a simple query, but this that wrong? Do I need to just loop around all my profiles and add a count to each? Or drop down to a raw SQL query with a sub select?
Thanks,
I have built this Raw SQL query,
dbConn().
Raw("SELECT p.*, count(i.id) as invoice_count FROM profiles p left join invoices i on i.profile_fk = p.id group by p.id").
Scan(&result)
In my hyperledger composer app I want to write a named query that returns all persons with two specified hobbies.
The model for "Person" is the following:
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
--> Hobby[] hobbies optional
}
The model for "Hobby" is the following:
asset Hobby identified by id {
o String id
o String name
}
The named query has the following structure:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE //not sure what to put here//
}
I'm not sure what to put after the "WHERE" operator in order to achieve what I want.
Is this correct?:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies CONTAINS ((name == _$hobby1) AND (name == _$hobby2)))
}
Or is the following correct?:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies CONTAINS (name == _$hobby1) AND CONTAINS (name == _$hobby2))
}
UPDATE:
Following the answer suggested by Paul O'Mahony, here is how I understand the situation:
Given the following model for "Person":
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
o Hobby[] hobbies optional
}
and the following model for Hobby:
asset Hobby identified by id {
o String id
o String name
}
the following query would succeed in returning all persons with the two specified hobbies:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE ( hobbies CONTAINS [ _$hobby1, _$hobby2] )
}
.... the parameter values sent with the query (and to be inserted for _$hobby1 and _$hobby2, respectively) would have be the Ids of the respective hobbies, correct?
You can't presently, in the Composer Query language, use an aggregate 'AND' in Concepts for CONTAINS currently in the fashion you suggest (OR is fine, but AND you can't) - the name field is compared for each class entry (and it can't be both at the same time with AND). In 0.19.12 onwards of Composer - you could use a getnativeAPI() from a readonly Trxn Processor function to make the equivalent CONTAINS call natively to CouchDB query language.
The AND (as requested above) would work if the field was a String array eg String[] hobbies eg.
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE ( hobbies CONTAINS [ _$hobby1, _$hobby2] )
}
but your field on Person would have to be Hobby[] hobbies optional (ie not an array of relationships ie relationship IDs as it is presently - ).
Here is my query.
SELECT letter
FROM
Letter AS letter,
(evaluateDisplayName) AS displayName
WHERE
letter.id =: someID
AND displayName =: someDisplayName
// AND etc etc...
The Subquery in this part:
(Do some subquery here) AS displayName
I don't know how to form. But the logic is something like this:
private String evaluateDisplayName(Letter letter) {
def username = letter?.sender?.username
def lastName = letter?.sender?.lastName
def emailAddress = letter?.sender?.emailAddress
return username ?: lastName ?: emailAddress
}
How to turn this into a subquery?
You don't need a subquery, the logic of evaluateDisplayName seems to be the same of the coalesce function: return the first not null value. Try with this:
SELECT letter
FROM
Letter AS letter LEFT JOIN letter.sender AS sender
WHERE
letter.id = :someID
AND COALESCE(sender.username, sender.lastName, sender.emailAddress) = :someDisplayName
// AND etc etc...