I use enum to store statuses of my model:
enum status: [ :fresh, :approved, :rejected, :returned, :completed, :removed ]
Now I want to select object with several values of status, something like this:
Documents.find_by_status(:fresh, :returned)
How should I do it correctly in Rails 4?
Every enum attribute has integer representation of its value in table column.
By default your statuses will have values – fresh: 0, approved: 1, rejected: 2 and so on.
The simplest way to get instances with one or another value is to call something like that
Document.where(status: [0, 1])
To improve readability you can implement scope in you model.
class Document < ActiveRecord::Base
enum status: %i(fresh approved rejected returned completed removed)
scope :find_by_status, ->(*args) { where(status: self.statuses.values_at(*args)) }
end
And use it more humanized way
Document.find_by_status(:fresh, :returned)
Related
I want to query records from a specific model via REST-Api from a LoopBack-application. Also i want to include related objects via the include-filter.
This works fine, but returns ALL related objects. Is it possible to limit them and also to order them by a field of related objects?
Models:
- DEPARTMENT
Fields:
- id
- name
- ...
Relations_ -> hasMany: Messages
Relations_ -> hasMany: Members
- MESSAGE
Fields:
- id
- senderId
- body
- ...
- MEMBER
Fields:
- id
- email
- ...
Queries:
What i want to achieve is to query all Departments with all their members, but only the last message ordered by a specific field (created-timestamp).
The first approach could be the plain query-string variant of a GET-Request:
http://loopback-server:3000/api/departments?filter[include]=members&filter[include]=messages
This will return all departments with all messages and all members. However, i would like to limit the number of returned messages to the last one (or last 5 or whatever, sorted by a specific field of MESSAGE-model.
I also tried the jsonfied query syntax:
http://loopback-server:3000/api/departments?filter={"include":{"relation": "messages","limit":1}}
Unfortunately the "limit"-parameter is not used here for the relation of messages.
The following variant will return only first department, means the limit-param is applied to the departments-model, not the relation model.
http://loopback-server:3000/api/departments?filter={"include":{"relation": "messages"},"limit":1}
Then i discovered the scope-parameter
and tried this:
http://loopback-server:3000/api/departments?filter={"include":{"relation": "messages","scope":{"limit":1, "skip":0}}}
This gives a really strange result. This ommits all messages related to departments, instead of one specific record returning one message (it has over 10), what i would expect. Removing the scope-parameter shows that the departments indeed has many messages each.
(I know that the parameters of an URL with all these special characters like {",:"} need to be url-encoded. I leave it clean here for better readability)
My question:
How to achieve that query with a single request?
It's not possible to query relationships by their properties (yet). As for the limit, your last approach with the scope should be modified a little:
"scope":{{"include":{"relation": "messages","limit":1, "skip":0}}}
Here you can read about queries on relations by their properties:
https://github.com/strongloop/loopback/issues/517
I dunno what version you are in, but for Loopback 3
you can do this..
include: {
{
relation: 'Messages', // include the messages object
scope: { // this is where you do a normal filter
where: {<whatevercondition>},
order: "<fieldname> <ASC/DESC>",
limit:1,
include:{
//yes, you can include 3rd level relation as well.
}
}
},
{
relation: 'Members', // include the Members object
scope: { // further filter the related model
order: "<fieldname> <ASC/DESC>",
limit: <whateverlimityoument>
}
}
}
try this code:
`${urlApi}/user/?filter[limit]=${records_per_page}&filter[skip]=${(currentPage -1) *
records_per_page}`
Limit for inclusion scope works correctly when you have only one parent record.
If you want to select N parent records and include 1 related record in each of them, try my workaround: Limit for included records in Loopback4
When querying, ActiveRecord automagically decides how to handle ranges, arrays, etc. based on column type.
Now what I want to do is query an integer column for a set of bit values by calling something like Model.where(bitfield: [1,4,16,32]).count where it will build a query accordingly, counting all objects that have at least one of those bits set.
I already know how to build the resulting SQL, I'm looking for a place to put my code that will basically check for the column type, find out I configured it to be bitfield and use my handler to build the relevant SQL parts.
My first thought would be to make a bit_where method in your model which is bitfield-aware...
class Model << ActiveRecord::Base
BITFIELDS = [:bitfield]
def self.bit_where(*args, options={})
bitfield_options = {}
options.each do |k, v|
if BITFIELDS.include?(k)
bitfield_options[k] = v
options.delete(k)
end
end
collection = self.where(*args, options) # the regular "where" query
bitfield_options.each do |k, v|
... collection.where( custom sql here based on each key, value )
end
collection
end
I have a IQueryable<T> object as search results object.
I apply the filtering and sorting on this search object.
Before I call the GetResults(), I want to order the results based on one of the field's (Fieldname - Priority) value. So for all the items in the IQueryable<T> object, I want to order them desc by Priority field, so all the items which has a value for that field stay at the top and the rest are at the bottom.
I have the fieldmap entry for Priority field.
search.OrderByDescending(i => !string.IsNullOrEmpty(i.GetItem().GetFieldValue("Priority")))
The above command doesn't work. Apparently, I can't use Sitecore extension methods with IQueryable?
If I convert search.ToList(). Do the ordering and then convert it back to AsQueryable(), I get the following error:
There is no method 'GetResults' on type 'Sitecore.ContentSearch.Linq.QueryableExtensions'
that matches the specified arguments
Is there a neat and quick way to get around this?
Cheers
I think you just need to add your field to your SearchResultItem and mark it as an int. I am making the assumption that the field is an int. Make a custom class that inherits SearchResultItem.
public class CustomSearchResultItem : SearchResultItem
{
[IndexField("Priority")]
public int Priority { get; set; }
}
Then use it in your search. Finally order by it.
using (var context = ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var results = context.GetQueryable<CustomSearchResultItem>().Where(prod => prod.Content.Contains("search box text").OrderByDescending(t => t.Priority);
}
Some data found here.
http://www.sitecore.net/learn/blogs/technical-blogs/sitecore-7-development-team/posts/2013/10/sorting-and-ordering-results.aspx
You can order search results using multiple fields by using the OrderByDescending combined with ThenByDescending. So you would need to order by Priority and then by [Name|Date|Whatever].
I want to order them desc by Priority field, so all the items which has a value for that field stay at the top and the rest are at the bottom.
I sort them first on the criteria chosen by the user - like Name, Date created etc. Once I get the results back, I need to order them by priority field
You are conflicting yourself in the questions and comments. If you want the results with priority first and then by user selected results then the following will work:
query = dataQuery.OrderByDescending(i => i.Title).ThenByDescending(i => i["Priority"]);
var results = query.GetResults().Hits.Select(h => h.Document);
There was a bug in earlier version of Sitecore which meant that the ThenBy clause will be added before the OrderBy clause hence it is added in reverse above. You may want to check if this is fixed in the current version. If so simply change your query to:
query = dataQuery.OrderByDescending(i => i["Priority"]).ThenByDescending(i => i.Title);
You don't have to add the field to your SearchResultItem if you just want to order by it, only if you need the actual value of that field returned to as well.
If you need to order by a custom user supplied value then you can pass in i => i["whatever-field-the-user-has-selected"] instead of i.Title.
You can find more info in this blog post.
I am trying to extract the date taken metadata from the flickr api. When I print the metadata it returns the url, the name of the owner and 'none' where a date should be.
I know there are dates taken associated with these photos because I can see them displayed on the pages in the group pool.
How can I retrieve a date?
import flickr_api
flickr_api.set_keys(api_key = 'your-flickr-api-key', api_secret = 'your-flickr-api-secret')
from flickr_api.api import flickr
def findurl(stuff):
photolist = stuff['photos']['photo']
for thing in photolist:
print thing['url_o']
print thing.get('date_taken')
print thing.get('ownername')
stuff = flickr_api.api.call_api(
method='flickr.groups.pools.getPhotos',
group_id = '1156559#N21',
extras = 'date_taken, ownername, url_o',
page = page,
per_page = 100
)
findurl(stuff)
Is this not just a case of the wrong identifier being referenced? Testing the groups.pools.getPhotos in the API Explorer, I see the date taken being returned as datetaken (no underscore). When specifying in extras there is an underscore, but when reading a photo result there is not.
If you are iterating through your photos lets say u have an integer called counter.
This would access datetaken print(photos['photo'][counter]['datetaken'])
because flickrapi returns sth like this
flickrApi({ "photos": {"page: "1" "pages": "1", "perpage": 100, "total": "29983680",
"photo": [ {"id" : "owner" and so on blabla also "datetaken" }]
sth along these lines, you can see that we have a dictionaries within a list which is within a dictionary of another dictionary or sth like that <.<
basically u go through tiers of access points just structure it in your mind
photos['page'] would return the page
photos['total'] would return total pages
photos['photo'] returns a photo object with all its values like id and height/width or whatever the object has various attributes and there are also multiple objects, its a list of objects, so you need to iterate through it to get all, since we want a specific attribute we need to tell him which photo he should take it from, therefor the counter
You can iterate through it without counter but then you can't access the attributes.
Make sure counter starts with 0 and counts up after u access datetaken, otherwise you miss that one photo in the start.
Short Answer:
photos['photo][counter]['datetaken']
I have a channel model with 2 associations, "contents" and "subscriptions".
In the channel index the user has the possibility of ordering the channels by number of subscriptions or number of approved contents.
While in development everything seems to work properly (by observation of the results, can be malfunctioning and be a question of not enough data to see it properly), in staging the results are random, sometimes showing them properly, sometimes don't.
At first I wasn't using delta indexes and thought the problem could be there so every time I approve a content I call:
Delayed::Job.enqueue(DelayedRake.new("ts:index"), queue: "sphinx")
Since the subscriptions don't have indexes, I don't reindex every time I create one ( should I do it? )
Then I started using delta indexes in the channel and I still get the same problems:
ThinkingSphinx::Index.define :channel, with: :active_record, delta: true do
# fields
indexes :name, sortable: true
indexes description
# attributes
has created_at, sortable: true
has approved, type: :boolean
has public, type: :boolean
join subscriptions
has "COUNT(subscriptions.id)", as: :subscription_count, type: :integer, sortable: true
join contents.approved
has "COUNT(contents.id)", as: :content_count, type: :integer, sortable: true
end
And here is the search call in the controller:
def index
if params[:order_by].present?
#channels = Channel.search params[:search],
order: "#{params[:order_by]} DESC",
page: params[:page], per_page: 6
else
#channels = Channel.search params[:search],
order: :name,
page: params[:page], per_page: 6
end
end
Summarising, my questions would be:
1. Are my channel indexes well formed?
2. Should subscriptions by indexed as well or is it enough to join them in my channel index?
3. Should I run reindex after I create a subscription / approve a content or the delta index in the channel deals with that since I have those two controllers joined in the channel index?
Your index looks fine, but if you're using deltas (and I think that's the wisest approach here, to have the data up-to-date), then you want to fire deltas for the related channels when a subscription or content is created/edited/deleted. This is covered in the documentation (see the "Deltas and Associations" section), but you'd be looking at something like this in both Subscription and Content:
after_save :set_channel_delta_flag
after_destroy :set_channel_delta_flag
# ...
private
def set_channel_delta_flag
channel.update_attributes :delta => true
end
Given you're using Delayed Job, I'd recommend investigating ts-delayed-delta to ensure delta updates are happening out of your normal HTTP request flow. And I highly recommend not running a full index after every change - that has the potential of getting quite slow quite quickly (and adding to the server load unnecessarily).