I have a model:
class SimpleAction
include Mongoid::Document
field :set_date, :type => Date
and I have some data in collection:
{ "_id" : ObjectId("4f6dd2e83a698b2518000006"), "name" : "lost",
"notes" : "", "set_date(1i)" : "2012", "set_date(2i)" : "3",
"set_date(3i)" : "25", "set_date(4i)" : "13", "set_date(5i)" : "57",
"duration" : 15, "todo" : "4" }
You can see that mongoid store date in the five fields - set_date(ni).
I have two question:
How can I filter data by set_date field in the mongo console client? Something like this:
db.simple_actions.find({ set_date : { "$lte" : new Date() } })
My query didn't return any data.
How can I filter data by set_date field in my Rails controller? Something like this:
#simple_actions = SimpleAction.where(:set_date => { '$lte' => Date.today })
I would recommend not using Date, but instead DateTime:
field :set_date, :type => DateTime
Now not only will it be stored in 1 field, like so:
"set_date" : ISODate("2012-03-14T17:42:27Z")
But Mongoid will correctly handle various conversions for queries like you want:
SimpleAction.where( :set_date => { :$lte => Date.today } )
You can use it with class Time.
Rails is so good with Api for mobile (such as Android),when you sent date from mobile to Api like: 1482723520. (last_created=1482723520)
You can use it like that:
time = Time.at(params[:last_created].to_i)
And in Rails you can query like that:
Number.where(created_at: { :$gte => time })
Related
I would like to know how to add an extra field to the response of collection.aggregate?
The query below groups activities by user_id. And I would like to know how to also include the user_name in the response.
DB Models
class Activity
include Mongoid::Document
field :hd_race_id, type: Float
field :metric_elapsed_time, type: Float
field :metric_distance, type: Float
field :user_name, type: String
belongs_to :user
...
class User
include Mongoid::Document
field :user_name, type: String
has_many :activities
...
Query
Activity.collection.aggregate([
{
"$group" => {
"_id" => "$user_id",
"distance" => { "$sum" => "$metric_distance" },
"time" => { "$sum" => "$metric_elapsed_time" },
},
},
{ "$sort" => { "distance" => -1 } },
])
Thank you in advance
Use the operator $first (aggregation accumulator) inside the $group stage.
For example:
"user_name": {"$first": "$user_name"}
or for the programming language you are using (not sure what it is), try something like:
"user_name" => {"$first" => "$user_name"},
For an example, see the "Group & Total" chapter in my Practical MongoDB Aggregations book
I have 2 models
Form.rb
has_many :elements
Element
belongs_to :form
I want to query Elements like this:
Element.joins(:form).where(hash)
hash = {
form: {
name: "some name",
surname: "some surname"
},
element_name: "some name"
}
these attributes are both from Form and Element models, and this works fine. Now i need to scope Forms with 1 more attribute (date) which is stored in meta attribute (json field) so Form object looks like:
Form
- name
- surname
- meta: {date: "2018-10-10"}
I have created scope that can work like this:
scope :by_date, ->(date) { where('JSON_EXTRACT(meta, "$.date") = ?', date) }
Form.by_date("2018-10-10")
and this works fine, but how can i use this scope in above scenario since i can't do:
hash = {
form: {
name: "some name",
surname: "some surname",
date: "2018-10-10"
},
element_name: "some name"
}
I have some fields in my model category, tags which contains lots of text
Take the following data for example,
It should be found by these search keywords Iron, Pig, Academic Data
{
"_id" : "M0130AUSM561NNBR",
"name" : "Pig Iron Production for United StatesMonthly, Not Seasonally Adjusted, ",
"categories" : [
"Production of Commodities",
"NBER Macrohistory Database",
"Academic Data"
],
"tags" : "[\"iron\", \"metals\", \"nber\", \"production\", \"monthly\", \"nation\", \"usa\", \"nsa\"]",
"updated_at" : ISODate("2014-12-30T03:38:13.954Z"),
"created_at" : ISODate("2014-12-30T03:38:13.954Z")
}
I tried to query by Indicator.text_search("Pig")
But I got nothing
irb(main):005:0> Indicator.text_search("Pig")
=> #<Mongoid::Contextual::TextSearch
selector: {}
class: Indicator
search: Pig
filter: {}
project: N/A
limit: N/A
language: default>
Tried to search with Indicator.any_of({ :text => /.*Production.*/ })
I still got nothing.
=> #<Mongoid::Criteria
selector: {"$or"=>[{"text"=>/.*Production.*/}]}
options: {}
class: Indicator
embedded: false>
Tried to search in mongo console, still not works (nothing in the result)
> db.indicators.find({"title": /.*Production.*/})
>
Model : Indicator.rb
class Indicator
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Attributes::Dynamic
# include Mongoid::Search
field :id, type: String
field :name, type: String
field :category, type: String
field :tags, type: String
# search_in :tags, :category
end
The shell query
db.indicators.find({ "title" : /.*Production.*/ })
won't find any documents because there is no title field, according to your sample documents. Is a text index defined?
db.indicators.ensureIndex({ "name" : "text", "categories" : "text", "tags" : "text" })
Additionally, tags is a string that resembles an array of strings - is that what you want? Or do you want n array of strings? I'd think you want
"tags" : ["iron", "metals", "nber", "production", "monthly", "nation", "usa", "nsa"]
rather than
"tags" : "[\"iron\", \"metals\", \"nber\", \"production\", \"monthly\", \"nation\", \"usa\", \"nsa\"]"
I have list of countries and I whant users be able to sort results by county. So I have this helper:
def facets_for model, field
ul = ""
links = ""
model.facets[field]['terms'].each do |facet|
links << content_tag('li') do
link_to("#{facet['term']} #{facet['count']}", params.merge(field => facet['term']))
end
end
ul << content_tag("ul", class: field) do
links.html_safe
end
ul.html_safe
end
and in model:
class model
....
mapping do
indexes :country do
indexes :name, :type => :string, index: "not_analyzed"
end
end
def self.search params
...
filter :term, destination: params[:destination] if params[:destination].present?
facet("destination") { terms 'country.name' }
...
end
but
facet['term']
always return country name in lowercase. I could make it with Country.find(facet).name but I think it is unnecessary. Is there any way to store in facet same string value as in field?
Updated
my mapping:
{
"wishes" : {
"wish" : {
"properties" : {
"body" : {
"type" : "string"
},
"country" : {
"properties" : {
"name" : {
"type" : "string"
}
}
},
"country_id" : {
"type" : "string"
},
"created_at" : {
"type" : "date",
"format" : "dateOptionalTime"
}
} ... }}}
Your mapping is not created right, you can try to reindex your data.
Model.index.delete # to delete index with bad mapping
Model.create_elasticsearch_index # this should create index with mapping defined in Model
And after that you can try to run Model.import again.
I have a mongo table that has statistical data like the following....
course_id
status which is a string, played or completed
and timestamp information using Mongoid's Timestamping feature
so my class is as follows...
class Statistic
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
field :course_id, type: Integer
field :status, type: String # currently this is either play or complete
I want to get a daily count of total # of plays for a course. So for example...
8/1/12 had 2 plays, 8/2/12 had 6 plays. Etc. I would therefore be using the created_at timestamp field, with course_id and action. The issue is I don't see a group by method in Mongoid. I believe mongodb has one now, but I'm unsure of how that would be done in rails 3.
I could run through the table using each, and hack together some map or hash in rails with incrementation, but what if the course has 1 million views, retrieving and iterating over a million records could be messy. Is there a clean way to do this?
As mentioned in comments you can use map/reduce for this purpose. So you could define the following method in your model ( http://mongoid.org/en/mongoid/docs/querying.html#map_reduce )
def self.today
map = %Q{
function() {
emit(this.course_id, {count: 1})
}
}
reduce = %Q{
function(key, values) {
var result = {count: 0};
values.forEach(function(value) {
result.count += value.count;
});
return result;
}
}
self.where(:created_at.gt => Date.today, status: "played").
map_reduce(map, reduce).out(inline: true)
end
which would result in following result:
[{"_id"=>1.0, "value"=>{"count"=>2.0}}, {"_id"=>2.0, "value"=>{"count"=>1.0}}]
where _id is the course_id and count is the number of plays.
There is also dedicated group method in MongoDB but I am not sure how to get to the bare mongodb collection in Mongoid 3. I did not have a chance to dive into code that much yet.
You may wonder why I emit a document {count: 1} as it does not matter that much and I could have just emitted empty document or anything and then always add 1 to the result.count for every value. The thing is that reduce is not called if only one emit has been done for particular key (in my example course_id has been played only once) so it is better to emit documents in the same format as result.
Using Mongoid
stages = [{
"$group" => { "_id" => { "date_column_name"=>"$created_at" }},
"plays_count" => { "$sum" => 1 }
}]
#array_of_objects = ModelName.collection.aggregate(stages, {:allow_disk_use => true})
OR
stages = [{
"$group" => {
"_id" => {
"year" => { "$year" => "$created_at" },
"month" => { "$month" => "$created_at" },
"day" => { "$dayOfMonth" => "$created_at" }
}
},
"plays_count" => { "$sum" => 1 }
}]
#array_of_objects = ModelName.collection.aggregate(stages, {:allow_disk_use => true})
Follow the links below to group by using mongoid
https://taimoorchangaizpucitian.wordpress.com/2016/01/08/mongoid-group-by-query/
https://docs.mongodb.org/v3.0/reference/operator/aggregation/group/