how to overwrite embeds_many in inherited model in Mongoid? - ruby-on-rails

I am using mongoid in Rails 3 and I came across this problem:
Let's say I have a Shape model:
class Shape
include Mongoid::Document
field :x, type: Integer
field :y, type: Integer
embedded_in :canvas
end
And a Canvas model (has many Shapes):
class Canvas
include Mongoid::Document
field :name, type: String
embeds_many :shapes
end
Then a Canvas model "has many Shapes".
I have Browser model inherited from Canvas:
class Browser < Canvas
field :version, type: Integer
end
Then Browswer model should "has many Shapes" now.
But, now I have a "Circle" model inherited from Shape:
class Circle < Shape
field :radius, type: Float
end
And I want to let Browser model to "has many Circles" instead of "has many Shapes". That is to say, I want to overwrite the "has many" relationship in Browser model from "has many Shapes" to "has many Circles".
How should I do it?

I'm not 100% sure, but I think you would just add the line for embeds_many :circles to the Browser model. You wouldn't need to remove the inherited relation.
Since Circle inherits from Shape, circles will get stored in an array stored in the "shapes" key in the Browser document anyway, they'll just have their _type attribute set to "Circle". In other words, having the embeds_many :shapes relation doesn't create anything in the DB that embedding many circles wouldn't create anyway.
It will, however, mean that you have methods such as Browser.frist.shapes available, but you can simply ignore these. Adding the embeds_many :circles will give you the methods for that relation, such as Browser.first.circles.

Related

Neo4j rb inheritance

I am using Neo4j.rb in my rails app.
I am having trouble with polymorphism.
I have something like this I have a Users class which looks like this:
class User
include Neo4j::ActiveNode
#other properties
has_many: out, :social_media_content, model_class: :SocialMediaContent
end
I have the SocialMediaContent class
class SocialMediaContent
include Neo4j::ActiveNode # Is it necessary?
property :first_name, type: Integer
property :first_name, type: Integer
end
I want to have an image class and a video class which inherit from Social media content
Does this mean that while creating a relationship between SocialMediaContent and a User i can provide an image or a video object instead of SocialMediaContent, How does it happen in the database, do i need to have a node for the image as well or can it be an ordinary object.
i.e
class Image < SocialMediaContent
include Neo4j::ActiveNode #Do i have to do this?
I want a behaviour like this: every User has many SocialMediaContent which can be either an image or a video (for now) i do not want to specify now, which one of them is an image and which is a video.
Can anyone suggest how i might acheieve this?
And finally can i store an array of SocialMediaContent instead of has_many associations (which is better?)
Yes, you can definitely do this. In Neo4j.rb when you inherit a class from an ActiveNode class then the child class represents nodes with both labels. For example:
class SocialMediaContent
include Neo4j::ActiveNode # This is necessary for the parent
end
class Image < SocialMediaContent
# No include needed
end
class Video < SocialMediaContent
# No include needed
end
Now if you do Image.create it will create a node which has both the Image and SocialMediaContent labels. If you search for an image with Image.find, Image.where, etc... it will limit to only nodes with both labels.
As for the association, you should be able to specify it as you have it in the question (though it's going to complain if you don't have a type, rel_class, or origin option).

Querying mongoid for objects with array containing specific values

I'm trying to retrieve only books where only authors in favorite_authors are among the authors for the book. (note: not an exact match, but an exclusion of all books with any authors other than those in favorite_authors).
favorite_authors = User.favorite_authors
=> [BSON::ObjectId('5363c73c4d61635257516000'),
BSON::ObjectId('5363c73c4d61635257516001'),
BSON::ObjectId('5363c73c4d61635257516002'),
BSON::ObjectId('5363c73c4d61635257516003'),
BSON::ObjectId('5363c73c4d61635257516004'),
BSON::ObjectId('5363c73c4d61635257516005'),
BSON::ObjectId('5363c73c4d61635257516006')]
class Author
include Mongoid::Document
field :name, type: String
class Book
include Mongoid::Document
field :name, type: String
field :authors, type: Array
Some naive pseudo code to give you an idea of what I'd like to do:
Book.where(authors: relevant_authors).not.where(authors: !relevant_authors)
Ideally, I'd like to accomplish this using a Mongoid Origin query.

Modeling product variations in Rails3/Mongoid with an embeds_many / embedded_in relation....?

