Mongoid and collections - mongodb

I am trying to configure and use mongoid for the first time. I have set the mongoid.yml config file simply as:
host: localhost
database: table
and my code:
Mongoid.load!("/mongoid.yml")
class Data
include Mongoid::Document
field :study, type: String
field :nbc_id, type: String
field :short_title, type: String
field :source, type: String
field :start_date, type: Date
end
puts Data.study
I keep getting an error:
NoMethodError at / undefined method `study' for Data:Class
I think it is because I have not specified the collection name which is 'test'. However I can find no examples on how to do this. Do I specify it in the .yml file or in the code. What is the correct syntax. Can anyone point me in the right direction?
Tx.

According to the Mongoid documentation, "Mongoid by default stores documents in a collection that is the pluralized form of the class name. For the following Person class, the collection the document would get stored in would be named people."
http://mongoid.org/docs/documents.html
The documentation goes on to state that Mongoid uses a method called ActiveSupport::Inflector#classify to determine collection names, and provides instructions on how to specify the plural yourself.
Alternatively, you can specify the collection name in your class by including "store_in" in your class definition.
class Data
include Mongoid::Document
store_in :test
Hope this helps!

Related

Firestore security rules passing wildcard variable to path

I have the following security rules:
match /collection1/{doc_id} {
allow read: if (get(/databases/$(database)/documents/collection2/$(doc_id)).author ==
request.auth.uid);
}
What I am doing is that I am trying to pass the wildcard variable from the parent path doc_id into the path of get method. The read access of this doc in collection1 depends on the author field of a document with the same id in another collection collection2. I don't believe that the way I am passing doc_id as $(doc_id) is correct, as I get an error of: Property author is undefined on object
I have also tried (doc_id) and \doc_id, but they are syntaxilly wrong. How do I pass a wildcard variable to a path then?
You're missing a data in there, which is needed to get at the fields of the document:
get(/databases/$(database)/documents/collection2/$(doc_id)).data.author

How to note a calculated default value in OAS3

I'm updating my API spec (OAS 3.0.0), and am having trouble understanding how to properly model a "complex" default value.
In general, default values for parameters are scalar values (i.e. the field offset has a default value of 0). But in the API I'm spec'ing, the default value is actually calculated based on other provided parameters.
For example, what if we take the Pet model from the example documentation, and decide that all animals need to be tagged. If the user of the API wants to supply a tag, great. If not, it will be equal to the name.
One possibility:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
default: '#/components/schemas/Pet/name'
This stores the path value as the default, but I'd like to have it explain that the default value will be calculated.
Bonus points if I can encode information from a parent schema.
Is the alternative to just describe the behavior in a description field?
OpenAPI Specification does not support dynamic/conditional defaults. You can only document the behavior verbally in the description.
That said, you can use specification extensions (x-...) to add custom information to your definitions, like so:
tag:
type: string
x-default: name
or
tag:
type: string
x-default:
propertyName: name
# or similar
and extend the tooling to support your custom extensions.

Though I have a record in database, it's giving "Mongoid::Errors::DocumentNotFound"

Though I have the record with id 13163 (db.locations.find({_id: 13163})), it's giving me error:
Mongoid::Errors::DocumentNotFound in LocationsController#show
Problem: Document(s) not found for class Location with id(s) 13163.
Summary: When calling Location.find with an id or array of ids, each
parameter must match a document in the database or this error will be
raised. The search was for the id(s): 13163 ... (1 total) and the
following ids were not found: 13163. Resolution: Search for an id that
is in the database or set the Mongoid.raise_not_found_error
configuration option to false, which will cause a nil to be returned
instead of raising this error when searching for a single id, or only
the matched documents when searching for multiples.
# Use callbacks to share common setup or constraints between actions.
def set_location
#location = Location.find(params[:id])
end
locations_controller.rb:
class LocationsController < ApplicationController
before_action :set_location, only: [:show, :edit, :update, :destroy]
# GET /locations
# GET /locations.json
def index
#locations = Location.all
end
# GET /locations/1
# GET /locations/1.json
def show
end
private
# Use callbacks to share common setup or constraints between actions.
def set_location
#location = Location.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def location_params
params.require(:location).permit(:loc_name_en, :loc_name_jp, :channel)
end
end
Setting up the option raise_not_found_error: false is not the case as I do have a document in database.
SOLUTION:
Big thanks to #mu is too short for giving me a hint.
The problem can be solved in 2 ways:
Declare field :_id, type: Integer in the model location.rb
Or converting the passing parameter to Integer like Location.find(params[:id].to_i) in locations_controller.rb as shown below in the #mu is too short's answer
I'd guess that you have a type problem. You say that this:
db.locations.find({_id: 13163})
finds the document in the MongoDB shell. That means that you have a document in the locations collection whose _id is the number 13163. If you used the string '13163':
db.locations.find({_id: '13163'})
you won't find your document. The value in params[:id] is probably a string so you're saying:
Location.find('13163')
when you want to say:
Location.find(13163)
If the _id really is a number then you'll need to make sure you call find with a number:
Location.find(params[:id].to_i)
You're probably being confused because sometimes Mongoid will convert between Strings and Moped::BSON::ObjectIds (and sometimes it won't) so if your _id is the usual ObjectId you can say:
Model.find('5016cd8b30f1b95cb300004d')
and Mongoid will convert that string to an ObjectId for you. Mongoid won't convert a String to a number for you, you have to do that yourself.

Embedded and no-embedded document in the same time

I need to have a model which will behave like a embedded and not-embedded.
For example if I want to store this model as embedded:
class MenuPosition
include Mongoid::Document
field :name, type: String
field :category, type: String
I need to add
embedded_in :menu
to it.
On the other side, if I add this line in the model I cannot store this model as not-embedded:
position = {
"name" => "pork",
"category" => "meal",
"portion" => 100
}
MenuPosition.create(position)
error message:
NoMethodError:
undefined method `new?' for nil:NilClass
Can I use one model for embedded and not-embedded documents?
In our project we had a similar thing. What we did is define the fields as a module. A bit like this:
module SpecialFields
extend ActiveSupport::Concern
included do
field :my_field, type: String
field :my_other_field, type: String
end
end
Then in your class where you want to embed, just do:
include SpecialFields
In your class that you'd like to store separately as a non-embedded document, do this:
class NotEmbeddedDoc
include Mongoid::Document
include SpecialFields
end
This worked pretty well in our project for a few things. However, it might not be appropriate in your case since you want to embed many. This only really works for embeds one cases I think. I have posted it here in case it helps people.

