List of starting characters in employee names from table - mysql

The employees list page on my rails app has a requirement of showing links for each of the starting characters in employee names, to facilitate quickly searching for users by a letter.
The link list should include only those letters which have names starting from them. Eg: If the table contains 3 names - Joe User, Jill User, and Example User - only two links E and J should be displayed.
Wondering what's the most efficient way to do this.
As a first attempt, I added a class method in the Employee class as follows:
def self.list_of_starting_characters
array = []
('A'..'Z').to_a.each do |char|
array << char unless self.where("name like '#{char}%'").count.zero?
end
array
end
This gets called on every render of the view; so changed it to use a class variable as follows:
##starting_characters_list = []
def self.list_of_starting_characters
return ##starting_characters_list unless ##starting_characters_list.empty?
array = []
('A'..'Z').to_a.each do |char|
array << char unless self.where("name like '#{char}%'").count.zero?
end
##starting_characters_list = array
end
Now it is called only once per session. Are there better ways to accomplish this?
One other option I am considering is to store the list of starting characters in a separate table and update it when the employee data is modified, but worried it might be too much of a hassle, to keep the separate table in sync with the main table.

The following should work for you:
first_letters = self.pluck(:name).group_by{ |name| name[0].upcase }.keys
It grabs all User's names, group them by the first letter of the name and get the only the keys of the hash generated by the group_by.

Related

Rails, MySql, JSON column which stores array of UUIDs - Need to do exact match

I have a model called lists, which has a column called item_ids. item_ids is a JSON column (MySQL) and the column contains array of UUIDs, each referring to one item.
Now when someone creates a new list, I need to search whether there is an existing list with same set of UUIDs, and I want to do this search using query itself for faster response. Also use ActiveRecord querying as much as possible.
How do i achieve this?
item_ids = ["11E85378-CFE8-39F8-89DC-7086913CFD4B", "11E85354-304C-0664-9E81-0A281BE2CA42"]
v = List.new(item_ids: item_ids)
v.save!
Now, how do I check whether a list exists which has item ids exactly matches with that mentioned in query ? Following wont work.
list_count = List.where(item_ids: item_ids).count
Edit 1
List.where("JSON_CONTAINS(item_ids, ?) ", item_ids.to_json).count
This statement works, but it counts even if only one of the item matches. Looking for exact number of items.
Edit 2
List.where("JSON_CONTAINS( item_ids, ?) and JSON_LENGTH(item_ids) = ?", item_ids.to_json, item_ids.size).count
Looks like this is working
You can implement a has many relation between lists and items and then access like this.
List.includes(:item).where('items.id in (?)',item_ids)
To implement has_many relation:
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

How to cross reference user input and return only exact matches

My program cross-references a user's inputted list of ingredients with names in my database and returns exact matches of items in the list (not the list itself) to names in the database.
Ingredients in the database have names up to 4 words long, also including spaces numbers, dashes and bracketed words. The user inputs a single item, or list of items and only the matches from the database are outputted on the results page. The users search can return only exact matches, as anything else would skew the results.
An example inputted list would look like this:
Avene Thermal Spring Water (Avene Aqua), Bis-Ethylexyloxyphenol Methoxyphenyl Triazine, Diethylhexyl Butamido Triazone, Butyl Methxydibenzoylmethane, Benzoic Acid, Caprylyl Glycol, Tocopheryl Glucoside, Polysorbate 20, Excipients, Methylene Bis-Benzotriazolyl Tetramethylbutylphenol [Nano]
And example matches would return as:
Methylene Bis-Benzotriazolyl Tetramethylbutylphenol [Nano],
Polysorbate 20,
Excipients
The inputted list is split into an array, then intersected with the array of ingredient names. If there are any matches, they are printed out in my view. The method is below.
class IngredientsController < ApplicationController
def search
#parameters = params[:search].downcase.split(/, /)
#results = #parameters & ingredient_names
end
private
def ingredient_names
#ingredient_names = Ingredient.pluck :name
end
end
This code functions correctly, but it's not very user friendly as it requires the user to input a list with each item separated by a comma and a space. If there are any mistakes in the input (eg. no space between comma and next word) no matches will be returned. I want to know if there is another way to implement this that is more user friendly, while still maintaining the exact matches.

What is the best way to merge 2 tables with Active Record and Mysql

