mongoid criteria result doesn't fill all field - ruby-on-rails

I am new to Rails and MongoDB as well as MongoID..
class User
include Mongoid::Document
include Mongoid::Timestamps
field :fbid, type: String
field :facebookname, type: String
field :competitorFbid, type: String
field :createdAt, type: DateTime
field :updatedAt, type: DateTime
# building constructor the rails way: http://stackoverflow.com/a/3214293/474330
def initialize(options = {})
#fbid = options[:fbid]
#facebookname = options[:facebookname]
#competitorFbid = options[:competitorFbid]
end
def writeasjson
hash = { :fbid => #fbid,
:facebookname => #facebookname,
:competitorFbid => #competitorFbid,
:createdAt => #createdAt,
:updatedAt => #updatedAt
}
hash.to_json
end
attr_accessor :fbid, :facebookname, :competitorFbid, :createdAt, :updatedAt
end
I am using MongoID to query my mongodb database like this:
myuser = User.where(fbid: params[:fbid]).first
render :json => myuser.writesajson
However, the result is all the fields are "null"
If I print the criteria result like this,
render :json => myuser
it prints all the _id, authData and bcryptPassword field, however the rest of the field have null value,
Here is what I got from the MongoDB database in my app. If I query from MongoHub, all the null values will be filled
{
"_id": {
"$oid": "56d2872f00af597fa584e367"
},
"authData": {
"facebook": {
"access_token": "yEf8cZCs9uTkrOq0ZCHJJtgPFxPAig9yhW6DhBCLuJqPdMZBLPu",
"expiration_date": "2016-04-17T13:52:12.000Z",
"id": "9192631770"
}
},
"bcryptPassword": "$2a$10$9mUW3JWI51GxM1VilA",
"competitorFbid": null,
"createdAt": null,
"created_at": null,
"facebookname": null,
"fbid": null,
"objectId": "nLurZcAfBe",
"runCount": 2446,
"sessionToken": "0SwPDVDu",
"updatedAt": null,
"updated_at": null,
"username": "XgcWo4iUCK"
}
I have been debugging the whole day without any light, any help will be greatly appreciated...
EDIT:
adding the response
{"_id":{"$oid":"56d2872f00af597fa584e366"},"authData":{"facebook":{"access_token":"[ACCESS_TOKEN_REMOVED]","expiration_date":"2015-12-19T14:17:25.000Z","id":"[ID_REMOVED]"}},"bcryptPassword":"[PASSWORD_REMOVED]","competitorFbid":null,"createdAt":null,"created_at":null,"facebookname":null,"fbid":null,"objectId":"H5cEMtUzMo","runCount":790,"sessionToken":"[SESSION_TOKEN_REMOVED]","updatedAt":null,"updated_at":null,"username":"[USERNAME_REMOVED]"}

A field in the database is declared using the field method:
field :fbid, type: String
This also defines fbid and fbid= methods to work with the fbid attribute.
An instance variable with associated accessor and mutator methods is declared using the attr_accessor method:
attr_accessor :fbid
This will also add fbid and fbid= methods to work with the underlying instance variable.
They're not the same thing. Mongoid only knows about fields, those are the things that it will work with in the database so your query works; field also defines accessor and mutator methods for your fields.
But you have an attr_accessor call after your field calls so the methods that field creates (such as fbid and fbid=) are overwritten by those created by attr_accessor. The result is that all your attributes appear to be nil.
The solution is to drop the attr_accessor call from your class. You only need the field calls.

Related

jsonapi-serializer hide the id of a model

Using jsonapi-serializer I don't want to return the ID of the blog post, eventually I'll return a custom slug instead, but I cant figure out how to remove fields in the serializer.
{
"data": {
"id": "6",
"type": "blog_post",
"attributes": {
"email": "mark#example.com",
"name": "Mark"
}
}
}
I tried using a conditional proc to hide the id to no avail:
class BlogPostSerializer
include JSONAPI::Serializer
attributes :email, :name
attribute :id, if: Proc.new { false }
end
Ah it turns out you need an ID per the JSON API spec:
https://discuss.jsonapi.org
I didn't want to reveal the actual id of the blog post however, so you can tell the serializer to return the uuid instead:
set_id :uuid
attributes :email, :name, :uuid

How to store boolean value in jsonb column

