Decoding Vapor raw query to MySQLModel - swift

I'm trying to run a SQL Raw Query in a Vapor application, and decoded to a MySQLModel but apparently it returns an error.
final class ClassA: MySQLModel {
var id: Int?
var title: String
var description: String
}
But when I run a simple Select From raw query it returns a decoding error: "Value of type 'String' required for key 'title'." with it's weird because the sql run in the MySQL console does run correctly. The thing is when I decode the same query but instead of using a MySQLModel I use a Content like this:
final class ClassB: Content {
var id: Int?
var title: String
var description: String
}
And this is actually ends up decoding without errors.
Edit: The ClassA works perfectly with the regular ORM, it is only when I try to decode from Raw Query that it fails.
Edit: As ask by #Nick the sql does affect the response, when run:
SELECT * FROM ClassA
The return comes back with no error, but my sql includes a complex Subquery like this:
SELECT c.* FROM ClassA c WHERE c.id IN (SELECT id FROM ...);
Note: When run in MySQL Console the query returns no Error and as said the query can be decoded to a Content class exactly like ClassA Example: ClassB.
Edit: The code for running and decoding the raw query is simply:
return request.withNewConnection(to: .mysql) { (con) -> EventLoopFuture<[ClassA]> in
return con.raw(sql).all(decoding: ClassA.self).map { (classes) -> [ClassA] in
return classes
}
}

I resolve the problem by trying different permutation of the same Query as proposed that the problem was there by #Nick.
The problem lies here in the query (Not the Subquery as initially thought), the difference between this query:
SELECT * FROM ClassA;
And this query:
SELECT c.* FROM ClassA c WHERE c.id IN (SELECT id FROM ...);
besides the subquery is the naming of the table to "c" this notation for some reason is invalid for decoding the MySQLModel type in Vapor they will only work on Content types.
The solution is to remove the notation and use the entire name in the Query:
SELECT ClassA.* FROM ClassA WHERE ClassA.id IN (SELECT id FROM ...);

Related

f# Insert on MongoDB using Records

