Rails mongoid insert_many and associate with model - ruby-on-rails

I'm inserting many records into a rails mongoid ds as such:
products = [{id: "123"},{id: "345"}]
products.each do |product|
product['product_id'] = product.delete 'id'
end
#store = current_user.store
# Clear the existing collection
#store.products.destroy_all
#store.products.collection.insert_many(products)
This works beautifully, however, the records entered into Owner are not associated with the Owner.
Examining a product, I can see that the field owner_id is nil.
I see that https://www.rubydoc.info/gems/mongo/Mongo%2FCollection:insert_many has the options hash. Is there a way to associate the records entered into the Owner when inserting them via the options. Would you do it before somehow? How do I associate each product entered into the Owner with the Owner?

This is a driver-level operation:
#store.products.collection.insert_many(products)
The driver only inserts the data you tell it to insert, i.e. only the keys/values in products. The driver does not have any knowledge of Mongoid associations or any other Mongoid features.
To associate products with their store, set store_id on each product accordingly:
products = [{id: "123", store_id: 1},{id: "345", store_id: 2}]

Related

how to use sunspot/solr to order by inclusion in an associated field

I am using rails 4 with sunspot which uses solr and I am trying to order a model by the field value of an associated model.
I have a number and the number has_many favorites associated to it (multiple users can favorite the number).
Each favorite stores a user_id.
I want to order the numbers where the user_id of an associated favorite matches the current_user.id.
something like order('number.favorites.user_id', current_user.id)
I have tried to index an array of favorites.user_ids in the number searchable boolean(:favorite_user_ids) { favorites.pluck(:user_id) }
but now I'm trying to think of how to order by inclusion in that indexed array.
For example: "order where the current_user.id matches an id in the number.favorite_user_ids array.
leftJoin strategy:
I also found leftOuterJoin directly in solr https://solr.apache.org/guide/7_2/stream-decorator-reference.html#leftouterjoin
I think if I do a left outer join where I use on current_user.id = favorite.user_id then after I can order by user_id

Rails - Passing a specific column to query by count of an associated model

I have a rails app with "recipes" model. Recipe can be liked ("like" model) by a user. What I'd like to achieve (eventually) is to sort the list of the recipes on the recipes-index page by the number of likes.
I've seen numerous threads but the two that guided me the most were:
Rails 3 ActiveRecord: order and group by association count
Rails order by results count of has_many association
I've got so far that I have an SQL-query that produces list of the recipes with the count of their likes:
SELECT recipes.name, COUNT(likes.id)
FROM recipes
LEFT JOIN likes on recipes.id = likes.recipe_id
GROUP BY recipes.name
ORDER BY COUNT(likes.id) DESC;
However, when "translating" it to something ActiveRecord can understand, I'm running into troubles.
I've concluded that using Recipe.joins(:likes) or Recipe.left_joins(:likes) passes all columns specified in "recipes", which leads to the following error:
"Column "recipes.id" must appear in the GROUP BY clause or be used in an aggregate function...".
Now, as I need to pass recipes.name and COUNT(likes.id) only, I used select:
Recipe.select("recipes.name, COUNT(likes.id)").joins(:likes).group("recipes.name").order("COUNT(likes.id) DESC")
And that's where I'm stranded as it produces this:
ActiveRecord::Relation [Recipe id: nil, name: "Scrambled eggz", Recipe id: nil, name: "Soup", Recipe id: nil, name: "Nutella pancakes"]
Recipe.joins("LEFT JOIN likes ON recipes.id = likes. recipe_id").group("recipes.id").order("COUNT(likes.id) ASC")
ActiveRecord parsing expects the returned resultset to have the columns that the original model has(Or a subset of columns).
When selecting a subset of the original columns, ActiveRecord nullifies all the other columns while parsing it in an ActiveRecord object/relation
This may help you if you want to it in active record query:
Recipe.left_joins(:likes).group(:id).order("COUNT(likes.id) DESC")
Warning for the JOIN:
Recipe.joins(:likes).group(:id).order("COUNT(likes.id) DESC")
This query will only return the recipes which have likes. If there are any recipes that don't have any likes, doing JOINS won't get you those.
Your query is correct. Please note that it use Recipe model class, which does not contains your custom field in its Class. If your query is
Recipe.select("COUNT(likes.id)").joins(:likes).group("recipes.name").order("COUNT(likes.id) DESC")
It will show you
ActiveRecord::Relation [Recipe id: nil, Recipe id: nil, Recipe id: nil]
However, You still can access your custom fields, the returned data hold it for you to use. You just need to give it a name to call it. Also, You can use that custom field to order, please don't direct order COUNT
recipes = Recipe.select("COUNT(likes.id) as total_likes").joins(:likes).group("recipes.name").order("total_likes DESC")
recipe.first.total_likes # TOTAL LIKE of first recipe