Mongo ids leads to scary URLs

This might sound like a trivial question, but it is rather important for consumer facing apps
What is the easiest way and most scalable way to map the scary mongo id onto a id that is friendly?
xx.com/posts/4d371056183b5e09b20001f9
TO
xx.com/posts/a
M
You can create a composite key in mongoid to replace the default id using the key macro:
class Person
include Mongoid::Document
field :first_name
field :last_name
key :first_name, :last_name
end
person = Person.new(:first_name => "Syd", :last_name => "Vicious")
person.id # returns "syd-vicious"
If you don't like this way to do it, check this gem: https://github.com/hakanensari/mongoid-slug
Define a friendly unique field (like a slug) on your collection, index it, on your model, define to_param to return it:
def to_param
slug
end
Then in your finders, find by slug rather than ID:
#post = Post.where(:slug => params[:id].to_s).first
This will let you treat slugs as your effective PK for the purposes of resource interaction, and they're a lot prettier.
Unfortunately, the key macro has been removed from mongo. For custom ids,
users must now override the _id field.
class Band
include Mongoid::Document
field :_id, type: String, default: ->{ name }
end
Here's a great gem that I've been using to successfully answer this problem: Mongoid-Slug
https://github.com/digitalplaywright/mongoid-slug.
It provides a nice interface for adding this feature across multiple models. If you'd rather roll your own, at least check out their implementation for some ideas. If you're going this route, look into the Stringex gem, https://github.com/rsl/stringex, and acts_as_url library within. That will help you get the nice dash-between-url slugs.