I know this is a (somewhat) common question but I'm a newbie and I always appreciate a second (or 100th) pair of eyes on a problem I'm been noodling.
I'm wondering if I'm taking the right approach here -- I'm trying to model a product with several variations and the associated gift certificates sold against that product.
A product, for example, for a restaurant might be a "tasting menu for 2" whereas variations might be "food only", "with partial wine pairing", and "with full wine pairing", etc. (just an illustrative example).
Here's a distilled version of what I've come up with:
class Product
include Mongoid::Document
field :name, type: String
has_many :gift_certificates
embeds_many :variations
end
class Variation
include Mongoid::Document
field :name, type: String
embedded_in :product
has_many :gift_certificates
end
class GiftCertificate
include Mongoid::Document
field :recipient, type: String
field :amount, type: Float
belongs_to :product
belongs_to :variation
end
So when we create a GiftCertificate, it's defined as belonging to both a product and a variation (is that correct?). An example might be a "$200 gift certificate for tasting menu for 2 [product] with partial wine pairing [variation]", etc.
I feel like the embeds_many / embedded_in relation here for Products/Variations is ideal as it fits the profile for when it should be used: I'll never be accessing a product without its variation, and I never need to access a variation independant of a product -- they go hand-in-hand.
So the question is whether I'm doing this the right way.
I've managed to massage the basic CRUD forms into working using ryanb's excellent nested_form gem, with a bit of tweaking. Editing the Products & Variations are easy (the basic use case), but the trouble I'm having comes when working with the Gift Certificates.
Mongoid is giving me a bit of trouble ... the GiftCertificate belonging to both a product and its variation seems kludgy, and I have to manually set the GiftCertificate.variation each time I do a find, i.e.
#gc = GiftCertificate.find(params[:id]) // as normal
#gc.variation // ==> nil, doesn't work by itself
#gc.variation = #gc.product.variations.find(#gc.variation_id) // Have to manually set this
#gc.variation // Now this works as it's supposed to
Anyway, before I go on -- would love to get some thoughts on the above.
Thanks in advance!

Full text search in embedded one to many, MongoID child docs

I have a MongoID User model
class User
include Mongoid::Document
field :name
embeds_many :watchlists
end
which embedded one to many
class Watchlist
include Mongoid::Document
field :description
embedded_in :user
end
I want to search full text into :description field which is in Watchlist child embedded in User. The most heavy search goes through 1.5k descriptions, with a max of 30, 40 words each.
The constrain here, is that I'll deploy on Heroku and they have not free plan for indexing currently.
Then I've tried with mongoid_fulltext (as well as mongoid_search and mongoid-searchable) but without success.
Does anybody have an idea how to do that ?
UPDATE:
This is an example of mongoid_fulltext. The User model embeds many Watchlist(s). I'm searcing a string in :description field, which is in Watchlist child doc :
class Watchlist
include Mongoid::Document
field :description
...
embedded_in :user
end
Watchlist is embedded in User :
class User
include Mongoid::Document
include Mongoid::FullTextSearch
field :name
...
embeds_many :watchlists
def search_in_description
self.watchlists.map{ |w| w.description }.join(' ')
end
fulltext_search_in :search_in_description
end
... but in that way, running User.fulltext_search("a presentation framework based on the power of CSS3") gives me back just the parent doc causing the match (a user instance) and not the watchlists doc ( the child instance ).
See the output: http://pastie.org/3226179
How can I get just the matching "watchlists"? ( I tryed few ways without success )
In MongoDB you cannot query directly for embedded documents. You can query they way you explained in the example to get the parent document, then query on the returned top document user for the desired embedded document among current user watchlists.
If you're going to do that frequently, perhaps you might want to consider using relational has_many rather than embeds_many.

Relations with mongoid, what should i use?

I'm using Ruby on Rails 3.1 with mongoid and trying to set up som rather simple relations between posts, comments, users and tags.
I'm very new to mongodb, and no-sql in general so I'm a bit confused.
What I am trying to accomplish is this:
Users, posts and comments should be able to have multiple tags.
Tags should have name, type and a count of how many times it has been used.
I need to be able to get all available tags so that users kan choose from them.
And the other way around, be able to retrieve tags from users, posts and comments.
I've read a lot about it, and still can't seem to figure out which approach I should take. Should I use referencial or embedded relationships?
I've looked at a couple of gems but no-one seems to work as i described above.
Sidenote: I am going to use Tire for my search-function later on.
Cool, welcome to MongoDB! This is hard to get right and depends on your application but I'll try and give you some pointers based on what you've written and what I think will work best.
This isn't always the case but the general theory is that if an object is always manipulated and viewed in context of another you should embed it inside that object. This is probably the case with comments and posts in your application. Therefore you may want to embed comments inside posts.
However, because you use the tag object in multiple contexts I would make it its own collection like this:
class Tag
include Mongoid::Document
field :tag, type: String
field :type, type: String
field :count, type: Integer
end
Let's run down your requirements and build the models.
Tags should have name, type and a count of how many times it has been used.
Done via above code for Tag class.
Users, posts and comments should be able to have multiple tags.
Ok so let's give each of these classes a "tags" field that has an array of tag IDs, this would be a referential relationship.
class User
include Mongoid::Document
field :first_name, type: String
field :last_name, type: String
field :email, type: String
field :tags, type: Array
end
Also here we will embed comments inside of the posts along with having the array of tag IDs like we do for Users.
class Post
include Mongoid::Document
field :subject, type: String
field :body, type: String
field :tags, type: Array
embeds_many :comments
end
class Comment
include Mongoid::Document
field :name, type: String
field :type, type: String
field :count, type: Integer
embedded_in :post
end
Make sense? There is some more info here on modeling these sorts of relationships in Rails but using mongomapper instead of mongoid (so don't pay attention to the syntax but pay attention to the ideas presented)

Resources