Ruby Mongoid DateTime.gte/lte query not working - ruby

I have a mongoid class User with a DateTime field called last_notification_check
not all the User objects have this field, because its only added after the first time a user logs in.
I need to query for the following:
User.where(:last_notification_check.lte => (Time.now - 60).utc)
this should return a response of 1 user object, but it continuously returns nil
my mongoid object that it should be able to pick up:
{
"_id" : ObjectId("590ad06f78cee4ffcd612d5e"),
"active" : true,
"first_name" : "Test",
"last_name" : "Person",
"suffix" : null,
"email" : "test#email.com",
"encrypted_password" : "s7FThz4a68nBKx6aGJ19Bw==",
"admin" : true,
"updated_at" : ISODate("2017-05-28T21:29:30.486+0000"),
"created_at" : ISODate("2017-05-04T06:55:42.990+0000"),
"title" : "Test User",
"last_notificaton_check" : ISODate("2017-05-28T21:29:30.485+0000")
}
I get the following results.. not sure what to do...
User.where(:last_notification_check.gte => Time.now)
# nil
User.where(:last_notification_check.lte => Time.now)
# nil
User.where(:first_name => 'Test').each do |user|
puts user.last_name
end
# "Person"

Related

LINQ to JSON - Querying an array

I need to select users that have a "3" in their json array.
{
"People":[
{
"id" : "123",
"firstName" : "Bill",
"lastName" : "Gates",
"roleIds" : {
"int" : ["3", "9", "1"]
}
},
{
"id" : "456",
"firstName" : "Steve",
"lastName" : "Jobs",
"roleIds" : {
"int" : ["3", "1"]
}
},
{
"id" : "789",
"firstName" : "Elon",
"lastName" : "Musk",
"roleIds" : {
"int" : ["3", "7"]
}
},
{
"id" : "012",
"firstName" : "Agatha",
"lastName" : "Christie",
"roleIds" : {
"int" : "2"
}
}
]}
In the end, my results should be Elon Musk & Steve Jobs. This is the code that I used (& other variations):
var roleIds = pplFeed["People"]["roleIds"].Children()["int"].Values<string>();
var resAnAssocInfo = pplFeed["People"]
.Where(p => p["roleIds"].Children()["int"].Values<string>().Contains("3"))
.Select(p => new
{
id = p["id"],
FName = p["firstName"],
LName = p["lastName"]
}).ToList();
I'm getting the following error:
"Accessed JArray values with invalid key value: "roleIds". Int32 array index expected"
I changed .Values<string>() to .Values<int>() and still no luck.
What am I doing wrong?
You are pretty close. Change your Where clause from this:
.Where(p => p["roleIds"].Children()["int"].Values<string>().Contains("3"))
to this:
.Where(p => p["roleIds"]["int"].Children().Contains("3"))
and you will get you the result you want (although there are actually three users in your sample data with a role id of "3", not two).
However, there's another issue that you might hit for which this code still won't work. You'll notice that for Agatha Christie, the value of int is not an array like the others, it is a simple string. If the value will sometimes be an array and sometimes not, then you need a where clause that can handle both. Something like this should work:
.Where(p => p["roleIds"]["int"].Children().Contains(roleId) ||
p["roleIds"]["int"].ToString() == roleId)
...where roleId is a string containing the id you are looking for.
Fiddle: https://dotnetfiddle.net/Zr1b6R
The problem is that not all objects follow the same interface. The last item in that list has a single string value in the roleIds.int property while all others has an array. You need to normalize that property and then do the check. It'll be easiest if they were all arrays.
You should be able to do this:
var roleId = "3";
var query =
from p in pplFeed["People"]
let roleIds = p.SelectToken("roleIds.int")
let normalized = roleIds.Type == JTokenType.Array ? roleIds : new JArray(roleIds)
where normalized.Values().Contains(roleId)
select new
{
id = p["id"],
FName = p["firstName"],
LName = p["lastName"],
};

Map keys with the same name

