Can I edit multiple documents from an array of them in Mongo? - mongodb

well...
I have a collection of users, one user looks like
{ userId: "someId", fullName: "AnyName", email: "anyEmail#mail.com"}
and of course Mongo adds one _id
all fine here, but a have an express server and I receive and array of existing users with a POST.
[
{ userId: "123", fullName: "name1 EDITED", email: "anyEmail1#mail.com"},
{ userId: "124", fullName: "name2 EDITED", email: "anyEmail2#mail.com"},
{ userId: "125", fullName: "name3 EDITED", email: "anyEmail3#mail.com"},
{ userId: "126", fullName: "name4 EDITED", email: "anyEmail4#mail.com"}
]
I want edit the existing user with the news values receive in the array but I don't know if I have to iterate and update each document or if I can pass the array of users to update all of them with a single query.
similar to what "insertMany" method does

Related

MongoDB: Best way query users by given names and username

I'm trying to implement a user search that looks into the user's given names and their username, while also being able to sort results by relevance.
I tried creating a text index like this
db.users.createIndex({
username: 'text',
firstName: 'text',
lastName: 'text'
},
{
name: 'text_search',
default_language: 'en',
language_override: 'language'
})
But this doesn't take into account partial terms, so if I search for "Juan F", I get the following results
{ score: 3.7, username: "juanjo", firstName: "Juan", lastName: "Rivas F" },
{ score: 2.95, username: "Juan.rodriguez", firstName: "Juan", lastName: "Rodriguez" },
{...} // 6 more
{ score: 2.2, lastName: "Fuentes", firstName: "Juan", username: "juanfuentes" }
I understand that text indexes take into account similar words, but not partial terms, so with "Juan Fuente" I get the desired result, with "Juan F", "Juan Fu", etc, I don't.
Is there a way to improve this, in order to be able to implement a search that returns results as the user types into a search box?
Edit This is the query I tried:
db.users.find(
{ $text: { $search: "juan f" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

MongoDB schema and structure

I just started learning mongoDB and mongoose here.
Is it possible to have value as key in mongoDB? For example, I'd like to have a structure like this:
Person collection:
USA: {
'John Doe': { phone: '...', somethingElse: '...' },
'Jane Doe': { phone: '...', somethingElse: '...' },
},
Australia: {
'John Doe': { phone: '...', somethingElse: '...' },
'Jane Doe': { phone: '...', somethingElse: '...' },
},
England: {
'John Doe': { phone: '...', somethingElse: '...' },
'Jane Doe': { phone: '...', somethingElse: '...' },
}
I know it's a terrible example, and I understand alternatively we can store the documents like:
{_id: 1, name: 'John Doe', address: 'USA', phone: '...', ...},
{_id: 2, name: 'John Doe', address: 'Australia', phone: '...', ...},
{_id: 3, name: 'John Doe', address: 'England', phone: '...', ...},
I guess I'm just trying to understand if storing value as key is even possible here. And if it is, how do we define the schema with mongoose?
Theoretically you could use a schema like:
const testSchema = new Schema({
countries: {
type: Map,
of: {
type: Map,
of: Object,
},
},
});
taking advantage of Map type in mongoose, then you can assign your dynamic object to contries property.
Personally I believe that second approach you mentioned is a way better idea (unless you really have a good reason for using the first one).
First reason is that having dynamic key names makes querying difficult. Instead of simple .find({name: 'John Doe'}) you need to run complicated aggregation queries like here. Basically any traversing like counting people, filtering by phone etc will be painful with dynamic keys.
Second reason is that MongoDB document has 16MB limitation so gathering too many people means you can approach that limit.

How can I update multiple fields in an array of embedded documents Mongoose?

My Mongoose model:
const userSchema = Schema({
firstName: String,
lastName: String,
books: [{title: String, author: String, isbn: Number}]
});
I'd like to add a new title & author with a book object for each POST request. My current approach giving me positional operator did not find the match error :
var User = mongoose.model("User", userSchema);
router.post('/submit', (req, res) => {
let update={
'books.$.title' : req.body.title,
'books.$.author' : req.body.author
}
User.findOneAndUpdate({_id:req.body.user_id},{$push: update},{new: true}).exec(function(err, doc) {
if (err){
console.log(err);
return;
}
res.send(doc);
});
});
I expect the following result for two form submit(POST) with different author and title in my DB:
{
_id: 'SomeId'
firstName: 'John',
lastName: 'Cena',
books: [{title: 'a', author:'b' }, {{title: 'c', author:'d' }}]
}
I don't care about ISBN in both post as it's not required field in our schema. Since my books sub-document is empty at beginning so I'm not able not set positional operator ($) properly in my code. Please help me to write findOneAndUpdate query correctly!
$push requires field name and value, in your case field name is book and value is an object {author, title}
User.findOneAndUpdate({_id:req.user._id}, {$push : { books : { author: "c", title: "d"}}})

Should I use separate collection or embed fields that I know won't be used for all models. MongoDB

Background:
I'm planning an app that will have 3 types of posts, for n number of games; Single-posts, team-posts, coach-posts. Now I'm not sure of the best Schema for a single type of post.
The posts of a certain type share a couple fundamental attributes, like: user_id, comments, status, etc. But the fields relevant to the game will be unique.
These are the two possibilities I'm considering:
1. Separate collection for each game:
As you can see the playerposts type requires different fields for each game but has a similar structure.
// game1_playerposts
{
_id: ObjectId(),
user_id: ObjectId(),
game: ObjectId(),
comments: [{
user_id: ObjectId(),
comment: String,
score: Number
}],
rank: {
name: String,
abbr: String,
img: String
},
roles: [String],
gamemode: [String]
}
// game2_playerposts
{
_id: ObjectId(),
user_id: ObjectId(),
game: ObjectId(),
comments: [{
user_id: ObjectId(),
comment: String,
score: Number
}],
level: {
name: String,
abbr: String,
img: String
},
champions: [String],
factions: [{
name: String,
abbr: String,
img: String
}]
}
2. One collection for all games:
This way I only need one collection, and will always only use the fields I need, and the rest would remain empty.
{
_id : ObjectId(),
user_id : ObjectId(),
game1 : {
game: ObjectId(),
rank: {
name: String,
  abbr: String,
img: String
},
roles: [String],
gamemodes: [String]
},
game2 : {
game: ObjectId(),
level: {
name: String,
  abbr: String,
img: String
},
champions: [String],
factions: {
name: String,
  abbr: String,
img: String
}
},
game_n {
...
},
comments : [{
user_id: ObjectId(),
comment: String,
score: Number
}],
}
What's better?
Which one of these options would be better suited? Performance is important, but I also want it to be simple to add to the Schema when we decide to add support for another game in the future.
MongoDB is schemaless.
I don't see why you have to have fields you know won't be used. Why not just have a separate document for each individual player post and that document will have the schema that relates to the type of post it is?
You can have in a single collection both of the documents that you have as examples under the "Separate collection for each game" header.
I have not worked with Mongoose, but if using it removes the benefits of MongoDB being schemaless, I don't think it would be as popular a tool as it is, so I think there's a way for it to work.

Reference field within same schema

Is it possible to reference field within same schema? See my example below. Or am I going about this wrong way?
var UserSchema = new mongoose.Schema ({
username: String,
password: String,
email: String,
foods: [{
name: String,
category: String,
ingredients: // how to reference values in the ingredients array?
}],
ingredients: [{
name: String,
category: String
}]
});
Short answer
This is a core MongoDB design decision: MongoDB relationships: embed or reference?
Storing references to objects, rather than independent copies of them, as you would do in a relational database is possible in MongoDB and often done, it just results in more and more complex queries when you need to look them up.
Long answer
If the goal is just to keep the definitions of ingredient schemas consistent, you can define a schema and use it twice. The ingredients will be stored as independent copies, e.g.
[{ username: 'bob',
ingredients: [ { name: 'Carrot', category: 'Vegetable' } , ...],
foods: [ { name: 'Salad', category: 'Lunch', ingredients: [ { name: 'Carrot', category: 'Vegetable'}, ...]}]
}, ...]
var IngredientSchema = new mongoose.Schema({
name: String,
category: String,
});
var UserSchema = new mongoose.Schema ({
username: String,
password: String,
email: String,
foods: [{
name: String,
category: String,
ingredients: [IngredientSchema] // brackets indicates it's an array,
}],
ingredients: [IngredientSchema]
});
Alternatively you can reference ingredients by objectId:
var UserSchema = new mongoose.Schema ({
username: String,
password: String,
email: String,
foods: [{
name: String,
category: String,
ingredients: [mongoose.Schema.Types.ObjectId] // IDs reference individual ingredients,
}],
ingredients: [IngredientSchema]
});
By defining IngredientSchema explicitly, each ingredient object gets its own ObjectId when it is declared. The upside to storing IDs of ingredients (rather than copies of ingredient objects) is more concise and consistent storage. The downside is there will be many more and more complex queries.
[{ username: 'bob',
ingredients: [ { _id: ObjectId('a298d9ef898afc98ddf'), name: 'Carrot', category: 'Vegetable' } , ...],
foods: [ { name: 'Salad', category: 'Lunch', ingredients: [ {$oid: 'a298d9ef898afc98ddf'}, ]}]
}, ...]
A better approach if you want to store references to Ingredients, may be to store Ingredients as its own first class collection. You'll still have many separate queries when you want to look up foods by ingredient, or ingredients by food, but the queries will be simpler.
var UserSchema = new mongoose.Schema ({
username: String,
password: String,
email: String,
foods: [{
name: String,
category: String,
ingredients: [mongoose.Schema.Types.ObjectId] // IDs reference individual ingredients,
}],
ingredients: [mongoose.Schema.Types.ObjectId]
});
if the goal is store normalized references to ingredients and search foods based on them, to quote another [SO post][1], "this is one of those cases where relational databases really shine"
See this SO post for querying subdocuments by Id:
Reading categories and number of articles in a single query
As one respondent notes, "this is one of those cases where relational databases really shine"