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" } })
Related
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
I have a 300k collection of test docs. I want to update all persons firstName and lastName to be lowercase.
const person = new Schema({
firstName: { type: String},
lastName: { type: String }
})
I've added lowecase:true to the schema but how do I update the existing documents?
I tried:
CaseFile
.find({ })
.cursor()
.eachAsync(async function (doc) {
await doc.save()
})
but i get the error
Error: Collection method find is synchronous
I also tried :
CaseFile
.find({ })
.then(docs => {
docs.forEach(doc => {
doc.save()
})
})
which gives the error:
JavaScript heap out of memory
db version v5.0.2
"mongoose": "^6.0.5",
thank you Wernfried Domscheit for the pipeline 🏄 solution:
CaseFile.updateMany({}, [
{
$set:
{
firstName: { $toLower: '$firstName' },
lastName: { $toLower: '$lastName' }
}
}]
)
.then(res => res)
Why on earth "iterate", i.e. line by line?
Use an aggregation pipeline:
db.CaseFile.updateMany({}, [
{ $set:
firstName: { $toLower: "$firstName" },
lastName: { $toLower: "$lastName" }
}
])
Let's say I have a schema with an index of:
Person.index({ name: 'text', occupation: 'text', country: 'text' })
and a collection of Person documents such as:
[
{
name: 'John Doe',
occupation: 'Doctor',
country: 'Canada'
},
{
name: 'Mary Smith',
occupation: 'Doctor',
country: 'Brazil'
}
]
When I query with a search term like the following:
PersonModel.find(
{ $text: { $search: 'Mary Doctor Canada' } },
{ score: { $meta: 'textScore' } }
).sort(
{ score: { $meta: 'textScore' } }
)
How can I ensure that the first word in the search term has a higher importance than that of the following? In this instance, how can I make it that "Mary" is received a higher text score than "Canada" in the search term?
Similarly, if the search term was instead "Brazil John", then how can I make sure that Mary receives the higher text score, since the term "Brazil" comes before "John"?
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.
I can do a find in mongodb like:
db.user.find({ "show": true}, { firstname: 1, lastname: 1, email:1 })
Now I'd like to be a little more specific to let people hide their email address.
So what I'd like is to query if "show":true then get firstname:1, lastname:1 AND (email:1 iff show_email:true)
worst case I can take all the info and filter it out on the back end with a map function but I wondered if there was a nice query-able way??
The truthiness of the projection object's fields are all that matter, so you can just do it as:
db.user.find({ "show": true }, { firstname: 1, lastname: 1, email: show_email })
Since you didnt specify any driver specification, I am betting on this, the MongoShell
if(show_email){
db.user.find({ "show": true}, { firstname: 1, lastname: 1, email:1 })
}
else{
db.user.find({ "show": true}, { firstname: 1, lastname: 1})
}