A GET to an API endpoint I'm working with returns json with an inconsistent order of contacts, either
{"contacts"=>[
{"id"=>$UUID_0, "name"=>nil, "email"=>$EMAIL_0, "phone"=>$PHONE_0, "type"=>"foo"},
{"id"=>$UUID_1, "name"=>nil, "email"=>$EMAIL_1, "phone"=>$PHONE_1, "type"=>"bar"}
]}
or
{"contacts"=>[
{"id"=>$UUID_1, "name"=>nil, "email"=>$EMAIL_1, "phone"=>$PHONE_1, "type"=>"bar"},
{"id"=>$UUID_0, "name"=>nil, "email"=>$EMAIL_0, "phone"=>$PHONE_0, "type"=>"foo"}
]}
The "type" values are the only static objects in these responses, so I'd like to map this so that the contact types are keys containing the other pairs:
{
"foo"=>{"id"=>$UUID_0, "name"=>$NAME_0, "email"=>$EMAIL_0, "phone"=>$PHONE_0},
"bar"=>{"id"=>$UUID_1, "name"=>$NAME_1, "email"=>$EMAIL_1, "phone"=>$PHONE_1}
}
A solution is not obvious to me.
If you use Ruby on Rails, or at least ActiveSupport, you can try index_by instead of group_by: it won't put the values into arrays.
hash['contacts'].index_by {|r| r['type']}
=>
{
"bar" => {
"id" => "asdf",
"name" => nil,
"email" => "EMAIL_1",
"phone" => "PHONE_1",
"type" => "bar"
},
"foo" => {
"id" => "asdf",
"name" => nil,
"email" => "EMAIL_0",
"phone" => "PHONE_0",
"type" => "foo"
}
}
Hash[data['contacts'].map { |c| [c['type'], c] }]
This can be done with Enumerable#reduce:
hash['contacts'].reduce({}) {|m,c| m[c['type']] = c;m}
How it works:
An empty hash is the starting point.
The block is called once for each element in the contacts list. The block receives the hash that we're building as m and the current contact as c.
In the block, assign c to the hash based on its type and return the hash so far.
Final result is the last return value of the block.

Compare three arrays of hashes and get the result without duplicates in ruby?

I m using the fql gem to retrieve the data from facebook. The original array of hashes is like this. Here. When i compare these three arrays of hashes then i want to get the final result in this way:
{
"photo" => [
[0] {
"owner" : "1105762436",
"src_big" : "https://fbcdn-sphotos-b-a.akamaihd.net/hphotos-ak-xap1/t31.0-8/q71/s720x720/10273283_10203050474118531_5420466436365792507_o.jpg",
"caption" : "Rings...!!\n\nView Full Screen.",
"created" : 1398953040,
"modified" : 1398953354,
"like_info" : {
"can_like" : true,
"like_count" : 22,
"user_likes" : true
},
"comment_info" : {
"can_comment" : true,
"comment_count" : 2,
"comment_order" : "chronological"
},
"object_id" : "10203050474118531",
"pid" : "4749213500839034982"
}
],
"comment" => [
[0] {
"text" : "Wow",
"text_tags" : [],
"time" : 1398972853,
"likes" : 1,
"fromid" : "100001012753267",
"object_id" : "10203050474118531"
},
[1] {
"text" : "Woww..",
"text_tags" : [],
"time" : 1399059923,
"likes" : 0,
"fromid" : "100003167704574",
"object_id" : "10203050474118531"
}
],
"users" =>[
[0] {
"id": "1105762436",
"name": "Nilanjan Joshi",
"username": "NilaNJan219"
},
[1] {
"id": "1105762436",
"name": "Ashish Joshi",
"username": "NilaNJan219"
}
]
}
Here is my attempt:
datas = File.read('source2.json')
all_data = JSON.parse(datas)
photos = all_data[0]['fql_result_set'].group_by{|x| x['object_id']}.to_a
comments = all_data[1]['fql_result_set'].group_by{|x| x['object_id']}.to_a
#photos_comments = []
#comments_users = []
#photo_users = []
photos.each do |a|
comments.each do |b|
if a.first == b.first
#photos_comments << {'photo' => a.last, 'comment' => b.last}
else
#comments_users << {'photo' => a.last, 'comment' => ''} unless #photos_comments.include? (a.last)
end
end
end
#photo_users = #photos_comments | #comments_users
#photo_comment_users = {photos_comments: #photo_users }
Here is what i'm getting final result
Still there are duplicates in the final array. I've grouped by the array by object id which is common between the photo and the comment array. But the problem it is only taking those photos which has comments. I'm not getting the way how to find out the photos which don't have the comments.
Also in order to find out the details of the person who has commented, ive users array and the common attribute between comments and users is fromid and id. I'm not able to understand how to get the user details also.
I think this is what you want:
photos = all_data[0]['fql_result_set']
comments = all_data[1]['fql_result_set'].group_by{|x| x['object_id']}
#photo_comment_users = photos.map do |p|
{ 'photo' => p, 'comment' => comments[p['object_id']] || '' }
end
For each photo it takes all the comments with the same object_id, or if none exist - returns ''.
If you want to connect the users too, you can map them by id, and select the relevant ones by the comment:
users = Hash[all_data[2]['fql_result_set'].map {|x| [x['id'], x]}]
#photo_comment_users = photos.map do |p|
{ 'photo' => p, 'comment' => comments[p['object_id']] || '',
'user' => (comments[p['object_id']] || []).map {|c| users[c['formid']]} }
end

