Mongoid "find" returns nil, when "find_by" retrieves record - ruby

I'm using Mongoid to access a MongoDB database, however I'm running into an odd problem. It seems like I can only query for records using find_by, as find will always return nil:
invoices = Invoice.find({})
p "invoices"
p invoices
puts ''
invoice = Invoice.find_by({ _id: <ObjectId> })
p "invoice"
p invoice
puts ''
The second query using find_by will return a single record. According to the documentation, find should be returning every record that satisfies the query.
Does anyone have an idea what could be causing this?

Be careful not to confuse the Moped syntax with the Mongoid syntax. For Mongoid, the docs describe the find method:
Find a document or multiple documents by their ids. Will raise an error by default if any of the ids do not match
If you really want every record, Invoice.all can do the trick. (Also be careful with your find_by method. The Mongoid syntax varies from mongo's a bit, so you don't have to have the curlies around your params.)

Related

Ruby: Paginate and sort across a large number of records

When simply displaying large amounts of data (over 100k records) my code works well, and I paginate on the server.
However, when I need to sort this data I'm stuck. I'm only sorting on the page, and NOT sorting on ALL the records related to this one customer.
How can I paginate but also sort across all the records of my customer and NOT simply sort the records returned from the server side pagination?
I'm also using BootStrap Table to display all my data.
Here is my code that gets all the customers:
def get_customers
#data_to_return = []
#currency = current_shop.country_currency
customers = current_shop.customers.limit(records_limit).offset(records_offset)#.order("#{sort_by}" " " "#{sort_order}")
customers.each do |customer|
#data_to_return.push(
state: false,
id: customer.id,
email: customer.email,
accepts_marketing: customer.accepts_marketing,
customer_status: customer.customer_status,
tags: customer.tags)
end
sort_customers
end
And then this is the sort_customers method:
def sort_customers
fixed_data = data_to_return.sort_by {|hsh| hsh[sort_by]}
customer_size = current_shop.customers.length
if sort_order == "ASC"
fixed_data
else
fixed_data.reverse!
end
render json: {"total": customer_size, "rows": fixed_data}
end
In the above code you can see that data_to_return is coming from get_customers and its limited. But I don't want to return ALL the customers for many reasons.
How can I sort across all the records, but only return the paginated subset?
You should actually sort at the model/query level, not at the ruby level.
The difference is basically:
# sort in ruby
relation.sort_by { |item| foo(item) }
# sort in database - composes with pagination
relation.order('column_name ASC/DESC')
In the first case, the relation is implicitly executed, enumerated and converted to array before calling sort_by. If you did pagination (manually or with kaminari), you will get just that page of data.
In the second case, you are actually composing the limit, offset and where (limit and offset are anyways used under the hood by kaminari, where is implicit when you use associations) with a order so your database would execute
SELECT `customers`.`*` FROM `customers`
WHERE ...
OFFSET ...
LIMIT ...
ORDER BY ...
which will return the correct data.
A good option is to define scopes in the model, like
class Customer < ApplicationRecord
scope :sorted_by_email, ->(ascending = true) { order("email #{ascending ? 'ASC' : 'DESC'}") }
end
# in controller
customers = current_shop.customers.
limit(records_limit).
offset(records_offset).
sorted_by_email(false)
You can resolve sorting and paginate issue using Data Tables library, which is client side. It's a Jquery library. Using this you need to load all data into page, then it would work very well.
Below are the references please check.
Data tables jquery libray
Data tables gem for rails
You can try these, they will work very well. You can customise it as well
If the answer is helpful, you can accept it.

How can I sum one column from the same table, to produce three different aggregates, using Sequel ORM?

