perfectmemory / mongoid-multitenancy Goto Github PK
View Code? Open in Web Editor NEWAdd Multitenancy to your models for Mongoid 6+ (6, 7, 8, 9)
License: MIT License
Add Multitenancy to your models for Mongoid 6+ (6, 7, 8, 9)
License: MIT License
We have a user model as -
class User
include Mongoid::Document
include Mongoid::Multitenancy::Document
tenant :organization
def root_admin?
HierarchyNode.where(organization: organization, parent_node_id: nil).first
end
end
When multiple users from different organizations are logged in then sometimes this query is adding other user's organization filter as well in where query with "$and" which should not be the case. But sometimes it works as expected.
#<Mongoid::Criteria selector: {"organization_id"=>BSON::ObjectId('62cffd3166b207d96a47de76'), "is_deleted"=>{"$ne"=>true}, "$and"=>[{"organization_id"=>BSON::ObjectId('559a23c269702d43a900002f')}], "parent_node_id"=>nil} options: {} class: HierarchyNode embedded: false>
Hi, there
we have a advance manager role
wants review multiple tenants data(not all tenants), can we set multiple tenants like Mongoid::Multitenancy.current_tenant = [tenant1, tenant2]
Hi, there
We got N+1 query issue because of the code below after upgraded from 0.4.x to 1.0+:
after_initialize lambda {
if Multitenancy.current_tenant && send(association.to_sym).nil? && new_record?
send "#{association}=".to_sym, Multitenancy.current_tenant
end
}
There is one more query when running send(association.to_sym).nil?, and it seems not working even use eager loading query in mongoid like:
Person.includes(:address).all.to_a
My guess is :after_initialize has some conflicts with eager loading, any thought on this?
Currently, if current_tenant is set, all records, whether newly created ones or already persisted ones, will have tenant set to current_tenant. Is this the indented behavior? This is especially strange if optional is set to true..
Full text indexes, now supported in the v4 mongoid master branch, cannot have a multi-key as a prefix to the text index. Instead they use a "filter" parameter to achieve the same effect (automatically handled by mongoid v4 when scoped). The mongoid-multitenancy gem should therefore ignore indexes of type "text". This can be achieved by changing line 61 of document.rb to:
spec = { self.tenant_field => 1 }.merge(spec) unless spec.inject(nil) {|result,v| result = result || v.last == 'text' }
I created a fork that contains this change along with a few other minor tweaks to work with mongoid v4 (plus a few tests). Not sure if you'd like a pull request, or if you're already planning a v4 branch.
Consider 2 clients client_instance_one
and client_instance_two
, Article have tenant with optional option for keep possibility to create Article without tenant.
class Article
include Mongoid::Document
include Mongoid::Multitenancy::Document
tenant(:client, optional: true)
field :title, :type => String
end
Create 1 Article belong to first client like your example in the README
Mongoid::Multitenancy.with_tenant(client_instance) do
article = Article.new(:title => 'New article 1')
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', client_id: nil>
# tenant needs to be set manually
article.tenant = client_instance_one
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', client_id: 50ca04b86c82bfc125000025>
end
We have following expected behavior
Mongoid::Multitenancy.current_tenant = client_instance_one
Article.all.map(&:title) # => ['New article 1']
Create a new article without tenant
Mongoid::Multitenancy.current_tenant = nil
article = Article.new(:title => 'New article 2')
article.save
Article.all.map(&:title) # => ['New article 1', 'New article 2']
First tenant list this articles
Mongoid::Multitenancy.current_tenant = client_instance_one
Article.all.map(&:title) # => ['New article 1', 'New article 2']
This is strange behavior for me, but can be ok so far. We will assign a tenant to the second article
Mongoid::Multitenancy.current_tenant = client_instance_two
article.tenant = client_instance_two
article.save
Article.all.map(&:title) # => ['New article 2']
but still visible with the scope of previous client
Mongoid::Multitenancy.current_tenant = client_instance_one
Article.all.map(&:title) # => ['New article 1', 'New article 2']
This is really what we are excepted? With this behavior it's impossible to delegate later the assignment of resource like creation of user and scoping them after for avoid patching of devise for example...
Is there a preference to use RequestStore vs. Thread? I saw the recent change in act_as_tenant. Not sure if it makes sense for mongoid-multitenancy.
Optional tenant models takes the current tenant id after initialize. The current tenant affectation should take place before the validation instead of after the initialize.
Furthermore a optional tenant model with a current tenant id set to nil
should not be updated.
In unicorn the Thread.Current is not cleared between requests. So you could end up having a tenant set when you don't want it set.
The acts_as_tenant had the same issue: ErwinM/acts_as_tenant#34
At the very least the dependancies need to be updated. Not sure if Mongoid 4 is GA enough to do so at this time. Perhaps a branch would be best initially.
Right now if you use Rails 4 and Mongoid 4 you get bundle errors. I have done a clone and removed the offending dependancies and will test using my local bundle.
Hi and thanks for that great gem.
I just did an update of mongoid from a 3.0 version to 3.1.4. After that my Rails App refuses to start. In the trace I've found this:
undefined method 'to_sym' for nil:NilClass
...
[...]mongoid-multitenancy-0.3.2/lib/mongoid/multitenancy/document.rb:57:in 'index'
This happens for the index
method on inherited models where I declare the tenant in the parent-model like this:
class Animal
include Mongoid::Document
include Mongoid::Multitenancy::Document
tenant :client
end
class Cat < Animal
include Mongoid::Document
include Mongoid::Multitenancy::Document
field :name, type: String
index 'name' => 1
end
It seems that the index
method is called before tenant
.
As per the README:
# All searches are scoped by the tenant
Article.all # => all articles where client_id => 50ca04b86c82bfc125000025
But the tenant
method never creates an index for the association (if I run rake db:mongoid:create_indexes, no indexes get created for client_id
). Won't the absence of this index effect the performance of the Article.all
kind of queries
Moreover, if the tenant call be appended with the option index: true
it results in a NoMethodError
. I am guessing this happens because the index method is overridden (though I am not sure). Following is the error trace:
~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/indexes.rb:131:in `block in normalize_spec': undefined method `to_sym' for nil:NilClass (NoMethodError)
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/indexes.rb:130:in `each'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/indexes.rb:130:in `inject'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/indexes.rb:130:in `normalize_spec'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/indexes.rb:90:in `index'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-multitenancy-0.4.0/lib/mongoid/multitenancy/document.rb:68:in `index'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/relations/macros.rb:316:in `reference'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/relations/macros.rb:338:in `reference_one_to_one'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-3.1.6/lib/mongoid/relations/macros.rb:141:in `belongs_to'
from ~/.rvm/gems/ruby-1.9.3-p286/gems/mongoid-multitenancy-0.4.0/lib/mongoid/multitenancy/document.rb:14:in `tenant'
As a workaround I added the following line in the tenant models to ensure that index be created for the client_id field
index({}, {background: true})
We are looking for a multi tenancy gem supporting mongoid. Your gem looks very promising. Is it still under active development? How reality-proven is it?
Thanks. Josh
Rails comes with default required for belongs_to relationship. This gem does not cater to this one.
There is 'optional' option in 'belongs_to' relationship but it is taken as MULTITENANCY_OPTIONS in current implementation, need to change current implementation.
When defining a tenant for a model, an option should be added to make the ownership optional. It is very practical when we want some objects to be shared between all the clients.
I use devise gem. There are Users::Base and Users::SuperAdmin < Users::Base.
Tenant defined in Users::Base class, like this: tenant :tenant, class_name: 'School', optional: true
.
And when i try to create admin without tenant errors appear: admin.errors => #<ActiveModel::Errors:0x000055d12b92a4f8 @base= #<Housefactions::Users::SuperAdmin _id: BSON::ObjectId('5f046730a55b358791d33176'), age: nil, avatar_content_type: nil, avatar_file_name: nil, avatar_file_size: nil, avatar_fingerprint: nil, avatar_updated_at: nil, created_at: nil, email: "[email protected]", first_name: "Super", last_name: "Admin", number: nil, registration_ip: nil, sex: nil, tenant_id: nil, updated_at: nil>, @details={:tenant=>[{:error=>:blank}, {:error=>:blank}]}, @messages={:tenant=>["can't be blank", "can't be blank"]}>
Found an issue in using MongoId 5:
mongoid/multitenancy.rb:11:
def mongoid4?
MongoId::VERSION.start_with? '4'
end
I change the number to '5', it works well with mongoid 5. but I didn't test all things.
Could you update this GEM to work with MongoId 5?
RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.
via e.g.
spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']
Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.
There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.
I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!
Appendix:
If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies
p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.
A compound index with the tenant id is not always the best index, usually when a complex query plan is used. An example would be a query with $or
, where the query can benefit from individual indexes and the compound index would be less useful.
Currently this is not possible with this gem, as it just overrides the index
method. This could be solved with an option or by explicitly including this feature from a separate module.
What do you think?
Hello,
In a class that has tenant model;
I use other relations belongs_to
has_many
. Can you please add examples how to use ;
:class_name, etc. : all the other options will be passed to the mongoid relation (belongs_to)
I am having a weird issue with Devise and mongoid-multitenancy where the current tenant seems to get stuck for my user
devise model.
class User
include Mongoid::Document
include Mongoid::Multitenancy::Document
tenant(:tenant)
...
end
When I sign in on my public
tenant and I inspect the resulting object, it looks as though it is trying to connect to my test
tenant instead of my public
tenant. However, there are times where this is reversed and my test
tenant is trying to connect to my public
tenant. I can generally force this to happen if I purposefully throw an error on one of the tenants during sign in.
Do I need to set the tenant somewhere in Devise to ensure that it is actually getting the right user? I have noticed that when it gets stuck on the wrong tenant, I can no longer sign in as a user for the current tenant.
Also, if a user exists for both tenants with different passwords, I still get a "signed in successfully" notification if i use the password of the other tenant. However, I am not actually signed in because I can't access any resources.
Bundler could not find compatible versions for gem "mongoid":
In snapshot (Gemfile.lock):
mongoid (= 7.0.1)
In Gemfile:
mongoid (~> 7.0)
mongoid-multitenancy (~> 2.0) was resolved to 2.0.1, which depends on
mongoid (~> 6.0)
Hi,
I installed 'mongoid-multitenancy' on an empty project to test it before using it on the real application. In my Articles page I'm receiving "uninitialized constant Tenant" error.
def index
@articles = Article.all
end
Rails: 5.1.3, mongoid: 6.2.0
Any idea why would this be?
Bundler reports that the mongoid-multitenancy version 2.0.2 is not compatible with mongoid 7.1:
Bundler could not find compatible versions for gem "mongoid":
In Gemfile:
mongoid (~> 7.1)
mongoid-multitenancy (~> 2.0.2) was resolved to 2.0.2, which depends on
mongoid (< 8, ~> 6)
I want to delete all models all record related to specific client when I delete any client. Can this be possible in single client destroy command. if not, Is there any workaround for the same.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.