We need to allow users to customize their entities like products... so my intention was to have a product table and a custom_product table with just the information the users are allowed to change.
When a client goes to the product I want to merge the information, means I want to merge the two tables - the custom overwrites the default Products table.
I know that in mysql there exists a ifnull(a.title, b.title) way but I was wondering if there is any nice and efficient way to solve this in Rails 4 with Active Record. Assume that the products and custom products table have just 2 columns, ID and TITLE
I think you can convert both objects to JSON and then handle their params as a hash, using the merge method:
class Product
end
class Customization
belongs_to :product
end
a = Product.find(...)
b = a.customization
c = JSON(a.to_json).merge(JSON(b.to_json).reject!{|k,v| v.nil?})
Therefore c will contain all params from Product eventually overridden by those in Customization which are not nil.
If you still want to use a Product object with hybrid values (taken from Customization) you can try this:
a.attributes = a.attributes.merge(b.attributes.reject!{|k,v| v.nil?})
In this case a will still be a Product instance. I would recommend to keep the same attributes in both models when doing this.

Build and Array of Arrays from query result with <cfloop>

I've got records in my MySQL projects database that have several boolean flags to help me sort the data. I have 3 categories planning, landscape, and environmental and 4 classes (or subcategories) for each category; industrial, government, education, residential.
My goal is to use ColdFusion to create and store the project_id numbers in an array of some kind that will basically have the projects sorted by category and class. That way I can grab just the industrial projects in the planning category and construct a link to that project.
So, the first position in the array would be planning and inside that first position would be the 4 classes, then, within that would be all of the project_id numbers that returned true for those particular criteria.
The logic I'm looking to create goes like this...
Loop over the query result, if planning = true and industrial = true, place the project id # into the planning array inside the industrial array.
How can I use <cfloop> to loop over the list of project records, recognize the category and class flags and construct a clean and usable dataset? Can this be handles in the query in some way?
Figure out the desired data structure
look at your existing data structure
figure out the algorithm to translate from one to the other
You may cfloop the query, and use a large cfswitch (or large set of if-then-else) to figure out how you want to store the id in your desired data structure. Or if you can map the class/category name as a struct key, then it might be easier.
Or you may use cfoutput group="" if it helps (cfloop group="" is available on in CF10+)
Finally, maybe you don't even need the data structure, just use Query of Queries wherever you need.
You may be able to utilize the Underscore.cfc library for this. For example, you could use the filter function to extract an array of structs representing the query rows you want:
planningArray = _.filter(queryResult, function(row){
return (row.planning == true && row.industrial == true);
});

Django: Get the next and previous item IDs for a specific item given multiple OR parameters

I've got a basic Item model in my Django app:
class Item(models.Model):
name = models.CharField(max_length=200)
brand = models.ForeignKey(User, related_name='items')
price = models.CharField(max_length=10)
upload_date = models.DateTimeField(auto_now=False, auto_now_add=True)
For a given item, I am trying to get the next and previous items in the db.
I know I can do this using Django's built-in Model.get_next_by_FOO(**kwargs), which works perfectly well with the upload_date field, but I need to get the previous and next items given certain OR parameters (for example items that have a certain brand OR are under a certain price).
I can get the previous item using a query like this:
previous_item = Item.objects.filter(id__lt=item.id).filter(Q(brand__in=brands)|Q(price__lt=100))[:1]
My problem is with the next item. Running the same query for the next item results in the most recent item overall that meets the criteria, not the item that comes after the current item in the database. I understand that this is because of the way Django queries the database, but don't know how to get around it to get the item that comes directly after the current item that also meets the criteria.
Is there a fast, simple way to get the next item for a specific item given multiple OR parameters?
Thanks!
I didn't test any of this so might just be talking out my arse, but maybe it's a starting point?
Edit: Oh, yes, you were pretty clear about the "OR" issue, but still I missed it. Trying again. The basic idea is to convert your queries into lists and merge the lists. But thinking on that now - I think you can create actual querysets yourself - not that it might be useful in this case? You could do the common heavy lifting of the OR in the function then pass back a queryset which can be further manipulated by the view.
def get_next(item, brand=None, max_price=None)
"""Returns the next item to the one provided"""
# apply limits to brand/price as specified
if brand is not None:
brand_candy = Item.objects.filter(brand=brand)\
.values_list('id','update_date')
if max_price is not None
price_candy = Item.objects.filter(price__lt=max_price)\
.values_list('id','update_date')
# arrange all candidates into a joined list
# TODO: finish this to merge the lists and sort by update_date
ids = brand_candy.join(price_candy)
# find the location of the current item in the list
# TODO: handle not being there
idx = ids.index(item.id)
# TODO: handle edges of the list
next_idx = idx + 1
# pluck out the next item
return Item.objects.get(id=ids[next_idx])