Is it possible to create indexes in Mongodb for dynamic fields? - mongodb

My site has several "domains" which represent sub-sections of the site. Each "domain" has its own users with profiles, and a search page where you can search for users based on profile fields that the users fill out.
User data from the 'users' collection is structured like so:
{
username: 'shawnmichaels',
first_name: 'Shawn',
last_name: 'Michaels',
domains: [
{
name: 'domain1',
user_fields: {
'bio': 'Short bio related to domain 1',
'skills': 'Pertinent skills for domain 1'
}
},
{
name: 'domain2',
user_fields: {
'bio': 'Short bio related to domain 2',
'skills': 'Pertinent skills for domain 2'
}
}
]
}
So, users have field data across multiple domains. The domain names and field names are dynamic. There could potentially be hundreds of domains for a user and dozens of fields in a domain.
Is it possible to have some sort of dynamic index so that I can search for fields under 'domain1' without getting any matches in 'domain2'?
For example, if a user1 has "skills": ["karate", "judo"] under 'domain1', and I want to search domain2 for "karate", I don't want a match for user1.

For anyone interested, I've answered my own question after a day of research.
I'm pretty new to MongoDB and didn't realize you can query arrays by treating them like any other keys, like so:
db.users.find({domains.title: 'domain1'})
// where 'domains' is an array of object each with a 'title' key
With this knowledge, I added a text index for each field I want to be searchable:
db.users.ensureIndex({
'username': 'text',
'first_name': 'text',
'last_name': 'text',
'domains.fields.bio': 'text',
'domains.fields.skills': 'text',
'domains.fields.title': 'text',
'domains.fields.training': 'text'
}, {name: 'domain_search'})
And I structured search queries in each domain like so:
db.users.find({
'domains.title': DOMAIN_NAME,
'$text': {'$search': SEARCH_TERM}
})

Related

Mongo DB Collection - Count values of an attribute