I've been trying for a while to insert on MongoDB using only records with no success.
My problem is that I want to create a simple insert function which I send a generic type and it is inserted into the database.
Like so.
let insert (value: 'a) =
let collection = MongoClient().GetDatabase("db").GetCollection<'a> "col"
collection.InsertOne value
From this function, I tried inserting the following records.
// Error that it can't set the Id
type t1 = {
Id: ObjectId
Text: string
}
// Creates the record perfectly but doesn't generate a new Id
type t2 = {
Id: string
Text: string
}
// Creates the record and autogenerates the Id but doesn't insert the Text, and there are two Ids (_id, Id#)
type t3 = {
mutable Id: ObjectId
Text: string
}
// Creates the record and autogenerates the Id but for every property it generates two on MongoDB (_id, Id#, Text, Text#)
type t4 = {
mutable Id: ObjectId
mutable Text: string
}
So does anyone can think of a solution for this or am I stuck having to use a class.
// Works!!!
type t5() =
member val Id = ObjectId.Empty with get, set
member val Name = "" with get, set
Also, does anyone has any Idea of why when the C# MongoDB library translates the mutable he gets the property with the # at the end?
I would be fine with having all my properties set as mutable, although this wouldn't be my first choice, having he create multiple properties on the DB is quite bad.
You could try annotating your records with CLIMutable (and no mutable fields).
The #s end up in the DB because MongoDB using reflection and F# implementing mutable with backing fields fieldName#

How to insert a vertex with link type property with the orientjs query builder?

I am trying to insert a vertex with orientjs(previously oriento) query builder. My class has a link type property pointing to another class.
I know I can get it to work with a raw query string but I would love to use the query builder.
Here is what I've tried so far :
db.insert()
.into('VertexClassName')
.set({"prop":"value", "linkProperty":"33:1289287"})
db.insert()
.into('VertexClassName')
.set({"prop":"value", "linkProperty":"#33:1289287"})
I get the following error :
Error on saving record in cluster #13
Am I setting properties in the right way ?
Could the error be related to somtehing else ?
I have sucessfully ran an insert query in the cluster #13 with a raw query string in the studio...
According to the official documentation it seems that the problem might be at the end of your statement
db.insert().into('VertexClassName')
.set({"prop":"value", "linkProperty":"33:1289287"}).one()
.then(function (data) {
// callback
});
Check if your code works adding one() to the pipe line
EDITED: I found this method in orientjs.
db.create('VERTEX', 'V')
.set({
key: 'value',
foo: 'bar'
})
.one()
.then(function (vertex) {
console.log('created vertex', vertex);
});
When using Tinkerpop API they recommend using createVertex instead of insert, because createVertex is intended for graphs and insert for Documents... Could you try with the create() method instead?
I am using SQL and it worked.
sql = "INSERT INTO Station set linked = (select from LinkedClass where LinkedProb = 'value'), prop = 'value'"
OrientVertex vertex = new OrientVertex();
vertex = graph.command(new OCommandSQL(sql)).execute();
I don't think that's possible unless you've added a proper field with the right type 'Link' in your schema. (which I rarely do).
Now instead of having the right 'link' type inserted you can do the opposite, store is as a String, and leverage the query functions to use it correctly:
db.insert().into('table').set({prop: '#15:14'}).one();
And it will be converted as String (which is a bit sad) but then you can use that in your queries:
SELECT eval(prop) FROM table;
And it will be 'eval'-ed to a Node RecordID that you can directly use and call functions like expand() on.
For example:
SELECT name FROM (SELECT expand(eval(prop)) FROM table);
Will eval the node stored in the insert(), grab the node, expand it and collect its name property.

LINQ to Entities does not recognize the method with Let Statement

I am in the process of converting an application that uses LINQ to SQL over to LINQ to Entities. I use a repository pattern and I have run in a problem that works in LINQ to SQL but not Entities.
In my data layer, I use LINQ statements to fill my object graph so that none of my database entities are exposed anywhere else. In this example, I have a Lookup Respository that returns a list of Categories. It looks like this:
public IQueryable<Entities.DomainModels.Category> getCategories()
{
return (from c in Categories
where !c.inactive
orderby c.categoryName
select new Entities.DomainModels.Category
{
id = c.categoryID,
category = c.categoryName,
inactive = c.inactive
});
}
Later, I want to put the categories into a sub query and it looks like this:
var d = from p in Programs
let categories = (from pc in p.Categories
join c in getCategories() on pc.categoryID equals c.id
select c)
select new
{
id = p.id,
title = p.title
categories = categories.ToList()
};
When I run this, I get the following error:
LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[Entities.DomainModels.Category] getCategories()' method, and this method cannot be translated into a store expression.
For reference, the following works though it doesn't return the data I need (it's basically a join):
var q = from p in Programs
from pc in p.Categories
join c in getCategories() on pc.categoryID equals c.id
select new
{
id = p.id,
category = c
};
I understand what the error means in concept however LINQ to SQL would make it work. I have this pattern throughout my data layer and I really want to keep it. Should this be working? If not, how can I modify it without mixing my layers.
You cant pass getCategories() to EF.
The query must be destructible to expression tree.
Calculate getCategories() first.
eg
var simpleList = getCategories().Select(id).Tolist;
then use a contains
where(t=> simpleList.Contains(t.CatId) // or the query syntax equivalent

EF Code First - convert string to int in query

I need to convert String to Int32 value in my query. It must be done on sql server-side, not in memory.
I've seen answers how to do this in Model First approach, LINQ to Entities.
here and here
But I need it to be done with Code First.
I write query to DbSet<>.
Is there any way to do this? Please, help :)
New Answer:
The only way I can find information for is to use a custom query.
For instance:
from user in Users.SqlQuery("SELECT Id, CAST(Age AS INT) as Age, FirstName, LastName FROM Users")
select new
{
id = user.Id,
Age = user.Age
}
In my tests it seems every value for property on the entity you map to have to be included in the select even if you select to a custom object that do not include every property. In my example above I include FirstName and LastName even though they aren't used in the select.
Old answer:
About converting to string on sql-side:
You can use SqlFunctions.StringConvert if you cast your int to a double or decimal first
Problem with converting int to string in Linq to entities
from user in Users
select new
{
IdAsText = SqlClient.SqlFunctions.StringConvert((decimal)user.Id)
}
The cast to float or decimal is necessary because the STR-function on the sql-server requires a float: Why is there no int overload for SqlFunctions.StringConvert
Update:
In LINQPad the generated SQL-query from the above comes out to:
SELECT
[Extent1].[Id] AS [Id],
STR( CAST( [Extent1].[Id] AS decimal(19,0))) AS [C1]
FROM [dbo].[Users] AS [Extent1]

I get LINQ to Entities Int32 ToInt32(System.String) when convert

I get LINQ to Entities Int32 ToInt32(System.String) when convert.i tried int.Parse() , SqlFunction and EdmFunction but the problem still going on.
Exception:
System.NotSupportedException: LINQ to Entities does not recognize the method 'Int32 ToInt32(System.String)' method, and this method cannot be translated into a store expression
Code:
try
{
ModelEntities me = new ModelEntities();
var query = from p in me.Products
join c in me.ProductCategories
on Convert.ToInt32(p.CategoryId) equals c.CategoryId
select new
{
p.ProductTitle,
c.CategoryName
};
rptProducts.DataSource = query;
rptProducts.DataBind();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
You cannot use Convert.ToInt32 inside your linq query. Linq has its own syntax and does not recognize external methods.
Either you have to extract the variable you are looking for to C#, convert it, and use it as a variable in another query. Or you could make both categoryIDs ints, if you have access to the database. It makes sense that similar fields like those should be of the same type.
Hope that helps!
I suggest converting c.CategoryId to string like this
var query = from p in me.Products
from c in me.ProductCategories
let categoryId = me.ProductCategories.Take(1).Select(x => c.CategoryId).Cast<string>().FirstOrDefault()
where p.CategoryId == categoryId
select new
{
p.ProductTitle,
c.CategoryName
};