I'm implementing persons list inside my Meteor app. Every person has a lastName and firstName. I want to be able to search by full name or it's part. Of course search must be case insensitive. There will be about 500 persons, so I need pagination & I can't just filter the results on client side.
My general idea is to generate additional field fullName upon insert or update of a person:
fullName: lastName.toLowerCase() + ' ' + firstName.toLowerCase(),
And then just use it inside the find method:
PersonsCollection.find({ fullName: {$regex : searchString}, ... })
I'm pretty new to Meteor, Mongo and backend in general. I spent some time researching, but still confused. My questions are:
Is there any real reasons against my implementation besides it's kinda ugly to create additional field?
Is there a conventional way to do this in Meteor?
It is usual to create an additional field.
But you can try to use an $or:
PersonsCollection.find(
{
$or: [
{ firstName: new RegExp(searchString, 'i') },
{ lastName: new RegExp(searchString, 'i') }
]
}
);
Don't forgot to create indexes to your search fields.
Related
I am working on MongoDB and C# client.
I am having the following data in collection:
1- { _id: xId, Desc: "ABC BLX CPO"}
2- { _id: yId, Desc: "ZNP CKL IOP ERKK"}
3- { _id: zId, Desc: "POL MIU WER XDF RRF"}
Now the issue is I have to search for dynamic values in Desc field depending upon user requirements. e.g
Searching for one string "ABC" at time,
Next time may be I have to search for two strings in Desc field "XYZ" and "IOP"
Next time may be I have search for five strings
I create a list of these search keywords(whether it is one, two or any number) and loop it through and search one by one :
list = ["ABC", "XYZ", "IOP"];
foreach(string item in list)
{
findInDB(item);
}
void findInDB(string val)
{
db.Collection.find({"Desc" : /val /i}) //pseudo code
}
Although the above code is working for me, but it takes too much time as I have a very rich database.
I am looking for some efficient way to achieve the goal. kindly guide me if there is any possibility.
I have to create a collection of document and I have a doubt about the right design.
Each document is an "identity"; each Identity has a list of "partner Data"; each partner data are defined by an ID and a set of Data.
One approach can be (1):
{
_id: ...
partners: [
{
id: partner1,
data: {
}
},
{
id: partner2,
data: {
}
},
]
}
Another approach can be (2)
{
_id: ...
partners: {
partner1: {
data: {
}
},
partner2: {
data: {
}
},
]
}
I prefer the first one, but considering that I could have million of these identities, which could be the most performed schema?
A typical query can be: "how many identities have partner with ID N".
With the second example, a query can be:
db.identities.find({partner.partnerName: {$exists:true}})
With first approach, how can I get this count?
The second solution is more easy to handle Server Side; each document will have a list where each KEY is the partner ID, so instead of scan all document, I can simply get partner data by key...
What do you think about these solutions? I prefer the first one but the second I think that is more "usable"...
Thanks
I prefer the first one, but considering that I could have million of these identities, wich could be the most performed schema?
If you going to have millions of identities, then both approaches
are not really scalable .
Each document in mongo has a size limit (16MB) (read about it here)
In case you are going to have really lot's of identities ,
the scalable approach would be to create a different collection,
only for the relations and partnership data.
Now , I also want you to consider how you treat "partnership",
if I'm a user and I got you on my partners list , will you see me as a partner on your list ?
In case we both see each other as partners , then mongo-db may not be the best solution. graph db's are more appropriate for dealing with relations of this type.
All solutions within mongo for two-ways relations will be built on double updates (Your id on my partner's list , My id on your partner's list).
(In SQL you could add an extra condition for joining but not in mongo) ,
so you don't need to save twice partnerships . (me and you , you and me)
just you and me.
Do you see where is going this way ?
If you need to go only in one way,
Then just create a second collection , "partnerships" ,
{
_id: should be uniqe,
user_id: 'your_id',
partner_id: 'his_id'
data: {} or just flatten the fields into the root object.
}
Please notice that you create a row for each partnership !
Then you could use $lookup in order to query for a user with all of his
partners .
something like:
db.getCollection('partners').aggregate([
{
$lookup: {
from: 'parterships',
localField: '_id',
foreignField: 'user_id',
as: 'partners'
}
},
{
$project: {
name: 1,
partners: 1,
num_partners: { $size: "$partners" }
}
}
])
Read more about the aggregation stages here.
In case you are not going to have lot's of partnership's Then please continue with
your first approach which is good .
The second approach will make most queries to this collection pretty weird and you will always have to write code in order to query this table .
It won't be "straight forward" mongo queries .
I have a question, lets say I have a collection called contact :
[
{"firstName": "Adam", "lastName":"Peter", "email":"adam#peter.com"},
{"firstName": "Adam", "lastName":"John", "email":"adam#john.com"},
{"firstName": "Adam", "lastName":"Petkovic", "email":"adam#petkovic.com"}
]
What I want is to search specifically, for example: I want to search "Adam peter" then I want to have a result of the first one ONLY which has Adam and peter.
I use meteor + mongo + react for my application.
Any suggestion / recommendation would be high appreciated.
Thanks for all the answers, but probably I need to ask more specific in order to get more appropriate answer.
Scenarios:
I only have 1 text box to search all the fields.
So:
when I enter "Adam", I expect to have 3 results. but when I enter "Adam Peter" I expect to have 1 result only.
When I enter "peter.com" it should have 1 result
When I enter "John", it should have 1 result
When I enter "Adam Pet" it should have 2 results.
From the answer here, below query should work fine.
db.contacts.find( { firstName: /^Adam$/i, lastName: /^peter$/i });
The query in MongoDB is case sensitive, if you want to query contact by ignoring case, you should use a regular expression, but it may not efficient.
db.contact.findOne({firstName: /^adam$/i, lastName: /^peter$/i})
it will much better if you always save these name value in lowercase, and query in lowercase
db.contact.findOne({firstName: 'adam', lastName: 'peter'})
Assuming that the rules that you are applying are:
If a single word, then that could match any field
two words mean "firstname surname"
In that case, you can't use text indices, but instead need to do some work before the mongo search.
First, split the words on whitespace, and then determine if there are one or two words. If there is one word, check that against all fields. If there are two, then only check the first word against the first name, and the second against the lastname.
// assuming input is in variable call 'term'
var words = term.trim().split(/\s+/) || [];
if(words.length === 0) {
return;
}
var first = new RegExp(words[0], 'i');
if(words.length === 2) {
var second = new RegExp(words[1], 'i');
return Contact.find({firstName: first, lastName: second});
else if(words.length === 1) {
return Contact.find({$or: [ {firstName: first}, {lastName: first}, {email: first}]})
}
Also, depending on how large your collection is, it might be better to wrap this up into a Meteor method, so that the search takes place on the server. Otherwise, you will have to publish the whole collection on the client to be able to do the search. That might be fine for a small collection though.
UPDATE:
Based on your examples, I think your rules are:
1. Search terms are combined with AND operator (e.g. Adam Pet returns two rows, not three).
2. Search terms use regular expression matching (Pet matches even though it's not any of the words).
Rule 2 means that text indices won't work, so you will need to build up a complex regex query document using $and and $or for each item:
// assuming input is in variable call 'term'
var words = term.trim().split(/\s+/) || [];
var query = {
"$and": []
};
words.forEach(function(token) {
var reg = new RegExp(token);
var innerQ = {"$or": [ {firstName: reg}, {lastName: reg}, {email: reg}]};
query["$and"].push(innerQ);
});
return Contact.find(query);
being a newbie to mongo, stuck in a conditional query:
I want to perform a search on the basis of 3 criteria, first name, last name and email id:
below query works perfect when all the fields exist:
db.students.find( { $and: [ { first_name: /^Abc$/i }, { last_name: 'Xyz'},{email_id:'gd#he'} ]})
the problem is when I don't give an email id , the query dosen't returns any result as it considers the email id to be null and searches for the combination 'Abc Firtis null',
where as I want the below scenario to be fulfilled:
I have a collection of students:
- FirstName: 1. ABC 2. ABC 3.ABC
- LastName: 1.XYZ 2. XY 3. XZ
- EmailID: 1.abc#xyz 2.Ab#xy 3.Ab#xz
if one enters only the first name in the search it should return all the 3 results
if user enters first name and last name it should return first two results and if the user enters all three details it should return only 1 result.
Any leads would be highly appreciated.
You seem to be talking about "input" data being different for the queries you want to issue and how to contruct the query to ignore fields as criteria for which you have no input.
This is all really about how the input is being collected as to how you handle it, but it all boils down to that you "conditionally build" the query ( which is just a data structure anyway ) rather than statically define a query and somehow ignore null or empty data.
So if you have seperate variables, then you test each value and build the query:
var firstName = "abc",
lastName = "xy,
email = null,
query = {};
if (firstName) {
query.firstName = new RegExp("^"+firstName,"i")
}
if (lastName) {
query.lastName = new RegExp("^"+lastName,"i")
}
if (email) {
query.email = new RegExp("^"+email,"i")
}
db.students.find(query)
This would build a query object that would end up like this based on the inputs:
{ "firstName": /^abc/i, "lastName": /^xy/i }
So since there is no value in email then the condition is not included. The end result is the condition not provided is not even queried for and then you get the relevant matches.
The same approach is basically simplified if you have some structured input to begin with:
var params = {
firstName = "abc",
lastName = "xy"
};
var query = {};
Object.keys(params).forEach(function(key) {
if (params[key])
query[key] = new RegExp("^"+key,"i");
});
db.students.find(query);
And it's the same thing, but since you have all parameter keys in one place then you can iterate them to build the query rather than test individually.
This is generally the case where you have input from something like a web request with parameters that come into req.params or even req.body depending on your method of input. So if you structure your code to accept input into a similar object ( or already have it ) then you use it to build your query.
Also note that all MongoDB query arguments are implicitly an "AND" condition by definition, so there is rarely any need to use $and unless you explicitly have multiple conditions to meet for the same document property. Even then there are generally better syntax alternates.
No Need to give and you can simply try this
find( { first_name: { $regex: '/^Abc$/i' }, last_name:'Xyz',email_id:'gd#he'}
I have a document that has an id of another document from a different collection embedded in it.
My desired result is to return (I'm using python and pymongo) the all the fields of the first collection, and all of the friends from the document that was embedded.
I understand mongo doesn't do joins and I understand I'll need to make two queries. I also don't want to duplicate my data.
My question is how to piece the two queries together in python/pymongo so I have one results with all the fields from both documents in it.
Here is what my data looks like:
db.employees
{_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Joe Smith', title: 'junior',
manager: ObjectId("4d85c7039ab0fd70a117d730") }
db.managers
{_id: ObjectId("ObjectId("4d85c7039ab0fd70a117d730"), name: 'Jane Doe', title: 'senior manager'}
desired result
x = {_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Joe Smith', title: 'junior',
manager: 'Jane Doe' }
Your basically doing something that Mongo does not support out of the box and would actually be more painful than using the two records separately.
Basically (in pseudo/JS code since I am not a Python programmer):
var emp = db.employees.find({ name: 'Joe Smith' });
var mang = db.managers.find({ _id: emp._id });
And there you have it you have the two records separately. You cannot chain as #slownage shows and get a merged result, or any result infact since MongoDB, from one qauery, will actually return a dictionary not the value of the field even when you specify only one field to return.
So this is really the only solution, to get the the two separately and then work on them.
Edit
You could use a DBRef here but it is pretty much the same as doing it this way, only difference is that it is a helper to put it into your own model instead of doing it youself.
If it works it should be something like:
db.managers.find({
'_id' => db->employees->find({ ('_id' : 1),
('_id': ObjectId("4d85c7039ab0fd70a117d733") }))
})
updated