How do you define an array field when using create in mongoid? - mongoid

I'm trying to create a seed file to populate my mongo database. The following syntax creates a nil value when I try to define the array field value. I'm using mongoid v3.0.9. What am I doing wrong?
These following examples do not work when I put them in the seed file:
User.create(name:'name', test_array_field:'[123,123]')
User.create(name:'name', test_array_field:[123,123])
User.create(name:'name', test_array_field:[123,123].to_a)
I've defined the field in my class like so:
field :test_array_field, type: Array

Your second syntax works for me.
class User
field :roles, type: Array, default: []
end
u = User.create roles: ['superadmin']
u.new_record? # => false
u.roles # => ["superadmin"]

Try this:
class Foo
include Mongoid::Document
field :bar, :type => Array, :default => []
field :baz, :type => Hash, :default => {}
end

Related

Mongoid query with where clause on an association of an embeded document

I have a Rails app using Mongoid with the classes User, ContractAgreement and ContractVersions.
The relationships are the following: User embeds ContractAgreement, and ContractAgreement belong to ContractVersions, like so:
User:
class User
include Mongoid::Document
include Mongoid::Stateful
include Mongoid::Timestamps
[...]
embeds_many :contract_agreements
ContractAgreement:
class ContractAgreement
include Mongoid::Document
include Mongoid::Timestamps
embedded_in :user
belongs_to :contract_version
field :date_of_agreement, type: DateTime
ContractVersion
class ContractVersion
include Mongoid::Document
include Mongoid::Timestamps
field :version, type: String
field :description, type: String
field :status, type: String
has_one :post, autosave: true
has_one :post, autosave: true
#has_many :contract_agreements
As you can see, #has_many :contract_agreements is commented out as Mongoid didn't like the association to an embeded document.
Is there a way of getting the list of Users that have agreed to a specified contract?
I've tried both doing $elemMatch all the way:
[64] pry(main)> reload!;
u = User.all.select{|u| u.contract_agreements.count > 0}.first ;
cv = u.contract_agreements.first.contract_version ;
User.where(contract_agreements: {'$elemMatch' => {contract_version: {'$elemMatch' => {_id: cv._id}}}}).count
Reloading...
=> 0
As well as the compressed single query notation:
[64] pry(main)> reload!;
u = User.all.select{|u| u.contract_agreements.count > 0}.first ;
cv = u.contract_agreements.first.contract_version ;
User.where("contract_agreements.contract_version._id" => cv._id).count
Reloading...
=> 0
I know the contract version id I am asking for exists since I explicitly select a user that has one.
Is such a query even possible or do I need to de-embed ContractAgreement? If it is possible, what am I doing wrong?
Referring to the foreign key worked:
[73] pry(main)> reload!;
u = User.all.select{|u| u.contract_agreements.count > 0}.first ;
cv = u.contract_agreements.first.contract_version ;
User.where("contract_agreements.contract_version_id" => cv._id).count
Reloading...
=> 1

How to convert two level array to one level array in laravel

I have a array of array data which is came from database. And my "array to xml converter" can convert only one level array.
Basicly I want to convert my database table to xml file.
public function downloadXml()
{
$fields = ['created_at', 'updated_at'];
$products = Product::where('user_id', auth()->id())
->exclude($fields)->get()->toArray();// this is returnin array of array like [0 => [], 1 => []]
$products = array_collapse($products);
$result = ArrayToXml::convert($product, 'product');
}
The problem is array_collapse method trim the one level array but give me only last array not all arrays. How can I get all arrays? Any help appreciated..
Edit: when dd(Product::where('user_id', auth()->id())
->exclude($fields)->get()->toArray(););
Output1 = array:2 [▼ 0 => array:18 [▶] 1 => array:18 [▶] ]
When dd(array_collapse(Product::where('user_id', auth()->id())
->exclude($fields)->get()->toArray());)
Output2 = array:18 [▶]
I need something like output2 but the problem is output2 assuming there is only one product but actualy there is two product.
When performing a database query using Eloquent (Laravel ORM) the results are returned inside a Collection (one per row). This is the 'first level' array you are mentioning.
Unfortunately, a 'second level' array is needed to define the attributes (among others) for every row.
So you either have to:
extend your ArrayToXml converter to work with Laravel collections (i.e. print xml tag, loop through the elements and print the closing xml tag)
Split your xml converter into two pieces: a wrapper for the xml tag opening and closing and your current ArrayToXml::convert inside a map function.
Let me illustrate the latter:
public function downloadXml()
{
return Product::where('user_id', auth()->id())
->get()
->map(function (Product $product){
return ArrayToXml::convert($product->only('id', 'user_id'), 'product');
})
}

Ruby Array Search Method Not Working As Intended

