I'm just curious how to handle nested json objects using mongoid, believing that it's possible. Here is an example GeoJson data from my frontend app:
{
"type": "Feature",
"properties": {
"name": "A Cool Feature",
"amenity": "A Cool Label",
"popupContent": "This is a cool area.."
},
"geometry": {
"type": "polygon",
"coordinates": [
{
"lat": 41.47566020027821,
"lng": 269.65942382812506
},
{
"lat": 40.17047886718111,
"lng": 266.81396484375006
},
{
"lat": 38.41055825094609,
"lng": 268.86840820312506
},
{
"lat": 38.24680876017446,
"lng": 270.91186523437506
},
{
"lat": 40.871987756697415,
"lng": 271.57104492187506
}
]
}
}
And here is my controller (very basic one)
class Api::V1::GeoDataController < ApplicationController
# GET /geo_data
def index
@geo_data = GeoDatum.all
render json: @geo_data
end
# POST /geo_data
def create
@geo_datum = GeoDatum.new(geo_datum_params)
if @geo_datum.save
render json: @geo_datum
else
render error: { error: 'Unable to create GeoDatum.' }, status: 400
end
end
private
def geo_datum_params
params.require(:geo_datum).permit(:type, :properties, :geometry)
end
end
Models are here:
class GeoDatum
include Mongoid::Document
include Mongoid::Timestamps
field :type, type: String
field :properties, type: Property
field :geometry, type: Geometry
embeds_one :properties, class_name: "Property"
embeds_one :geometry, class_name: "Geometry"
end
class Property
include Mongoid::Document
field :name, type: String
field :amenity, type: String
field :popupContent, type: String
embedded_in :geo_datum, class_name: "GeoDatum"
end
class Geometry
include Mongoid::Document
field :type, type: String
field :coordinates, type: Array
embeds_many :coordinates, class_name: "LatLng"
embedded_in :geo_datum, class_name: "GeoDatum"
end
class LatLng
attr_reader :longitude, :latitude
def initialize(lng, lat)
@longitude = lng
@latitude = lat
end
embedded_in :geometry, class_name: "Geometry"
end
Here is the console log for POST request:
Started POST "/api/v1/geo_data" for 192.168.1.209 at 2020-07-13 21:58:53 +0000
Processing by Api::V1::GeoDataController#create as */*
Parameters: {"type"=>"Feature", "properties"=>{"name"=>"A Cool Feature", "amenity"=>"A Cool Label", "popupContent"=>"This is a cool area.."}, "geometry"=>{"type"=>"polygon", "coordinates"=>[{"lat"=>41.47566020027821, "lng"=>269.65942382812506}, {"lat"=>40.17047886718111, "lng"=>266.81396484375006}, {"lat"=>38.41055825094609, "lng"=>268.86840820312506}, {"lat"=>38.24680876017446, "lng"=>270.91186523437506}, {"lat"=>40.871987756697415, "lng"=>271.57104492187506}]}, "geo_datum"=>{"type"=>"Feature", "properties"=>{"name"=>"A Cool Feature", "amenity"=>"A Cool Label", "popupContent"=>"This is a cool area.."}, "geometry"=>{"type"=>"polygon", "coordinates"=>[{"lat"=>41.47566020027821, "lng"=>269.65942382812506}, {"lat"=>40.17047886718111, "lng"=>266.81396484375006}, {"lat"=>38.41055825094609, "lng"=>268.86840820312506}, {"lat"=>38.24680876017446, "lng"=>270.91186523437506}, {"lat"=>40.871987756697415, "lng"=>271.57104492187506}]}}}
Unpermitted parameters: :properties, :geometry
MONGODB | [11] 172.17.0.1:27017 #1 | leaflet.insert | STARTED | {"insert"=>"geo_data", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5f0cd91d44041302afa3ea2a'), "type"=>"Feature", "updated_at"=>2020-07-13 21:58:53.921377228 UTC, "created_at"=>2020-07-13 21:58:53.921377228 UTC}], "$db"=>"leaflet", "lsid"=>...
MONGODB | [11] 172.17.0.1:27017 | leaflet.insert | SUCCEEDED | 0.005s
Completed 200 OK in 14ms (Views: 0.7ms | MongoDB: 0.0ms | Allocations: 1098)
The point is that Unpermitted parameters: :properties, :geometry is happening, those attributes are not being fed at all. Any help would be appreciated.
if you want nested object(s) then you wrap it inside a hash or array… like this
params.require(:foo).permit(:type, :properties => [:name, :amenity, :popUpContent], geometry: [:type, { coordinates: [:lat, :lng] }])
see doc