I'm getting a checkbox value from my form like this
<%= f.label 'Most Popular', class: "form-label" %>
<%= check_box_tag "price_template[preferences][pcb_budget_options][][most_popular]",
true,
f.object.preferences.dig("pcb_budget_options", index, "most_popular") %>
And the params I am permitting is like this
params.require(:price_template).permit(:currency_id,
:program_id,
:active,
:default,
country_ids: [],
preferences: [budget_options: [:amount, :most_popular, :text],
pcb_budget_options: [:amount, :most_popular]])
and it stored in DB like this
{
"budget_options"=>[
{"amount"=>"1.0", "text"=>"budget options"},
{"amount"=>"2.0", "most_popular"=>"true", "text"=>"budget options"},
{"amount"=>"3.0", "text"=>"budget options"}
],
"pcb_budget_options"=>[
{"amount"=>"1.0"},
{"amount"=>"0.0"},
{"amount"=>"-1.0", "most_popular"=>"true"}
]
}
but the most_popular value is stored here is in string format but I want to store it as a boolean.
It's not possible in jsonb it will be always save as a "string" in the DB.
You can create your own serializer to parse the jsonb as you want or if only the boolean part matter for you, you can create a method in your model like this
class Mymodel
def budget_most_popular
ActiveRecord::Type::Boolean.new.cast(preferences["budget_options"]["most_popular"])
end
def pcb_budget_most_popular
ActiveRecord::Type::Boolean.new.cast(preferences["pcb_budget_options"]["most_popular"])
end
end
So anywhere in your code you can get a real boolean value by calling this method.
Imagine you have the following record
$> MyModel.first
<MyModel:0x029I23EZ
id: 1,
name: "foo",
preferences: {
"budget_options" =>
[
{ "amount"=>"10", "most_popular"=>"true", "text"=>"" },
{"amount"=>"0.0", "text"=>""}, {"amount"=>"0.0", "text"=>""}
],
"pcb_budget_options"=>
[
{"amount"=>"20", "most_popular"=>"false"}, {"amount"=>"0.0"},
{"amount"=>"0.0"}
]
}
...
And somewhere in your code you need to check if budget_option is most_popular
class MyModelController
def show
#mymodel = MyModel.first
if #mymodel.budget_most_popular
render "template/most_popular"
else
render "template/less_popular"
end
end
end
Since in our last record budget_options as most_popular set as 'true' our model method budget_most_popular will cast this 'true'to return a boolean true so our show will render the most_popular template
Here is an interesting blog article about the jsonb with Rails

Exclude data from as_json method

