I have the following mongo document:
{
_id: 'someid',
name: 'John Doe',
address: {
city: 'Osaka',
country: 'Japan'
}
}
How do I index by city and country?
From the MongoDB documentation:
Indexing on Embedded Fields
You can create indexes on fields embedded in sub-documents, just as you can index top-level fields in documents. [...] Instead, indexes on embedded fields allow you to use a “dot notation,” to introspect into sub-documents.
[...]
db.people.ensureIndex( { "address.zipcode": 1 } )
Mongoid uses the same Dot Notation:
You can define indexes on embedded document fields as well.
class Person
include Mongoid::Document
embeds_many :addresses
index "addresses.street"
end
So you want something like this:
class C
include Mongoid::Document
index 'address.city'
index 'address.country'
#...
end
Related
I have two models Core::User and Core::UserMeta :
class Core::User
include Mongoid::Document
embeds_many :core_user_meta, class_name: 'Core::UserMeta'
end
class Core::UserMeta
include Mongoid::Document
field :meta_key, type: String
field :meta_value
embedded_in :core_user, class_name: 'Core::User'
end
I'm using 2 criterias for the query Core::UserMeta on Core::User looks like :
Core::User.where('core_user_meta.meta_key': 'roles', 'core_user_meta.meta_value': 'member').first
I want to get a record has criterias "roles" in meta_key and ["member"] in meta_value but the result always, looks like I'm using or clause instead of and clause
#<Core::User _id: BSON::ObjectId('5b961b07eea19009d397cfaf'), core_user_meta: [{"_id"=>BSON::ObjectId('5b961b07eea19009d397cfb0'), "meta_key"=>"nickname", "meta_value"=>"member"}, {"_id"=>BSON::ObjectId('5b961b07eea19009d397cfbb'), "meta_key"=>"roles", "meta_value"=>["subscriber"]}]>
Your query, as written, is:
Give me a user which has a meta with meta_key of 'roles' and which has a meta with meta_value of 'member'.
This query is satisfied with a user object that has two metas, each of the metas satisfying one of the conditions.
To impose both conditions on the same meta, use $elemMatch:
Core::User.where('core_user_meta'=>{'$elemMatch'=>{'meta_key': 'roles', 'meta_value': 'member'}}).first
How to make searchkick search integer fields?
Let's say i have a Book model with three properties namely name:string, author:string and pages:integer.
I want to search according to pages field. Right now if i use a query like below it works for string fields i.e name and author but it doesnt work for pages field which is of integer type.
Book.search(q,
misspellings: { below: 5 },
fields: [:name, :author, :pages],
order: { name: 'asc' },
page: params[:page],
per_page: 20)
I go to console and just searched Book.search(120, fields: [:pages]) and it returns empty result even though there are records with pages 120. Why is searchkick not searching for integer fields? I appreciate any help to this dilemma i am facing. Thanks!
I fixed it with this
In Book model
def search_data
{
name: name,
author: author,
pages: pages.to_s
}
end
Originally, I specified a relationship where contact has_many services. Therefore, services has a foreign key of contact_id:
class Contact
include Mongoid::Document
field :name, type: String
end
class Service
field :name, type: String
field :contact_id, type: Integer
end
Now there is a possibility to add an additional contact to a service, so service has many contacts. However, the contacts that are added are ones that already exist independently. So I do not want to embed one entity inside another. A contact and service will always live independently. No embedding.
So should I just store the ids of the contacts inside an array of Service? In other words, my new models will look like this:
class Contact
include Mongoid::Document
field :name, type: String
end
class Service
field :name, type: String
field :contact_id, type: Integer
field :contact_ids, type: Array, default: []
end
Or is there a better solution to address the many to many problem here (without embedding one document in another)?
For the Many-To-Many, you don't have 36 options : you actually have 2 :
Array of IDs on One side like you did
Array of IDs on Both sides.
The cool thing with the "both sides" solution is that you can find query documents from both collections to get the links.
Example with books and authors :
db.books.findOne()
{
_id: 1,
title: "The Great Gatsby",
authors: [1, 5]
}
db.authors.findOne()
{
_id: 1,
firstName: "F. Scott",
lastName: "Fitzgerald",
books: [1, 3, 20]
}
This model:
class SimCustomer < Customer
index({ user_id: 1 }, { background: true })
belongs_to :user, :inverse_of => :sim_customers
end
inherits from this model:
class Customer
include Mongoid::Document
include Mongoid::Timestamps
field :mail_address, type: String
end
I create the indexes from my terminal:
bundle exec rake db:mongoid:create_indexes
But this creates indexes on the Customer instead of the SimCustomer:
I, [2014-11-13T16:21:17.210343 #11407] INFO -- : MONGOID: Created indexes on Customer:
I, [2014-11-13T16:21:17.210381 #11407] INFO -- : MONGOID: Index: {:user_id=>1}, Options: {:background=>true}
And when I try to batch insert SimCustomer objects it creates Customer objects instead:
SimCustomer.collection.insert(Array.new << {mail_address: "hello#hello.com", user_id: "54652f5b43687229b4060000"})
# => #<Customer _id: 54654b7b6220ff4f28364ee9, created_at: nil, updated_at: nil, mail_address: "hello#hello.com", _type: "Customer">
How can I fix this?
This sets up Single Collection Inheritance:
class SimCustomer < Customer
That means that both Customer and SimCustomer will be stored in the customers collection inside MongoDB and they'll be differentiated using the _type field.
Specifying an index in SimCustomer:
class SimCustomer < Customer
index({ user_id: 1 }, { background: true })
will create the index on the customers collection because that's where SimCustomers are stored.
The same collection chicanery is causing your problem with your bulk insert. If you look at SimCustomer.collection.name you'll find that it says 'customers' so of course SimCustomer.collection.insert will create new Customers. If you want to create SimCustomers by hand then specify the _type:
SimCustomer.collection.insert(
_type: 'SimCustomer',
mail_address: "hello#hello.com",
user_id: "54652f5b43687229b4060000"
)
Note that I dropped that strange looking Array.new << stuff, I don't know where you learned that from but it is unnecessary when inserting on object and odd looking if you were inserting several, if you want to insert several then just use an array literal:
SimCustomer.collection.insert([
{ ... },
{ ... },
...
])
Your next problem is going to be that string in user_id. That really should be a Moped::BSON::ObjectId or you'll end up with a string inside the database and that will make a mess of your queries. Mongoid may know what type a property should be but neither Moped nor MongoDB will. You'll want to use Moped::BSON::ObjectId('54652f5b43687229b4060000') instead.
I have the following code and i'm trying to use ElasticSearch to query it.
It is working when i do Book.search(:q=>'Foo') but it doesn't work when i do Book.search(:author=>'Doctor'). In my database I have a entry with a name like "Barz, Foo, Doctor"
I'm not sure if I should use terms or term, in my query, because i'm breaking the name using snowball. I tried with terms and then I get an error. With term I get no results.
class Author < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :author
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :title,
indexes :description
indexes :author,type: 'object', properties: {
name: { type: 'multi_field',
fields: { name: { type: 'string', analyzer: 'snowball' },
exact: { type: 'string', index: 'not_analyzed' }
}
} }
end
def to_indexed_json
to_json(:include=>{:author=>{:only=>[:name]}} )
end
def self.search(params = {})
tire.search(load:true) do
query do
boolean do
should { string params[:q] } if params[:q].present?
should { term params[:author] } if params[:author].present?
end
end
filter :term, :active=>true
end
end
end
You can do like this
should { terms :author, [params[:author]]} if params[:author].present?
OR
should { term :author, params[:author]} if params[:author].present?
OR
should { string "author:#{params[:author]}"} if params[:author].present?
As #Karmi stated enter link description here
Hi, yeah, your approach seems one. Couple of things:
* unless you want to use Lucene query syntax (boosting, ranges, etc), it's maybe best to use the text query,
* yes, filters are more performant then queries, an the active=true in your example is a good fit for filters. Beware of the interplay between queries, filters and facets, though.
Your definition of the term query is incorrect, though -- it should be:
term :author, params[:author]