Mongoid Embedded Documents Adding / Removing With Minimum Hits to the Database - ruby-on-rails

I have a a model "Entry" that has many items "Items" that are embedded documents:
class Entry
embeds_many :items, cascade_callbacks: true
...
end
the issues is i have to move a bunch of embedded document Items around deleting some, add others, and moving others between Entrys. It seems like any operation I do on an Entry.items like:
entry.items << item or entry.items.delete(i)
causes its own database write. And if i'm making many changes that seems very expensive. Is there a way to tell mongoid to let me add items, remove them, move them locally and only when everything is done send a single entry.save! write to the database?

Replacing the items array by doing:
entry.items = new_items
Is the most database efficient. But it turns out is buggy, make sure you have the latest version of mongoid and do an entry.save if entry.changed? || entry.new_record? because it occasionally wont save entry above when you modify the items.

Related

Rails, locking record in database

I'm trying to lock one of the records in my database when seeding (using API). By locking I mean- not being able to create topics under certain movie or just disable 'show' method.
It'll be simpler if I'll just show you my seeds.rb file:
require 'open-uri'
#doc=Nokogiri::XML(open("http://www.kinoballada.info/repertuar/export/small/dzien/xml"))
movie_array = []
#doc.css('dzien').each do |node|
children=node.children
movie_array << children.css('tytul').inner_text
Movie.find_or_create_by(
:name => children.css('tytul').inner_text
)
end
movies_to_delete = Movie.where.not(name: movie_array)
movies_to_delete.destroy_all
Last 2 rows are essential- I want to LOCK the movie, not destroy it, making something like:
movies_to_lock = Movie.where.not(name: movie_array)
movies_to_lock.??????_all
Is there any way I can do this?
The easiest way to do what you want is to issue a select for update command. Once you select a set of rows, they'll be locked for other threads until you do something that causes the database to release the lock.
Not every RDBMS supports the command, and some older databases will lock the entire table when you select from one of its rows. You'll probably want an RDBMS agnostic solution from the application side that avoids such problems, preserving the freedom to switch databases in the future without additional worries.
Consider adding a boolean column called locked to your table which you can read before allowing the row to be included in any result set. This approach seems to come at a minimal expense, while allowing you to avoid database specific problems.

Sunspot Gem Using in STI TABLE

i have Account Model,Asset, Capital and Revenue this table are all inherited in my Account model. i have 3 kind of attributes in my Account model. name, code and type. when i create an account where will be to insert will happen one in my account and the other one is in my type for example
Account.create(name: "test123", code:"test123", type:"Asset")
sql will run Two Insert one for Account model and one for Asset Table
and my sunspot work well it will reindex my database and i can search my params
but when i update my model Account my sql run one insert and one update
my question is how can i reindex my model when i update. with a particular data. i can do Sunspot.reindex but this is will load all data in my sql. that will cause me to slow
sql will run Two Insert one for Account model and one for Asset Table
FYI you use STI when you want to share same database table between multiple models because they are similar in attributes and behavior. Like AdminUser model is likely to have almost same attributes/columns as PublisherUser or ReaderUser. Therefore you might wish to have a common table called users or model User and share this table among the above mentioned models.
Point is: ActiveRecord will run a single SQL query not two, like:
INSERT INTO "accounts" ("name", "code", "type") VALUES ('test123', 'test123', 'Asset')
my question is how can i reindex my model when i update. with a particular data. i can do Sunspot.reindex but this is will load all data in my sql. that will cause me to slow
Actually sunspot_rails is designed to auto-reindex whenever you make changes to your model/record. It listens to the save callbacks.
But you need to make sure that you are not using methods like update_column(s). See the list of silent create/update methods which do not trigger callbacks and validations at all.
In addition, you need to understand the concept of batch size in terms of Solr. For performance reasons, all of your new indexes are not immediately committed. Committed means, writing indexes to database like in RDBMS commits.
By default the batch_size for commits is 50. Meaning after 50 index method executions only the indexes will be committed and you will be able to search the records. To change it, use following
# in config/initializers/sunspot_config.rb
Sunspot.config.indexing.default_batch_size = 1 # or any number
or
# in models; its not considered good though
after_commit do
Sunspot.commit
end
For manual re-indexing, you can use like #Kathryn suggested.
But, I don't think you need to intervene in the auto-operation. I think you were not seeing immediate results so you were worrying.
According to the documentation, objects will be indexed automatically if you are on Rails. But it also mentions you can reindex a class manually:
Account.reindex
Sunspot.commit
It also suggests using Sunspot.index on individual objects.
i put this to my model
after_update do
Sunspot.index Account.where(id: self.id)
end

Rails acts_as_soft_deletable with has_and_belongs_to_many

I have a problem with the acts_as_soft_deletable plugin and a has_and_belongs_to_many relationship.
I have a model "Place" which has a couple of Categories (like restaurant, hotel, etc). This means that a table "places_categories" is created in the database, containing two columns "place", and "category".
When I destroy a place, it is placed in the table "deleted_places" by acts_as_soft_deletable. Then I try to restore it and the application crashes because a place cannot exist without categories. The entries in "places_categories" that stored which categories the place belonged to are deleted as the place is deleted.
How can I make sure that "places_categories" does not remove the relations when a place is moved to the "deleted_places" table?
Since there is no option to explicitly preserve those entries - you could do something crazy and stupid and just override the delete_sql option to an empty String or somethings thats not going to fail on the "database-side" like so:
class Place
has_and_belongs_to_many :categories, :delete_sql => "select true"
end
This is untested ! Just an idea.
You can read about all available options here.

How to export a nested ActiveRecord record to a document that can be reimported to update?

I'm trying to turn a database record into an exportable document, such that I can reimport it and update the database. I'd just use CSV but it's also nested, say Country has_many Provinces has_many Cities.
I've tried dumping YAML per this earlier question:
File.open("#{RAILS_ROOT}/lib/tasks/canada.yml", 'w') do |file|
country = Country.find(1)
country.provinces
country.cities
YAML::dump(country, file)
end
But when I load, it doesn't:
country = YAML.load_file("#{RAILS_ROOT}/lib/tasks/canada.yml")
I even tried hacking the new_record flag per this article but it doesn't change anything. I'm using Rails 3.x.
This must be something other people have done before. How? Or do I need to approach the problem differently?
I guess the problem is that country.save will not produce a new record in the database? You can try Country.create!(country.serializable_hash). That will at least create a new record based on the attributes of country. The country id will be changed and I guess nested objects (Province, City) will not be handled properly.

Calling ActiveRecord's #relationship_ids = [1,2,3] saves immediately. Any workarounds?

I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.

Resources