Mongoid nested embedded document save ignores query

I'm having trouble saving the correct nested embedded document with mongoid. I've tried this in both v3.1.6 and v4.0.0 and the result is the same. The parent document is saved, but the nested document I modified ignores the changes, and instead updates the first nested document instead.
Assume some models like so:
Mongoid.configure do |config|
config.sessions = {default: {hosts: ["localhost:27017"], database: "test_mongoid"}}
end
class Band
include Mongoid::Document
field :name, type: String
embeds_many :members
end
class Member
include Mongoid::Document
embedded_in :band
field :name, type: String
embeds_many :instruments
end
class Instrument
include Mongoid::Document
embedded_in :member
field :name, type: String
def make_badass!
self.name = "Badass #{self.name}"
self.save
end
end
And a program that runs:
Band.destroy_all
a_band = {
name: "The Beatles",
members: [
{
name: 'Paul',
instruments: [
{
name: 'Bass guitar'
},
{
name: 'Voice'
}
]
}
]
}
Band.create(a_band)
the_beatles = Band.first
puts the_beatles.as_document
paul = the_beatles.members.first
voice = paul.instruments.where({name: "Voice"}).first
voice.make_badass!
puts Band.first.as_json
The database should now contain:
{
"_id": ObjectId('53aa7d966d6172889c000000'),
"name" : "The Beatles",
"members" : [
{
"_id" : ObjectId('53aa7d966d6172889c010000'),
"name" : "Paul",
"instruments" : [
{"_id" : ObjectId('53aa7d966d6172889c020000'), "name" : "Bass guitar"},
{"_id" : ObjectId('53aa7d966d6172889c030000'), "name" : "Voice"}
]
}
]
}
But instead, it contains:
{
"_id": ObjectId('53aa7d966d6172889c000000'),
"name" : "The Beatles",
"members" : [
{
"_id" : ObjectId('53aa7d966d6172889c010000'),
"name" : "Paul",
"instruments" : [
{"_id" : ObjectId('53aa7d966d6172889c020000'), "name" : "Badass Voice"},
{"_id" : ObjectId('53aa7d966d6172889c030000'), "name" : "Voice"}
]
}
]
}
What would be a working way of having the correct embedded document to change from within the instance method of Instrument?
Thanks for your help!
Mongoid is quickly forcing me to become an alcoholic. Hopefully it will help someone in the same situation.
class Instrument
include Mongoid::Document
embedded_in :member
field :name, type: String
def make_badass
self.name = "Badass #{self.name}"
self.member.band.save
end
def unset_name
# self.unset :name does not work
self.remove_attribute :name
self.member.band.save
end
end

Ruby Mongo::ObjectID comparison