My Model includes some country informations
class MyModel
include Mongoid::Document
field :name, type: String
field :country, as: :country, type: Country
end
MyModel.first.country
#<Country:0x007fc6a5d5f278 #data={"continent"=>"Asia",
"alpha2"=>"TH",
"alpha3"=>"THA",
"country_code"=>"66",
"currency"=>"THB",
"international_prefix"=>"001",
"ioc"=>"THA", "latitude"=>"15 00 N",
"longitude"=>"100 00 E", "name"=>"Thailand",
"names"=>["Thailand", "Thaïlande", "Tailandia", "タイ"],
"translations"=>{"en"=>"Thailand",
"it"=>"Tailandia",
"de"=>"Thailand",
"fr"=>"Thaïlande",
"es"=>"Tailandia",
"ja"=>"タイ",
"nl"=>"Thailand",
"ru"=>"Таиланд"},
"national_destination_code_lengths"=>[2],
"national_number_lengths"=>[9, 10],
"national_prefix"=>"0", "number"=>"764",
"region"=>"Asia", "subregion"=>"South-Eastern Asia",
"un_locode"=>"TH", "languages"=>["th"], "nationality"=>"Thai"}>
Calling MyModel.first.to_json(only: [:name, :country]) should only return alpha2, translations and names
How can I achieve that?
I try to avoid to write an extra method for that.
EDIT:
Expected Output something like this:
MyModel.first
{"name": "ModelName",
"country": {"alpha2"=> "TH",
"name" => "Thailand",
"names"=> ["Thailand", "Thaïlande", "Tailandia", "タイ"]
}
You can override as_json method for your Model to get the desired result.
class MyModel
def as_json(options = {})
super(only: [:name]).merge(
country: country.data.slice("alpha2", "unofficial_names", "translations")
)
end
end
Now you can get the data by calling as_json on Model
MyModel.first.as_json
Hope it helps !

Reduce complexity of RABL template

My RABL template seems to be very un-DRY and over complex. Because of this I think I may be using it wrong, or that there are better ways at generating my desired output.
As you can see from the show.rabl code, I have to turn the plugins_vulnerability.vulnerability association into a JSON hash, explicitly selecting which keys I need, then merge the plugins_vulnerability.fixed_in value into the hash, and finally adding the new hash, which now contains the fixed_in value, to the vulnerabilities_array array.
I'm doing this because I want the fixed_in value to be within the vulnerability node.
plugins_controller.rb
class Api::V1::PluginsController < Api::V1::BaseController
def show
#plugin = Plugin.friendly.includes(:plugins_vulnerability, :vulnerabilities).find(params[:id])
end
end
show.rabl:
object #plugin
cache #plugin if Rails.env == 'production'
attributes :name
# Add the 'vulnerabilities' node.
node :vulnerabilities do |vulnerabilities|
vulnerabilities_array = []
# turn the plugins_vulnerability association into an array
vulnerabilities.plugins_vulnerability.to_a.each do |plugins_vulnerability|
vulnerability = plugins_vulnerability.vulnerability.as_json # turn the plugins_vulnerability.vulnerability association into json
vulnerability = vulnerability.select {|k,v| %w(id title references osvdb cve secunia exploitdb created_at updated_at metasploit fixed_in).include?(k) } # only select needed keys
vulnerabilities_array << {
:vulnerability => vulnerability.merge(:fixed_in => plugins_vulnerability.fixed_in)
} # merge the fixed_in attribute into the vulnerability hash and add them to an array (fixed_in is from plugins_vulnerabilities)
end
vulnerabilities_array
end
output.json
{
"plugin": {
"name": "simple-share-buttons-adder",
"vulnerabilities": [
{
"vulnerability": {
"id": 88157,
"title": "Simple Share Buttons Adder 4.4 - options-general.php Multiple Admin Actions CSRF",
"references": "https:\/\/security.dxw.com\/advisories\/csrf-and-stored-xss-in-simple-share-buttons-adder\/,http:\/\/packetstormsecurity.com\/files\/127238\/",
"osvdb": "108444",
"cve": "2014-4717",
"secunia": "",
"exploitdb": "33896",
"created_at": "2014-07-15T17:16:51.227Z",
"updated_at": "2014-07-15T17:16:51.227Z",
"metasploit": "",
"fixed_in": "4.5"
}
},
{
"vulnerability": {
"id": 88158,
"title": "Simple Share Buttons Adder 4.4 - options-general.php ssba_share_text Parameter Stored XSS Weakness",
"references": "https:\/\/security.dxw.com\/advisories\/csrf-and-stored-xss-in-simple-share-buttons-adder\/,http:\/\/packetstormsecurity.com\/files\/127238\/",
"osvdb": "108445",
"cve": "",
"secunia": "",
"exploitdb": "33896",
"created_at": "2014-07-15T17:16:51.341Z",
"updated_at": "2014-07-15T17:16:51.341Z",
"metasploit": "",
"fixed_in": "4.5"
}
}
]
}
}
I guess you can do something like this:
object #plugin
cache #plugin if Rails.env == 'production'
attributes :name
child(#plugin.vulnerabilities => :vulnerabilities) {
attributes :id, :title, :references, :osvdb, :cve, :secunia, :exploitdb, :created_at, :updated_at, :metasploit
# Add the 'fixed_in' node.
node :fixed_in do |vulnerability|
#plugin.plugins_vulnerability.fixed_in
end
}
This should create the same output that you need. And it doesn't look awefully complex to me.

Search json field with mongoid

I've got the following MongoDB/Mongoid document:
#<Event
_id: 51e406a91d41c89fa2000002,
namespace: :namespace,
bucket: "order_created",
data: {
"order"=>{
"id"=>100,
"deleted_at"=>nil,
"created_at"=>2011-10-06 15:45:04 UTC,
"updated_at"=>2013-07-10 16:37:07 UTC,
"completed_at"=>2013-07-10 16:37:07 UTC
}
}>
Here is the event class definition:
class Event
include Mongoid::Document
field :namespace, type: Symbol
field :bucket, type: String
field :data, type: Hash
end
I want to find and update it using the find_and_modify method in Mongoid but I can't figure out how to properly structure the search criteria to search the data field properly.
Specifically, I want to find data.order.id = 100. I've tried the following with no luck:
Event.where(:data.order.id => 100)
Event.where(:'data["order"]["id"]' => 100)
Event.where( { data: { order: { id: 100 } } } )
Event.where( { data: { :"order" => { :"id" => 100 } } }
The latter returns nil, but the former (and, from the documentation I've read, the correct way to do it) gives me a SyntaxError: unexpected ':'.
This is with Mongoid 3.1.4 and MongoDB 2.4.5.
Answering my own question. The Event class is not referencing a collection, which is what's critical for the Criteria search to work. I've instantiated a new db object to use against the collection and the find/where methods work. Here's an example:
#db = Mongoid::Sessions.default
#db[:events].find().first['order']
#db[:events].where("data.order.id" => 100).first

Resources