My query is this:
DB[:expense_projects___p].where(:project_company_id=>user_company_id).
left_join(:expense_items___i, :expense_project_id=>:project_id).
select_group(:p__project_name, :p__project_id).
select_more{count(:i__item_id)}.
select_more{sum(:i__amount)}.to_a.to_json
which works.
However, payment methods include cash, card and invoice. So I would like to sum each of those for summary purposes to achieve a discrete total for payments by cash, card, and invoice repsectively. I included the following line into the query
select_more{sum(:i__amount).where(:i__mop => 'card')}.
and the error message was
NoMethodError - undefined method `where' for #<Sequel::SQL::Function:0x007fddd88b5ed0>:
so I created the dataset separately with
ds1 = expense_items.where(:mop=>'card', :expense_company_id=>user_company_id).sum(:amount)
and appended it, at the end of the original query, with
select_append{ds1}
which achieved partial success as the returned json is now:
{"project_name":"project 2","project_id":2,"count":4,"sum":"0.40501E3","?column?":"0.2381E2"}
as can be seen there is no name for this element which I need in order to reference it in my getJSON call. I tried to add an identifier by adding ___a
to the ds1 query as below
ds1 = expense_items.where(:mop=>'card', :expense_company_id=>user_company_id).sum(:amount___a)
but that failed.
In summary, is this the right approach and, in any case, how can I provide an identifier when doing a sequel sum query? In other words sum(:a_column).as(a_name)
Many thanks.
Dataset#sum returns the sum, not a modified dataset. You probably want something like:
ds1 = expense_items.where(:mop=>'card', :expense_company_id=>user_company_id).select{sum(:amount)}
select_append{ds1.as(:sum)}
I am not sure for approach ( better ask Jeremy Evans ) but it work.
You just change: .sum(... to .select_more{:amount___a).as(:desired_name)}
ds1 = expense_items.where(:mop=>'card', :expense_company_id=>user_company_id).select_more{:amount___a).as(:desired_name)}
and actually get that desired_name in db response.

How to use Sequel to select one field from database

I am using Sinatra and Sequel with PostgreSQL.
After authentication, I want to welcome the user by printing their name but I cannot get only the value of the user's name from the database, it comes out as a hash.
The query is:
current_user = DB[:users].select(:username).where('password = ?', password).first
and the resulting piece of data is:
Welcome, {:username=>"Rich"}
which looks rather weird, I would prefer it to read "Welcome, Rich".
What am I doing wrong here? I tried the same query without 'first" at the end and that does not work either.
You can either pull the (single) column you selected out of the Hash you are given:
current_user = DB[:users].select(:username).where('password=?', password).first[:username]
Or you can map your results to an array of usernames and pull the first:
# Using a hash in the filter method is simpler than SQL placeholders.
current_user = DB[:users].filter(password:password).select_map(:username).first
But the best way is to get only the user you care about, and then get the name:
# Using [] on a dataset returns the first row matching the criteria
current_user = DB[:users][password:password][:username]
Try Sequel::Dataset#get. Also, as Phrogz points out, Sequel::Dataset#where can take a hash (it will securely escape values to prevent injection attacks).
current_username = DB[:users].where(password: password).get(:username)
There's also Sequel::Dataset#where_single_value, which is optimized for this exact situation:
current_username = DB[:users].select(:username).where_single_value(password: password)

Mongoid where clause ruby hash

Say I have a mongoid document which has a field :pairs with the type of hash. When I wan't to query on the hash like this:
Doc.where(:pairs=>{"field1"=>1})
I get results back because I have in pairs a field with value one. I also have values for more than 1. When I do the following, nil is returned:
Doc.where(:pairs=>{"field1"=>{"$gt"=>0}})
This doesn't seem to work, and I do have pairs with key field1 and values bigger than 0. Can anyone provide me info on why this doesn't work?
Just try: Dco.where(:pairs.gt => 0)
For more detail mongoid querying see following link.
http://mongoid.org/en/mongoid/docs/querying.html
Doc.where('pairs.field1' => {"$gt"=>1})

how to say 'is not in' in an active record query

Hey I hope you can help me,
#courses = Course.where(:id != current_user.courses)
I want to get the courses, where the current user is not registered to
#courses = Course.where(:id => current_user.courses) gives me the courses where the user has registered to. But I want the opposite
Associations are just fine. I can use current_user.courses or current_user.assignments.
You probably need something like this:
#courses = Course.where("id NOT IN (?)", current_user.courses)
The reason you can't use a pure array or hash condition is because you're negating ("NOT IN") the query. As far as I know, this can't be done without using the string based syntax. If you just wanted to find matching ("IN") items, you could use the hash form:
#courses = Course.where(:id => current_user.courses)
It's the negating ("NOT IN") part that makes it tricky.
More information can be found in the Active Record Query Interface Guide.
If you wish to avoid using raw strings, you could use ARel
#courses = Course.where(Course.arel_table[:id].not_in(current_users.courses))
Unfortunately, ARel is not documented very well. I use this as a reference.

Resources