I have a mongo db with companies with the following data structure:
{'Beschreibung': [],
'Branche': ['Doctor'],
'Homepage': 'http://www.doctorsdomain.de',
'Mail': None,
'Name': ['Companyname'],
'Ort': 'Doctors City',
'PLZ': '12345 ',
'StrHausnummer': 'Doctors Street',
'Telefonnummer': '1235345646',
'_id': ObjectId('sadfsdfawdfawedfbad5f'),
'domain': 'doctorsdomain',
'doctors': [{'Salutation': 'Dear Madame', 'Name': 'Maximiliane Mustermann'},
{'Salutation': 'Dear Mister', 'Name': 'Bernd Beispiel'}],
'other information'
I don't find the query to count how many names (doctors) I have in my database.
I tried something like
collection.find({"doctors":{"$exists":1}}).count()
but than I only get the number of the companies where doctors exists.
Need help please!
Thanks a lot!

Correct way to do a PATCH upsert request

What is the proper way to request the following upsert to a REST API?
I'm struggling to structure a NoSQL collection, which is based on the most requested returns to the front-end application.
Suppose you have the following document:
{
'user' : {
'private_comments': [
//object available only to user 1
{
'id': 1,
'bar': 'He is very good',
'...': '...'
},
//object available only to user 2
{
'id': 2,
'bar': 'He is very bad',
'...': '...'
}
],
'public_comments': '' //infos available to all users
}
}
It is needed to upsert an element to the user.private_comments array.
According to https://www.rfc-editor.org/rfc/rfc6902#appendix-A.5, I could request a replace instruction PATCHing the following data:
{ 'op': 'replace', 'path': '/user/comments/$index', 'value': 'Actually, he is OK'}
The problem is that '$index' is unknown in this case.
A possible solution that I came up with was to create something like the following operation:
{ 'op': 'upsert', 'find_by': 'id', 'find': 1, 'path': '/user/comments', 'value': 'Nope, he really sucks' }
However, the user implementing the API should't provide the id value inside the PATCH request, because this value is already accessible via the receive token. Should I simplify the operation to:
{ 'op': 'upsert', 'path': '/user/comments', 'value': 'Nope, he really sucks' }
and treat it at the backend, so when it's and upsert operation without 'find' and 'find_by' variables I assume 'find_by': 'id' and 'find': value_from_token?
Also, I cannot do a simple GET/UPDATE at the hole document because the user doesn't receive the hole document, so an update would compromise the data.

Mongoid add field but don't exist in mongodb

I use Mongoid to query db in a rails project.
When i add a field "scope" to a User Model
class User
include Mongoid::Document
field :scope, type: String
end
When use rails console, we can find this field have add to User model.
But when we go to mongodb console to view users data, use db.users.find(), it don't have scope field yet.
also when i want update scope field in rails console. It always have below error.
pry(main)> user = User.first
=> #<User _id: 521dcd4464fad29a74000009, scope: "private", xxx_id: "521dcd4464fad29a74000008">
pry(main)> user.update_attributes(scope: "public")
Moped::Errors::OperationFailure: The operation: #<Moped::Protocol::Command
#length=84
#request_id=17
#response_to=0
#op_code=2004
#flags=[]
#full_collection_name="blog_development.$cmd"
#skip=0
#limit=-1
#selector={:getlasterror=>1, :safe=>true}
#fields=nil>
failed with error 11000: "E11000 duplicate key error index: blog_development.users.$_id_ dup key: { : ObjectId('521dcd4464fad29a74000009') }"
Any one can tell me why?
Just adding field :scope, type: String to your model won't add it to the underlying MongoDB collection, that just tells Mongoid that it should allow the model to have a scope for accessors, mutators, validations, mass-assignment, ... Keep in mind that different documents in a MongoDB collection can have entirely different properties so the schema is really a fiction that is applied by Mongoid.
If you want all your users to have a scope then you'd have to say something like this:
User.collection.find().update_all(:$set => { :scope => 'whatever it should be' })
to manually add it to all the documents.
The field exists in your saved objects. But to be able to find one with your asked value, you have to create one.
class User
include Mongoid::Document
field :name, type: String
field :age, type: Integer
end
User.find(name: 'John')
# => nil
User.create({ name: 'John', age: '18' })
# => User 284692 :name => 'John', :age => 18
User.find(name: 'John')
# => User 284692 :name => 'John', :age => 18
Hope it helps.
edit after 'mu is too short' post :
To fill all existing objects in db, just provide a :default value to your field
class User
field :name, type: String, default: 'Martin Dupont'
end
Thanks for reply.
The reason is in this model, i have overwrite new_record? method. So cause this error.

SugarCRM Rest API: Required fields for a Meeting entry?

I'm trying to create a Meeting entry via the API, but I can't seem to find it in my calendar even though it saved successfully (I got the ID back) and I can see it in the Meetings list.
What fields do I need to populate for it to show in my Sugar calendar? Here are the fields I'm populating:
{
'assigned_user_id': '1', # My user's ID
'date_end': '2013-04-16 01:30:00',
'date_start': '2013-04-16 01:23:45',
'description': 'hello world',
'location': 'JCenter',
'name': 'Test',
'team_id': '1',
'type': 'Sugar' # This doesn't seem to be required
}
Try to set "user_id" and take a look into mettings_users table where you will find it.

MongoDB, add new { field : value } in existing embedded document with multiple level dot notation?

What I am trying to do is add a new {field:value} for a blog post. So for example, if I wanted to start tracking impressions on websites.blog_posts.url: 'http://www.example.com/01.html' how can I add that impressions attribute for that blog post?
My current document structure:
{
email_address: 'webmaster#example.com',
password: 'random_password',
first_name: 'John',
last_name: 'Doe',
user_type: 'WEBMASTER',
newsletter: 'NO',
websites: [{
main_title: 'My Blog Website',
main_url: 'http://www.example.com',
blog_posts: [{
url: 'http://www.example.com/01.html',
title:'My first blog post',
description: 'My first description.'
}, {
url: 'http://www.example.com/02.html',
title: 'My second blog post',
description: 'My second description.'
}, {
url: 'http://www.example.com/03.html',
title: 'My third blog post',
description: 'My third description.'
}, {
url: 'http://www.example.com/04.html',
title: 'My fourth blog post',
description: 'My fourth description.'
}]
}]
}
Here is what I thought would work using update and making upsert TRUE.
db.my_collection.update( {'websites.blog_posts.url': 'http://www.example.com/01.html' }, {'$set': {'websites.blog_posts.impressions': 549}}, true )
The error that I received is:
*can't append to array using string field name [blog_posts]*
Maybe "$set" is not correct for this or maybe I can not reference that deep with dot notation? I just started using MongoDB yesterday, any help would be great.
Thank you!
What you're trying to do is not possible given your schema. Dot-notation can be multi level but if there's more than one level that is an array it can no longer be addressed using the positional operator '$'.
E.g. you'd need to do :
db.my_collection.update(
{'websites.blog_posts.url': 'http://www.example.com/01.html' },
{'$set': {'websites.$.blog_posts.$.impressions': 549}},
true );
But having two position operators in the update is not possible since MongoDB can only determine the position of an element in the first array.
Your only option is to redesign your schema to have a dedicated collection of user websites (which is better for other reasons too in this case).