Update an array in rails

I am building up an array of products in rails. Which is working fine, but my question is...
Is there any way to update an item if it exists in the array already? So as I am looping through products, and the model is "TV-32D300B" I need to check the array to see if it exists, but it may only be a partial number like "TV-32D300" (minus the last letter).
If the is the case I want to be able to update that product with the correct details.
product = {
name: product_name,
url: product_url,
modelnumber: product_modelnumber,
category_id: category.id,
group_id: category.group_id,
image_url: image_url
}
I'm using the include? to add products to the array if a product doesn't already exist, so I am guessing I need to add a like condition to find the number.
unless products.include?(product)
products << product
end
Assuming products is an array of hashes and what you call a model is a product_name held under name key in this hash, the following would do:
existing = products.find { |p| product[:name].include? p[:name] }
if existing
# update existing
else
products << product
end
More info on Enumerable#find.

Can i cache a parent in rails?

I have this code:
Business.all.limit(50).each do |business|
card = {name: business.name, logo: business.logo, category: business.category.name}
feed << card
end
In my models, Business belongs to Category, and Category has many Business
My problem is that this will query the DB 50 times, each time I want to retrieve each business' category name.
I have seen Rails cache effectively by using :include, but all examples I have seen are for child records, for example:
Category.all :include => [:businesses]
but in this case I want to cache the parent's data.
Its the same you can do by using singular model name
Business.includes(:category)

Selecting multiple documents using a criteria in mongoid

I am new to mongodb and mongoid. I am used to ActiveRecord/mysql in Ruby
on Rails so pardon my ignorance.
In ActiveRecord world if I want to search for all records meeting a certain
criteria (students from a specific zipcode), I could use
students = Student.where(zipcode: "12345")
and this would return an array of students.
Using Mongoid, if I query
Student.all.where(zipcode: "12345")
it just returns a criteria and I have to use an iterator like
students = []
Student.all.where(zipcode: "12345").each { |s| students << s }
Is there a better way to do a Mongoid /Mongo query to get all the documents
meeting the search criteria without using the ruby iterators (.each) ?
I have been referring to the mongoid document from
https://docs.mongodb.org/ecosystem/tutorial/mongoid-queries/
and couldn't find an example to get all the documents in one query.
You're being fooled by the console if you think:
students = Student.where(zipcode: "12345")
gives you an array of Students in students. That actually gives you a relation object in students but the relation's inspect (which is called by the console) will load the records from the database behind your back; similarly, as soon as you try to do anything array-ish with a relation, it will load records from the database.
Mongoid's where behaves the same but it won't necessary resolve to model instances in all the same cases that an ActiveRecord relation will.
The easiest thing to do (with both ActiveRecord and Mongoid) is to call to_a on the relation/criteria if you really want an array:
# ActiveRecord
students = Student.where(zipcode: "12345") # students is a relation
students = Student.where(zipcode: "12345").all # students is still a relation
students = Student.where(zipcode: "12345").to_a # students is now a real array
# Mongoid
students = Student.where(zipcode: "12345") # students is a criteria
students = Student.where(zipcode: "12345").all # students is still a criteria
students = Student.where(zipcode: "12345").to_a # students is now a real array
In both cases, if you want an array then just say so using to_a.

Resources