SO,
I have two Mongo::ObjectID objects that are equal according to both == and eql? (they both return true). However, if one is a key in a Hash and the other is in a document stored in an array, this fails:
myhash[array_of_docs[0]['_id']] # => nil
myhash.fetch(array_of_docs[0]['_id']) # => KeyError: key not found
My db has 2 collections, "bookmarks", with mainly a title and url, and "tags", with a 'bkm_id' key pointing to a bookmark doc's _id and a 'name' key. With the following query, I map each bookmark's _id to the corresponding comma-separated list of tags:
bkms_tags_array = tags_collection.group(['bkm_id'], nil, { tags: Array.new }, "function(tag, agg){ agg.tags.push(tag.name) }", true)
bkms_tags = {}
bkms_tags_array.each do |bt|
bkms_tags.merge! Hash[bt.values[0], bt.values[1].join(", ")]
end
bkms_tags # => {4d60b29603e5665f82000001=>"socialnw, blablabla", 4d60b44703e5665fff000001=>"mail, app, google", 4d61812f03e5661ad8000001=>"socialnw, comms, web"}
Given that bks is the result of 'bookmarks_collection.find.to_a', this is my problem:
bkms_tags[bks[0]['_id']] # => nil
bkms_tags.include? bks[0]['_id'] # => false ; however:
bkms_tags.keys.include? bks[0]['_id'] # => true
How come 'hash.include?' be false and 'hash.keys.include?' be true? Is there a difference between ObjectIDs returned by different queries?
Like I said, both == and eql? return true:
bkms_tags.each { |k,v| puts k == bks[0]['_id'] } # => true false false
bkms_tags.keys.each { |k| puts k == bks[0]['_id'] } # => true false false
bkms_tags.each { |k,v| puts k.eql? bks[0]['_id'] } # => true false false
bkms_tags.keys.each { |k| puts k.eql? bks[0]['_id'] } # => true false false
So, by any comparison possible, 'bks[0]['_id']' is a key of bkms_tags, but when I try to retrieve it's value, Ruby gets confused somehow.
Any ideas? Thanks!
Extra info:
Some sample documents:
Bookmarks
{ "_id" : ObjectId("4d60b29603e5665f82000001"), "url" : "http://www.facebook.com/", "title" : "Facebook", "host" : "facebook.com", "saved_at" : "Sun Feb 20 2011 01:20:06 GMT-0500 (PET)" }
{ "_id" : ObjectId("4d60b44703e5665fff000001"), "url" : "http://mail.google.com/", "title" : "gmail", "host" : "mail.google.com", "saved_at" : "Sun Feb 20 2011 01:27:19 GMT-0500 (PET)" }
{ "_id" : ObjectId("4d61812f03e5661ad8000001"), "url" : "http://twitter.com/", "title" : "twitter", "host" : "twitter.com", "saved_at" : "Sun Feb 20 2011 16:01:35 GMT-0500 (PET)" }
Tags
{ "_id" : ObjectId("4d60b44703e5665fff000002"), "bkm_id" : ObjectId("4d60b44703e5665fff000001"), "name" : "mail" }
{ "_id" : ObjectId("4d60b44703e5665fff000003"), "bkm_id" : ObjectId("4d60b44703e5665fff000001"), "name" : "app" }
{ "_id" : ObjectId("4d60b29603e5665f82000003"), "bkm_id" : ObjectId("4d60b29603e5665f82000001"), "name" : "socialnw" }
{ "_id" : ObjectId("4d60b29603e5665f82000004"), "bkm_id" : ObjectId("4d60b29603e5665f82000001"), "name" : "blablabla" }
{ "_id" : ObjectId("4d61812f03e5661ad8000003"), "bkm_id" : ObjectId("4d61812f03e5661ad8000001"), "name" : "comms" }
{ "_id" : ObjectId("4d61812f03e5661ad8000004"), "bkm_id" : ObjectId("4d61812f03e5661ad8000001"), "name" : "web" }
EDIT
Testing some more, I came up with more irregularities:
bkms_ids # => [4d60b29603e5665f82000001, 4d61812f03e5661ad8000001, 4d61ba9103e5667dbe000001, 4d61ba9103e5667dbe000001]
bkms_ids[2] == bkms_ids[3] # => true
bkms_ids[2].eql? bkms_ids[3] # => true
bkms_ids.uniq # => nothing changes: [4d60b29603e5665f82000001, 4d61812f03e5661ad8000001, 4d61ba9103e5667dbe000001, 4d61ba9103e5667dbe000001]
EDIT 2
As requested, my bson version:
irb> BSON::VERSION # NameError: uninitialized constant BSON::VERSION
$ gem list bson
*** LOCAL GEMS ***
bson (1.2.2)
bson_ext (1.2.2)
and inspect and class of my array of docs:
array_of_docs = bookmarks_collection.find.to_a
array_of_docs[0].inspect # => "{\"_id\"=>4d60b29603e5665f82000001, \"url\"=>\"http://www.facebook.com/\", \"title\"=>\"Facebook\", \"host\"=>\"facebook.com\", \"saved_at\"=>2011-02-20 06:20:06 UTC}"
array_of_docs[0].class # => OrderedHash
array_of_docs[0]['_id'].class # => Mongo::ObjectID
Change uniq to uniq! :
bkms_ids
bkms_ids[2] == bkms_ids[3]
bkms_ids[2].eql? bkms_ids[3]
bkms_ids.uniq!

Resources