I am having trouble making the search method that i wrote in my script to work. Here is the relevant code from the script:
$records = []
def xml_extractor(document)
document.xpath("//record").each do |record|
$records << {"id" => record.xpath('id').text,
"first_name" => record.xpath('first_name').text,
"last_name" => record.xpath('last_name').text,
"email" => record.xpath('email').text,
"gender" => record.xpath('gender').text,
"ip_address" => record.xpath('ip_address').text,
"send_date" => record.xpath('send_date').text,
"email_body" => record.xpath('email_body').text,
"email_title" => record.xpath('email_title').text}
puts record
end
puts "\nAll records loaded!"
end
def search_by_ip(ip)
record_to_return = $records.select {|k| k["ip_address"] == ip.to_s}
puts JSON.pretty_generate(record_to_return)
end
Basically my xml_extractor method works fine and it stores everything into the array using nokogiri. The xml file that is being implemented has a thousand records each having its own first_name, last_name etc. But the problem is when i try to implement the search_by_ip method on the array a "null" value is returned, when what the method should really be doing is returning the entire record that belongs to that specific ip address. Also, i realised that every time i implement the xml_extractor method, i.e. when an xml document is parsed in into the array, the contents arent really saved in rather they are only displayed for while the loop is going. Which might be why I get a "null" for my search methods. Let me know what you guys think though.
I wrote an example of how to use OO to obtain what you want.
I don't have your document so I simplified your document to a 2 dimensional array
In the method read switch the comment to work with your xml
Each method can be chained and does only what is required
They can all be tested separatly (here by p'ting them)
class Xml_extractor
attr_reader :document, :records
def initialize document
#document = document
#records = []
end
def read
# #document.xpath("//record").each do |record|
#document.each do |record|
#records << {id: record[0], ip_address: record[1]}
end
self # return self so that you can chain another method
end
def search_by_ip(ip)
#return first of an array if found, nil otherwise
# attention to use a hash key here to search, not a string
#records.select {|k| k[:ip_address] == ip.to_s}.first
end
end
document = [[0, "192.168.0.1"], [1, "192.168.0.2"]]
p Xml_extractor.new(document).read.records
# [{:id=>0, :ip_address=>"192.168.0.1"}, {:id=>1, :ip_address=>"192.168.0.2"}]
p Xml_extractor.new(document).read.search_by_ip("192.168.0.2")
# [{:id=>1, :ip_address=>"192.168.0.2"}]
p Xml_extractor.new(document).read.search_by_ip("192.168.0.2").to_json
# "[{\"id\":1,\"ip_address\":\"192.168.0.2\"}]"
In ruby your method will return the last line. If you want your method to return data, you need to return it on the last line. puts doesn't return anything.
Try to change like this:
def search_by_ip(ip)
record_to_return = $records.select {|k| k["ip_address"] == ip.to_s}
puts JSON.pretty_generate(record_to_return)
record_to_return
end

Parse Array of JSON Objects in Crystal lang

Suppose I've got a simple JSON mapped object in Crystal lang, e.g.:
class Item
JSON.mapping(
id: UInt32,
name: String,
)
end
I can parse individual objects from JSON strings easily like so:
foo = Item.from_json(%({"id":1,"name":"Foo"}))
puts "OK: foo=#{foo}"
# => OK: foo=Item(#id=1, #name="Foo")
But how would I parse an array of Items from a JSON string? I've tried a few approaches but am not sure how to proceed, e.g.:
items_str = %([{"id":1,"name":"Foo"},{"id":2,"name":"Bar"}])
items : Array(Item) = JSON.parse(items_str)
# => Error in foo.cr:15: type must be Array(Item), not JSON::Any
Of course, I'd also like to be able to do this with a JSON pull parser, so presumably there's some mapping trick or type hint I'm missing. Ideas?
Found it in this spec. So, you can use Array(Item).from_json:
items = Array(Item).from_json %([{"id":1,"name":"Foo"},{"id":2,"name":"Bar"}])
items.first.id #=> 1
items.first.name #=> "Foo"
items.last.id #=> 2
items.last.name #=> "Bar"

Remove duplicates from array of objects based on multiple attributes in ruby

I have an array of objects which may contain objects with same attribute values. I am trying to remove the duplicates based on multiple attributes (not just one attribute value)
class Font
attr_accessor :color, :name, :type
end
a = <#color="blue", #name="s", #type="bold">
b = <#color="blue", #name="r", #type="italic">
c = <#color="green", #name="t", #type="bold">
d = <#color="blue", #name="s", #type="some_other_type">
fonts = [a, b, c, d]
I need to eliminate duplicates based on the values of color, name (I don't care about type)
what I have tried
uniq_fonts = fonts.uniq { |f| f.name.to_s + f.color.to_s}
is there any cleaner way in which I can achieve the same result?
Note:
these are objects and not hashes. I know we could have used:
fonts.uniq { |f| f.values_at(:name, :color)}
if they were hash
You can try:
uniq_fonts = fonts.uniq { |f| [ f.name, f.color ] }
You can defined your own values_at method like:
class Font
attr_accessor :color, :name, :type
def values_at *args
args.map { |method_name| self.public_send method_name }
end
end
And then do like :
fonts.uniq { |f| f.values_at(:name, :color)}

Resources