I'm creating a messaging system in Rails, and in order to display conversation partners, I get their ID from the messages that current_user is included in:
Message.where("to_id = ? or user_id = ?", current_user.id, current_user.id)
However, as I need only the other user's id from that query, I need to select it, and I would like to choose either :to_id or :user_id whichever is not equal to current_user.id
Something like:
Message.where("to_id = ? or user_id = ?", current_user.id, current_user.id).select(:to_id, :user_id unless current_user.id)
Thank you in advance!
Edit: Here is the Messages table simplified:
create_table "messages", force: true do |t|
t.integer "to_id" -> User to whom the message was forwarded to
t.integer "user_id" -> User who wrote the message
end
if your just trying to get the IDs:
Message.where("to_id = ? or user_id = ?", current_user.id, current_user.id).
map { |m|
m.user_id == current_user.id ? m.to_id : m.user_id
}
this will give you and Array like
# => [3771, 3611, 3749, 3638, 3697, 3786]
Related
Hi i'm try to check search engine's performance of my ROR application.
I have 4 search input forms : title, content, created_on (date) and updated_on (date)
I want to check performace of search depending on the presence or absence of an index. (in my case, index presence on created_on and absence on updated_on)
My controller of Post
def index
search_start_time = Time.now
#posts = Post.search(params[:title], params[:content], params[:created_on], params[:updated_on])
# this line for check performance of search
puts Time.now - search_start_time
end
My schema
create_table 'posts', force: :cascade do |t|
t.string 'title', null: false
t.string 'content', null: false
t.date 'created_on', null: false, index: true
t.date 'updated_on', null: false
end
In my post.rb, i maked search method like this
def self.search(title, content, started_on, finished_on)
where([
"title LIKE ? AND content LIKE ? AND CAST(started_on AS text) LIKE ? AND CAST(finished_on AS text) LIKE ?",
"%#{title}%", "%#{content}%", "%#{started_on}%", "%#{finished_on}%"
])
end
With my code, i performance but there were not big difference with search performance of "indexed" and "not indexed" columns.
Is there a problem with my code? Or does the index not affect the search results?
The number of records is 10 million, and an indexed column always comes out similar to the speed of an unindexed column.
I tried to change my search method like this ->
def self.search(title = '', content = '', started_on = '', finished_on = '')
But there was not difference.
I'm looking to find records where #messages.visiblity (array) matches the current_user.id with this simple call:
#messages = Message.where(visibility: [current_user.id])
However it's not returning anything! So frustrating. Here's a sample of a message that should be in that #messages collection:
2.3.3 :007 > Message.last
Message Load (0.4ms) SELECT "messages".* FROM "messages" ORDER BY "messages"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Message id: 35, subject: "hello!", content: "can you see this", user_id: 40, created_at: "2020-08-06 03:22:59", updated_at: "2020-08-06 03:22:59", visibility: ["55", "49"], active: true>
Here the current user.id is 49.
It wont add that message to the #message instance until all user.ids are met, not just the one id that is 49. The record will be added to #messages if I do this:
#messages = Message.where(visibility: ["55", current_user.id]) #DON'T WANT THIS!
But that's ridiculous. Everywhere I'm looking says the first call should work, what am I doing wrong?
Here is my Message schema:
create_table "messages", force: :cascade do |t|
t.string "subject"
t.string "content"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "visibility", default: [], array: true
and here is the form that collects the data:
<%= form_with(model: #message) do |form| %>
<%= form.check_box(:visibility, {:multiple => true}, u.id, nil) %> - <%= u.email %>
<% end %>
this should work:
Message.where('visibility #> ARRAY[?]::string[]', ["49"])
or this:
Message.where("'49' = ANY(visibility)")
Following is the way how we can do it in databases irrespective of if database supports Array or not.
There is a pattern how serialized array is stored in database.
["55", "49"] will be stored as "55"\n "49"\n (or 55\n 49\n)
So you can search using LIKE operator
#messages = Message.where("visibility LIKE ?", "% #{current_user.id}\n%")
OR
#messages = Message.where("visibility LIKE ?", '% "#{current_user.id}"\n%')
Note: There is space between % and # in % #{current_user.id}
You can use this also
Message.where("visibility && array[?]",current_user.id.to_s)
I am casting current_user.id to STRING, because visibility is a STRING ARRAY and it cannot be compared to integer value of ID
You can also specify it as below:
Message.where("visibility && array[?]",[current_user.id.to_s])
Or with Multiple value array as below:
Message.where("visibility && array[?]",['55','49'])
Multiple values are compared as AND operation.
I have part of a rails application where a user will create a recipe that will be saved in their "cookbook". Other users will be able to take recipes from other users. So there will be an aspect in the application that shows who created the recipe.
Schema for a Recipe
create_table "recipes", force: :cascade do |t|
t.string "recipe_name"
t.string "description"
t.integer "calories"
t.integer "carbs"
t.integer "fats"
t.integer "protein"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Where I am having trouble is displaying the recipe's creator.
def show
#user = current_user
#recipe = Recipe.find_by(params[:id])
creator = User.find_by(params[#recipe.user_id])
#creator = creator.first_name
end
So for right now I have two user's John (Id: 1) and Alex (Id:2). When I have Alex make a recipe and I put a pry under #recipe I get a user_id of 2 when I call #recipe.user_id.
However, when I put the pry under creator and call creator I get the user_id of 1 and I get John. I believe something is wrong with how I am trying to find the user using the user_id in #recipe. I was wondering if anyone know what I am doing wrong or if I need to add more information. Thanks.
This:
User.find_by(params[#recipe.user_id])
Doesn't make sense for a couple of reasons:
find_by expects a hash-like structure. Something like: User.find_by(id: xxx)
params[#recipe.user_id] doesn't make sense because that's going to be something like: params[1] which is not what you want.
This:
#recipe = Recipe.find_by(params[:id])
Also suffers from the malformed find_by.
So, try something like:
def show
#user = current_user
#recipe = Recipe.find(params[:id])
creator = #recipe.user
#creator = creator.first_name
end
This, naturally, assumes you have your association between Receipt and User set up correctly (i.e., using belongs_to).
I am trying to use ThinkingSphinx (with SQL-backed indices) in my Rails 5 project.
I need some dynamic run-time indices to search over.
I have a Message model:
class Message < ApplicationRecord
belongs_to :sender, class_name: 'User', :inverse_of => :messages
belongs_to :recipient, class_name: 'User', :inverse_of => :messages
end
and its indexer:
ThinkingSphinx::Index.define :message, :with => :active_record, :delta => true do
indexes text
indexes sender.email, :as => :sender_email, :sortable => true
indexes recipient.email, :as => :recipient_email, :sortable => true
indexes [sender.email, recipient.email], :as => :messager_email, :sortable => true
has sender_id, created_at, updated_at
has recipient_id
end
schema.rb:
create_table "messages", force: :cascade do |t|
t.integer "sender_id"
t.integer "recipient_id"
t.text "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "read", default: false
t.boolean "spam", default: false
t.boolean "delta", default: true, null: false
t.index ["recipient_id"], name: "index_messages_on_recipient_id", using: :btree
t.index ["sender_id"], name: "index_messages_on_sender_id", using: :btree
end
The problem is about so-called "dialogs". They don't exist in the database - they are determined at run-time. A dialog - that's a set of messages between 2 users, where each user may be either a sender or a receiver.
The task is to search through my dialogs and to find the dialog (dialog's messages) by the piece of the correspondent email. So complicated!
Here's my effort:
conditions = {messager_email: search_email}
with_current_user_dialogs =
"*, IF(sender_id = #{current_user.id} OR recipient_id = #{current_user.id}, 1, 0) AS current_user_dialogs"
messages = Message.search search_email, conditions: conditions,
select: with_current_user_dialogs,
with: {'current_user_dialogs' => 1}
This is almost fine - but still not. This query correctly searches only within my dialog (within the messages I sent or received) and only within :sender and :recipient fields simultaneously (which is not best).
Say my email is "client1#example.com". Other emails are like "client2#example.com", "client3#example.com", "manager1#example.com".
The trouble is that when I search for "client1" - I get all the messages where I was either a sender or a receiver. But I should get nothing in response - I need to search only across my correspondents emails - not mine.
Even worse stuff happens also while querying "client" - I get back the correct correspondents with "client2#example.com", "client3#example.com" - but the result is spoiled with wrong "client1#example.com".
I need a way to choose at run-time - which index subset to search within.
I mean this condition is not enough for me:
indexes [sender.email, recipient.email], :as => :messager_email, :sortable => true
It searches (for "client") within all the sender.email and all the recipient.email at once.
But I need to dynamically choose like: "search only within sender.email values conforming to if sender.id != current_user.id" OR "search only within recipient.email conforming to if recipient.id != current_user.id" (because I can be as a sender as a receiver).
That's what I call a "dynamic index".
How to do that? Such "dynamic index" surely would depend on the current current_user value - so it will be different for the different users - even on the same total messages set.
It is clear that I can't apply whatever post-search cut-offs (what to cut off?) - I need to somehow limitate the search itself.
I tried to search over some scope - but got the error that "searching is impossible over scopes" - something like that.
Maybe I should use the real-time indexing instead of the SQL-backed indexing?
Sorry for the complexity of my question.
Would the following work?
other = User.find_by :email => search_email
with_current_user_dialogs = "*, IF((sender_id = #{current_user.id} AND recipient_id = #{other.id}) OR (recipient_id = #{current_user.id} AND sender_id = #{other.id}), 1, 0) AS current_user_dialogs"
Or do you need partial matches on the searched email address?
[EDIT]
Okay, from the discussion in the comments below, it's clear that the field data is critical. While you can construct a search query that uses both fields and attributes, you can't have logic in the query that combines the two. That is, you can't say: "Search field 'x' when attribute 'i' is 1, otherwise search field 'y'."
The only way I can possibly see this working is if you're using fields for both parts of the logic. Perhaps something like the following:
current_user_email = "\"" + current_user.email + "\""
Message.search(
"(#sender_email #{current_user_email} #recipient_email #{search_email}) | (#sender_email #{search_email} #recipient_email #{current_user_email})"
)
I am trying to populate a new table from an existing database but my method does not seem to be working properly. Below is my code.
class CreateEmployees < ActiveRecord::Migration
def change
create_table :employees do |t|
t.string :first_name, null: false
t.string :last_name, null: false
t.string :email, null: false
t.timestamps
end
Sale.find_each do |sale|
unless Employee.exists?(sale.employee)
puts "Employee #{sale.employee} created!"
else
puts "Employee #{sale.employee} already existed!"
end
employee_info = sale.employee.split
Employee.find_or_create_by(first_name: employee_info[0], last_name: employee_info[1], email:employee_info[2])
end
end
end
What I have is a main database called sales that with a field that contains employee. In that field you will find a string entry as so: "Mary Higgins higgins#korning.com".
Basically the sales database contains four distinct employees but the employees are listed many times. What I'm trying to do is to create four unique rows. I thought the code above would work but something seems to be off with my logic. When I run the above code it, goes through the n amount of rows and creates the Employee object so, essentially the unless statement never results to true for some reason. Could the problem lie in the .find_each method. Would a .each suffice? I don't know if any more information would need to be provided with my database but if its needed I'll supply more details.
sale.employee is a string eg "Mary Higgins higgins#korning.com"
exists? excepts a hash with the conditions like Employee.exists?(:email => "higgins#korning.com"). If you pass a string like you did, first, it converts the string to an integer then tries to find the record with that id which in your case will be 0 and because of that it always returns false.
I would change the find_each loop like this:
Sale.find_each do |sale|
employee_info = sale.employee.split
employee = Employee.find_or_create_by(first_name: employee_info[0], last_name: employee_info[1], email:employee_info[2])
if employee.new_record?
puts "Employee #{sale.employee} created!"
else
puts "Employee #{sale.employee} already existed!"
end
end