Rails 4 - Comparing two variables in scope - ruby

How do you compare two variables in a custom scope with Rails 4 ?
Documentation and common examples always show basic comparisons with table attributes
Like in this example :
class Post < ActiveRecord::Base
scope :created_before, ->(time) { where("created_at < ?", time) }
end
But in my case, I want to compare a datetime from my Model to a date string coming from a form submission
scope :departure, -> (departure) { where("departure = ?", "%#{departure}%")}
The problem is that I want to do some manipulation to my model variable before the comparison (ex, convert to string or change format)
I tried with different methods like this one
def self.departure(departure)
#date_without_time = flights.departure.to_date.to_s
where(#date_without_time, "= ?", departure)
end
But the condition end up like this (2012-01-03) instead of (departure = "%#{departure}%")
Is there a better way to do it?
To be more general, how do you create methods or scope to compare two variables instead of only comparing one variable to a model attribute?
In this case, I want to compare my model attribute which is a Datetime with a form submitted string date, so before I can compare the two dates I need to do some treatment on my model attribute since I need to ignore the time part and format it to a string
Update :
I ended up making it work using this method, but I'd still like to know if it's possible to call class or instance method on a Model column in a scope or if there is a better way to handle the Datetime/form date comparison in a better way
scope :departure_s, -> (departure) { where("departure > ?", departure.to_datetime.beginning_of_day)}
scope :departure_e, -> (departure) { where("departure < ?", departure.to_datetime.end_of_day)}
scope :departure, -> (departure) { departure_e(departure).departure_s(departure)}

You're taking the wrong approach trying to manipulate models. You're running a query to return models, you don't have any models to manipulate yet.
You can accomplish what you're trying to do on the database level with SQL. I would write the scope like this
if the departure variable is a string that represents a date in the format 'yyyy-mm-dd' you can do
scope :departure, -> (departure) { where("date(departure) = ?", departure )}
This assumes you're using postgres, it might be slightly different for mysql. the date() function converts a datetime to date on the database elevel

Related

How to return a query from cosmos db order by date string?

I have a cosmos db collection. I need to query all documents and return them in order of creation date. Creation date is a defined field but for historical reason it is in string format as MM/dd/yyyy. For example: 02/09/2019. If I just order by this string, the result is chaos.
I am using linq lambda to write my query in webapi. I have tried to parse the string and try to convert the string. Both returned "method not supported".
Here is my query:
var query = Client.CreateDocumentQuery<MyModel>(CollectionLink)
.Where(f => f.ModelType == typeof(MyModel).Name.ToLower() && f.Language == getMyModelsRequestModel.Language )
.OrderByDescending(f => f.CreationDate)
.AsDocumentQuery();
Appreciate for any advice. Thanks. It will be huge effort to go back and modify the format of the field (which affects many other things). I wish to avoid it if possible.
Chen Wang.Since the order by does not support derived values or sub query(link),so you need to sort the derived values by yourself i think.
You could construct the MM/dd/yyyy to yyyymmdd by UDF in cosmos db.
udf:
function getValue(datetime){
return datetime.substring(6,10)+datetime.substring(0,2)+datetime.substring(3,5);
}
sql:
SELECT udf.getValue(c.time) as time from c
Then you could sort the array by property value of class in c# code.Please follow this case:How to sort an array containing class objects by a property value of a class instance?

Creating a scope on an ActiveRecord Model

I have a ActiveRecord model called Panda with a column called variant, which can consist of values like ‘bam-abc’, ‘bam-123’, ‘boo-abc’ and ‘boo-123’ I want to create a scope which selects all records where the variant starts with ‘boo’.
In the console, I can select those records (starting with ‘boo') with the following:
Panda.select{|p| p.variant.starts_with? 'boo'}
Is there a way to turn that into a scope on the Panda class? I need to be able to do a 'to_sql' on the scope for my RSpec tests.
You'd want to use a scope that sends a LIKE into the database, something like:
scope :boos, -> { where('pandas.variants like ?', 'boo%') }
or equivalently, use a class method:
def self.boos
where('pandas.variants like ?', 'boo%')
end
Then you can say things like:
Panda.boos.where(...)
Panda.where(...).boos
Panda.boos.where(...).to_sql
Panda.where(...).boos.to_sql
You only need to use the pandas prefix on the column name if you think you'll be doing JOINs with other tables that might leave the variant name ambiguous. If you'll never be doing JOINs or you'll only ever have one variant column in your database then you could use one of:
scope :boos, -> { where('variants like ?', 'boo%') }
def self.boos
where('variants like ?', 'boo%')
end
Add the line below to the Panda class
scope :boo, -> { where('variant LIKE ?', 'boo%') }
You can then use Panda.boo to get all the records with variant starting with boo. Panda.boo.to_sql will give you the sql

Rails 3 scope query

I have a model named "SessRequest", which contains fields "sess_start" (start datetime of session) and "sess_duration" (session duration in hours). So, end time of a session is: sess_start + sess_duration.hours
I need to filter out all sessions whose "end_time" is behind current time.
For this the corresponding model scope looks like following:
SESSION_ASSUMED_PAST_AFTER_HRS = 3
scope :past,
lambda{where('(sess_start < :current_time) AND (sess_start IS NOT NULL)', :current_time => 0.days.from_now - SESSION_ASSUMED_PAST_AFTER_HRS.hours)};
I want to make this scope based on "sess_duration" database field rather than constant SESSION_ASSUMED_PAST_AFTER_HRS.
How can this be done while still keeping it database agnostic?
You can use squeel gem for creating complex SQL queries and various RDBMS systems.
Your code will looks like:
SESSION_ASSUMED_PAST_AFTER_HRS = 3
scope :past, where { (sess_start < SESSION_ASSUMED_PAST_AFTER_HRS.hours.ago) & (sess_start != nil ) }

Changing values of an object in a LINQ-statement

I want to add some calculated properties to an EntityObject without loosing the possibility of querying it agains the database.
I created a partial class and added the fields I need in the object. Than I wrote a static function "AttachProperties" that should somehow add some calculated values. I cannot do this on clientside, since several other functions attach some filter-conditions to the query.
The functions should look like this:
return query.Select(o =>
{
o.HasCalculatedProperties = true;
o.Value = 2;
return o;
});
In my case the calculated value depends on several lookups and is not just a simple "2". This sample works with an IEnumerable but, of course, not with an IQueryable
I first created a new class with the EntityObject as property and added the other necessary fields but now I need this extended class to be of the same basetype.
First, in my opinion changing objects in a Select() is a bad idea, because it makes something else happen (state change) than the method name suggests (projection), which is always a recipe for trouble. Linq is rooted in a functional programming (stateless) paradigm, so this kind of usage is just not expected.
But you can extend your class with methods that return a calculation result, like:
partial class EntityObject
{
public int GetValue()
{
return this.MappedProp1 * this.MappedProp2;
}
}
It is a bit hard to tell from your question whether this will work for you. If generating a calculated value involves more than a simple calculation from an object's own properties it may be better to leave your entities alone and create a services that return calculation results from an object graph.
Try something like this:
return from o in collection
select new O()
{
OtherProperty = o.OtherProperty,
HasCalculatedProperties = true,
Value = 2
};
This will create a copy of the original object with the changes you require and avoid all the messiness that come with modifying an entity in a select clause.

Entity Framework LINQ Query using Custom C# Class Method - Once yes, once no - because executing on the client or in SQL?

I have two Entity Framework 4 Linq queries I wrote that make use of a custom class method, one works and one does not:
The custom method is:
public static DateTime GetLastReadToDate(string fbaUsername, Discussion discussion)
{
return (discussion.DiscussionUserReads.Where(dur => dur.User.aspnet_User.UserName == fbaUsername).FirstOrDefault() ?? new DiscussionUserRead { ReadToDate = DateTime.Now.AddYears(-99) }).ReadToDate;
}
The linq query that works calls a from after a from, the equivalent of SelectMany():
from g in oc.Users.Where(u => u.aspnet_User.UserName == fbaUsername).First().Groups
from d in g.Discussions
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > DiscussionRepository.GetLastReadToDate(fbaUsername, p.Discussion)).Count()
};
The query that does not work is more like a regular select:
from d in oc.Discussions
where d.Group.Name == "Student"
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > DiscussionRepository.GetLastReadToDate(fbaUsername, p.Discussion)).Count(),
};
The error I get is:
LINQ to Entities does not recognize the method 'System.DateTime GetLastReadToDate(System.String, Discussion)' method, and this method cannot be translated into a store expression.
My question is, why am I able to use my custom GetLastReadToDate() method in the first query and not the second? I suppose this has something to do with what gets executed on the db server and what gets executed on the client? These queries seem to use the GetLastReadToDate() method so similarly though, I'm wondering why would work for the first and not the second, and most importantly if there's a way to factor common query syntax like what's in the GetLastReadToDate() method into a separate location to be reused in several different other LINQ queries.
Please note all these queries are sharing the same object context.
I think your better of using a Model Defined Function here.
Define a scalar function in your database which returns a DateTime, pass through whatever you need, map it on your model, then use it in your LINQ query:
from g in oc.Users.Where(u => u.aspnet_User.UserName == fbaUsername).First().Groups
from d in g.Discussions
select new
{
UnReadPostCount = d.Posts.Where(p => p.CreatedDate > myFunkyModelFunction(fbaUsername, p.Discussion)).Count()
};
and most importantly if there's a way to factor common query syntax like what's in the GetLastReadToDate() method into a separate location to be reused in several different places LINQ queries.
A stored procedure would probably be one way to store that 'common query syntax"...EF, at least 4.0, works very nicely with SP's.

Resources