I have more then one objects. I need to update a filed in embeded objects. Here my objects.
{
"_id" : ObjectId("50dc134bb4a6de8e6b000001"),
"emails" : [
{
"_id" : ObjectId("50dc134bb4a6de8e6b000004"),
"_type" : "Email",
"name" : "personal",
"email" : "",
"current" : false
},
{
"_id" : ObjectId("51a855763183a6301e009461"),
"_type" : "Email",
"name" : "work",
"email" : "xxx#gmail.com",
"current" : true
},
],
}
{
"_id" : ObjectId("50dc134bb4a6de8e6b000002"),
"emails" : [
{
"_id" : ObjectId("50dc134bb4a6de8e6b000067"),
"_type" : "Email",
"name" : "personal",
"email" : "",
"current" : false
},
{
"_id" : ObjectId("51a855763183a6301e004795"),
"_type" : "Email",
"name" : "work",
"email" : "xxx#gmail.com",
"current" : true
},
],
}
It is a Contact collection. Here I need to set current : true where name is personal. I tried the loop function. It works fine.
contacts = Contact.where(:emails.elem_match => {"name" => "personal"})
contacts.each do |contact|
contact.email.where("name" => "personal").update_all(:current => true)
end
In mongodb we can write single line of query to update multiple objects like
db.contacts.update({"emails": {$elemMatch : {"name" : "personal"}},{ $set: {"emails.$.current":true}})
So I would like to know, Is there any possibility to write a single line of mongoid query to update multiple objects on mongoid.
An equivalent single-line Ruby statement using Mongoid is:
Contact.where(:emails.elem_match => {"name" => "personal"}).update_all("$set" => {"emails.$.current" => true})
Here's the proof that it works.
app/models/contact.rb
class Contact
include Mongoid::Document
embeds_many :emails
end
app/models/email.rb
class Email
include Mongoid::Document
field :name, type: String
field :email, type: String
field :current, type: Boolean
embedded_in :contact
end
test/unit/contact_test.rb
require 'test_helper'
require 'pp'
class ContactTest < ActiveSupport::TestCase
def setup
Contact.delete_all
end
test '0. mongoid version' do
puts "\nMongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}\nRails.version:#{Rails.version}"
end
test 'update all embedded objects' do
docs = [
{
"_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000001"),
"emails" => [
{
"_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000004"),
"_type" => "Email",
"name" => "personal",
"email" => "",
"current" => false
},
{
"_id" => Moped::BSON::ObjectId("51a855763183a6301e009461"),
"_type" => "Email",
"name" => "work",
"email" => "xxx#gmail.com",
"current" => true
},
],
},
{
"_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000002"),
"emails" => [
{
"_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000067"),
"_type" => "Email",
"name" => "personal",
"email" => "",
"current" => false
},
{
"_id" => Moped::BSON::ObjectId("51a855763183a6301e004795"),
"_type" => "Email",
"name" => "work",
"email" => "xxx#gmail.com",
"current" => true
},
],
}
]
Contact.collection.insert(docs)
# contacts = Contact.where(:emails.elem_match => {"name" => "personal"})
# contacts.each do |contact|
# contact.emails.where("name" => "personal").update_all(:current => true)
# end
#db.contacts.update({"emails": {$elemMatch : {"name" : "personal"}},{ $set: {"emails.$.current":true}})
Contact.where(:emails.elem_match => {"name" => "personal"}).update_all("$set" => {"emails.$.current" => true})
puts
contacts = Contact.all.to_a
contacts.each do |contact|
emails = contact["emails"].select{|email| email["name"] == "personal"}
assert_equal(true, emails.first["current"])
end
pp contacts.collect{|contact| contact.serializable_hash}
end
end
rake test
Run options:
# Running tests:
[1/2] ContactTest#test_0._mongoid_version
Mongoid::VERSION:3.1.6
Moped::VERSION:1.5.2
Rails.version:3.2.17
[2/2] ContactTest#test_update_all_embedded_objects
[{"_id"=>"50dc134bb4a6de8e6b000001",
"emails"=>
[{"_id"=>"50dc134bb4a6de8e6b000004",
"current"=>true,
"email"=>"",
"name"=>"personal"},
{"_id"=>"51a855763183a6301e009461",
"current"=>true,
"email"=>"xxx#gmail.com",
"name"=>"work"}]},
{"_id"=>"50dc134bb4a6de8e6b000002",
"emails"=>
[{"_id"=>"50dc134bb4a6de8e6b000067",
"current"=>true,
"email"=>"",
"name"=>"personal"},
{"_id"=>"51a855763183a6301e004795",
"current"=>true,
"email"=>"xxx#gmail.com",
"name"=>"work"}]}]
Finished tests in 0.083133s, 24.0578 tests/s, 24.0578 assertions/s.
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
Related
I'm getting response from Ecto Schema query with joins multiple tables.
Here is my query:
query =
from(
item in ItemSchema,
join: item_cat in ItemCategorySchema,
on: item.item_category_id == item_cat.item_category_id,
preload: [:BusinessSchema],
select: %{
"item_id" => item.item_id,
"item_name" => item.item_name,
"description" => item.description,
"item_category" => item_cat.category_name,
"businesses" => item.business_ids,
}
)
Repo.all(query)
Here is Item Schema:
schema "items" do
field(:item_id, :string)
field(:item_category_id, :string)
field(:user_id, :string)
field(:business_ids, {:array, :string})
field(:item_name, :string)
field(:description, :string)
end
Here is Item Category Schema:
schema "item_categories" do
field(:item_category_id, :string)
field(:item_category_id, :string)
field(:category_name, :string)
end
Here is Business Schema:
schema "businesses" do
field(:business_id, :string)
field(:business_name, :string)
end
But business_ids not populated with an item. How to populate list of businesses which are in each item.
Desirable result:
[
%{
"item_id" => "id_01",
"item_name" => "A",
"description" => "A description",
"item_category" => "some category",
"businesses" => [
${
"business_id" => "business_id_01",
"business_name" => "Business Name 1",
},
${
"business_id" => "business_id_02",
"business_name" => "Business Name 2",
},
]
},
%{
"item_id" => "id_02",
"item_name" => "B",
"description" => "B description",
"item_category" => "some category",
"businesses" => [
${
"business_id" => "business_id_02",
"business_name" => "Business Name 2",
},
${
"business_id" => "business_id_03",
"business_name" => "Business Name 3",
}
]
}
]
I try make feature when in chat there are some unacceptable message that any other users can reply on this message with command '/report' and admins received meesage from bot with inline keyboad where he can:
ban sender of report
ban reporter of message
So, for this I have two methods:
Action "/report" - it sends to admin inline keyboard(there is all
needed data)
"callback_query" - it processes message(don't have data what need
for ban of user) - it has only data of chat, user who clicked inline
button
Ok, i can save data from point 1 to redis and pass it into point 2, but how can I found(indenify) that variable to use it...
Any tips?
def report!(*)
Rails.cache.write(:update, update)
# create_report
# binding.pry
user_name_reporter = "#{update.dig('message', 'from', 'first_name')} #{update.dig('message', 'from', 'last_name')}"
user_name_sender = "#{update.dig('message', 'reply_to_message', 'from', 'first_name')} #{update.dig('message', 'reply_to_message', 'from', 'last_name')}"
respond_with :message, text: "We got report in the group!\n#{user_name_reporter} sent report on #{user_name_sender}", reply_markup: {
inline_keyboard: [
[
{text: t('.restrict_messages_for_sender'), callback_data: 'restrict_messages_for_sender'},
{text: t('.restrict_messages_for_reporter'), callback_data: 'restrict_messages_for_reporter'},
],
[
{text: t('.ban_for_sender'), callback_data: 'ban_for_sender'},
{text: t('.ban_for_reporter'), callback_data: 'ban_for_reporter'},
],
# [{text: t('.repo'), url: 'https://github.com/telegram-bot-rb/telegram-bot'}],
],
}
end
def callback_query(data)
report_update = Rails.cache.read(:update)
sender_user_id = report_update.dig('message', 'from', 'id')
reporter_user_id = report_update.dig('message', 'reply_to_message', 'from', 'id')
chat_id = report_update.dig('message', 'chat', 'id')
# binding.pry
case data
when 'restrict_messages_for_sender'
answer_callback_query t('.alert'), show_alert: true
bot.restrict_chat_member(chat_id: chat_id, user_id: sender_user_id, permissions: { can_send_messages: false }.to_json, until_date: (Time.now + 1.hours).to_i)
when 'restrict_messages_for_reporter'
answer_callback_query t('.no_alert')
bot.restrict_chat_member(chat_id: chat_id, user_id: reporter_user_id, permissions: { can_send_messages: false }.to_json, until_date: (Time.now + 1.hours).to_i)
when 'ban_for_sender'
answer_callback_query t('.no_alert')
bot.ban_chat_member(chat_id: chat_id, user_id: sender_user_id)
when 'ban_for_reporter'
answer_callback_query t('.no_alert')
bot.ban_chat_member(chat_id: chat_id, user_id: reporter_user_id)
end
end
data in action '/report' :
{
"update_id" => 156273636,
"message" => {
"message_id" => 523,
"from" => {
"id" => 308967324,
"is_bot" => false,
"first_name" => "PRD",
"last_name" => "RSD",
"username" => "anko20094",
"language_code" => "en"
},
"chat" => {
"id" => -686671946,
"title" => "test gropt for bots",
"type" => "group",
"all_members_are_administrators" => true
},
"date" => 1659272502,
"reply_to_message" => {
"message_id" => 522,
"from" => {
"id" => 1949490661,
"is_bot" => false,
"first_name" => "Den",
"username" => "ItshenotI",
"language_code" => "uk"
},
"chat" => {
"id" => -686671946,
"title" => "test gropt for bots",
"type" => "group",
"all_members_are_administrators" => true
},
"date" => 1659272484,
"text" => "unacceptable text, that must be reported"
},
"text" => "/report",
"entities" => [
[0] {
"offset" => 0,
"length" => 7,
"type" => "bot_command"
}
]
}
}
and here data in action 'callback_query':
{
"update_id" => 156273638,
"callback_query" => {
"id" => "1327004554721482603",
"from" => {
"id" => 308967324,
"is_bot" => false,
"first_name" => "PRD",
"last_name" => "RSD",
"username" => "anko20094",
"language_code" => "en"
},
"message" => {
"message_id" => 524,
"from" => {
"id" => 5362380113,
"is_bot" => true,
"first_name" => "anko_test",
"username" => "anko_test_bot"
},
"chat" => {
"id" => -686671946,
"title" => "test gropt for bots",
"type" => "group",
"all_members_are_administrators" => true
},
"date" => 1659272601,
"text" => "We got report in the group!\nPRD RSD sent report on Den",
"reply_markup" => {
"inline_keyboard" => [
[0] [
[0] {
"text" => "Restrict messages for sender user",
"callback_data" => "restrict_messages_for_sender"
},
[1] {
"text" => "Restrict messages for reported user",
"callback_data" => "restrict_messages_for_reporter"
}
],
[1] [
[0] {
"text" => "Ban for sender",
"callback_data" => "ban_for_sender"
},
[1] {
"text" => "Ban for reporter",
"callback_data" => "ban_for_reporter"
}
]
]
}
},
"chat_instance" => "-5057992267045364113",
"data" => "restrict_messages_for_sender"
}
}
and screenshot how it looks in telegram:in telegram
I'm having issues with a Ruby gem that used to work, but now doesn't. It's openstates https://rubygems.org/gems/openstates . The code to find Legislators doesn't seem to work anymore. We have had this code in production for a while, but are now getting this error
NoMethodError - undefined method `map' for #<String:0x007ff0b51941c0>
openstates (0.0.1) lib/openstates/model.rb:37:in `where'
This is the how we are calling the method.
OpenStates::Legislator.where(parameters)
parameters = {:state=>"Ca"}
Your code hasn't changed, the gem hasn't changed, but the API probably has.
Here's a very basic module to help you get an Array from OpenStates::Legislator.where(parameters) :
require 'open-uri'
require 'active_support/core_ext/object'
module OpenStates
class Legislator
#cache={}
class << self
def where(parameters)
url = "https://openstates.org/api/v1/legislators/?#{parameters.to_query}"
#cache[url] ||= get_json(url)
end
private
def get_json(url)
puts "# Downloading #{url}"
sleep 2
JSON.load(open(url))
end
end
end
end
parameters = {:state=>"Ca"}
p OpenStates::Legislator.where(parameters).first(2)
It outputs :
# Downloading https://openstates.org/api/v1/legislators/?state=Ca
[
{
"last_name" => "Gatto",
"updated_at" => "2016-09-24 07:16:00",
"nimsp_candidate_id" => nil,
"full_name" => "Mike Gatto",
"id" => "CAL000123",
"first_name" => "Mike",
"middle_name" => "",
"district" => "43",
"chamber" => "lower",
"state" => "ca",
"votesmart_id" => "120220",
"party" => "Democratic",
"+capitol_office" => {
"phone" => "(916) 319-2043",
"street" => "P.O. Box 942849, Room 4140",
"zip" => "94249-0043",
"city" => "Sacramento"
},
"all_ids" => [
"CAL000123",
"CAL000246",
"CAL000246",
"CAL000367"
],
"leg_id" => "CAL000123",
"active" => true,
"transparencydata_id" => "76584a5322274b9b892642b7b6ae3db5",
"photo_url" => "http://assembly.ca.gov/sites/assembly.ca.gov/files/memberphotos/AD43.jpg",
"+district_offices" => [
{
"phone" => "(818) 240-6330",
"street" => "300 East Magnolia, Suite 504",
"zip" => "91502",
"city" => "Burbank"
}
],
"url" => "http://asmdc.org/members/a43",
"country" => "us",
"created_at" => "2012-01-31 19:25:09",
"level" => "state",
"+district" => "43",
"offices" => [
{
"fax" => nil,
"name" => "Capitol Office",
"phone" => "916-319-2043",
"address" => "P.O. Box 942849, Room 5136\nSacramento, CA 94249-0043",
"type" => "capitol",
"email" => nil
},
{
"fax" => nil,
"name" => "District Office",
"phone" => "818-558-3043",
"address" => "300 East Magnolia Blvd, Suite 504\nBurbank, CA 91502",
"type" => "district",
"email" => nil
}
],
"+party" => "Democratic",
"suffixes" => ""
},
{
"last_name" => "Gordon",
"updated_at" => "2016-09-24 07:16:01",
...
How to group array of hashes into one "groupname" and "id" hash and push elements occurring after "words" keyword into array of hashes? I can't find similar issue.
My flat input:
[{
"id" => "1",
"groupname" => "Unit 1",
"words" => nil,
"unit" => "1",
"name" => "asdfwe"
}, {
"id" => "1",
"groupname" => "Unit 1",
"words" => nil,
"unit" => "1",
"name" => "testasdf"
}, {
"id" => "2",
"groupname" => "Unit 2",
"words" => nil,
"unit" => "2",
"name" => "test2wewe"
}, {
"id" => "2",
"groupname" => "Unit 2",
"words" => nil,
"unit" => "2",
"name" => "test2sadf"
}]
Desired output:
[{
"id" => "1",
"groupname" => "Unit 1",
"words" => [{
"unit" => "1",
"name" => "asdfwe"
}, {
"unit" => "1",
"name" => "testasdf"
}]
}, {
"id" => "2",
"groupname" => "Unit 2",
"words" => [{
"unit" => "2",
"name" => "test2wewe"
}, {
"unit" => "2",
"name" => "test2sadf"
}]
}]
Current not working as expected steps:
out = []
arrhsh.each_with_index do |hsh,index|
hsh.each do |(k,v)|
if k === "words"
newhsh = {}
newhsh["id"] = hsh["id"]
newhsh["groupname"] = hsh["groupname"]
newhsh["words"] = []
wordshsh = {
"unit" => hsh["unit"],
"name" => hsh["name"]
}
newhsh["words"] << wordshsh
out << newhshq
end
end
end
out.group_by {|h| h["id"]}
What about this?
b = a.group_by { |i| i['id'] }.map do |id, group|
{
id: id,
groupname: group.first['groupname'],
words: group.map { |g| g.slice('unit', 'name') }
}
end
When things went wrong, Rails echoes a dump of given params, it looks like this:
Parameters:
{"utf8"=>"✓",
"filter"=>{"title"=>"1",
"address"=>"2",
"city"=>{"title"=>"3"},
"people"=>{"name"=>"4",
"surname"=>"5",
"patronymic"=>"6",
"emails"=>{"email"=>"7"}},
"emails"=>{"email"=>"8"}},
"sort"=>{"0"=>{"field"=>"",
"dir"=>"asc"},
"1"=>{"field"=>"",
"dir"=>"asc"}},
"commit"=>"Show"}
But it actually would be much more useful if looks like this:
{
"utf8" => "✓",
"filter" => {
"title" => "1",
"address" => "2",
"city" => {
"title" => "3"
},
"people" => {
"name" => "4",
"surname" => "5",
"patronymic" => "6",
"emails" => {
"email" => "7"
}
},
"emails" => {
"email" => "8"
}
},
"sort" => {
"0" => {
"field" => "",
"dir" => "asc"
},
"1" => {
"field" => "",
"dir" => "asc"
}
},
"commit" => "Show"
}
How to achieve that?
With vanilla Ruby the nicest way is to turn the hash to JSON:
JSON::pretty_generate(hash_here, :allow_nan => true, :max_nesting => false)
This outputs a prettified JSON version of your object (array, hash or whatever). It won´t be pure ruby but this comes right at the base library so there is no need to install anything.