How can I search two columns (one of which only appears in a few records)? - mongodb

I am performing a search on the following 2 columns of a document: name and displayName. However, only a few records have displayName column. When a record has both, I want to search both fields (name and displayName) for the possible match. Otherwise, if the record doesn't have the column displayName, I just want to search name instead. So I thought I'd do an OR. This however will only search fields that have both columns - although I want to search all records.
How can I update the query to achieve the above? Here's what I tried:
function search(search) {
return this.find({ $or: [
{"displayName" : {"$regex" : ".*"+ search +".*", "$options": "-i"}},
{"name" : {"$regex" : ".*"+ search +".*", "$options": "-i"}}
]}).exec();
}
So on the query above, the logic tells me that if displayName column doesn't exist, the first check will return false so it will then search name which will return true/false based on the match. I am obviously missing something here because even if the second check would be true, the overall result comes out false (which is more like AND behavior instead of OR).
Data Example:
{ id: 3, name: "John Steinbeck", displayName: "Steinbeck" }
{ id: 4, name: "John Lennon" }
{ id: 5, name: "Doe", displayName: "John" }
{ id: 5, name: "Harry Potter", displayName: "Harry" }
Search word:
john
Expected Result:
{ id: 3, name: "John Steinbeck", displayName: "Steinbeck" }
{ id: 4, name: "John Lennon" }
{ id: 5, name: "Doe", displayName: "John" }
Later Edit
This query does seem to work after all, the problem turned out to be the data and not the query. Human error. My apologies :)

change your query:
db.getCollection('user').find({ $or: [
{"name" : {$regex: "john", $options: "$i"}},
{"displayName" : {$regex: "john", $options: "$i"}}]})

You've to different conditions:
Both field exist and have the correct value
Only name exist and have the correct value
use $exist and $or!
for example:
db.col.find({ $or: [ { name: {$regex : ".*John.*"}}, displayName: {$regex : ".*Steinbeck.*"}}, { name: "john", { displayName: { $exists: false } } } ] } )

Related

Update a nested field with an unknown index and without affecting other entries

I have a collection with a layout that looks something like this:
student1 = {
"First_Name": "John",
"Last_Name": "Doe",
"Courses": [
{
"Course_Id": 123,
"Course_Name": "Computer Science",
"Has_Chosen_Modules": false
},
{
"Course_Id": 284,
"Course_Name": "Mathematics",
"Has_Chosen_Modules": false
}
]
};
I also have the following update query:
db.Collection_Student.update(
{
$and: [
{First_Name: "John"},
{Last_Name: "Doe"}
]
},
{
$set : { "Courses.0.Has_Chosen_Modules" : true }
}
);
This code will currently update the Computer Science Has_Chosen_Modules value to true since the index is hardcoded. However, what if I wanted to update the value of Has_Chosen_Modules via the Course_Id instead (as the course might not necessarily be at the same index every time)? How would I achieve this without it affecting the other courses that a given student is taking?
You can select any item in the sub array of your document by targeting any property in the sub array of your document by using dot .
You can easily achieve this by the following query.
db.Collection_Student.update(
{
First_Name: "John",
Last_Name: "Doe",
'Courses.Course_Id': 123
},
{
$set : { "Courses.$.Has_Chosen_Modules" : true }
}
);
Conditions in search filter are by default treated as $and operator, so you don't need to specifically write $and for this simple query.

MongoDB: Text search (exact match) using variable

MongoDB 3.4
I have a value in a variable. val1 = "Fort Minor"
I need to search in a collection stores (with text index on name field) with documents as
db.stores.insert([
{ _id: 1, name: "Java Hut", description: "Coffee and cakes" },
{ _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },
{ _id: 3, name: "Coffee Shop", description: "Just coffee" },
{ _id: 4, name: "Fort Coffee", description: "Discount clothing" },
{ _id: 5, name: "Java Shopping", description: "Indonesian goods" }
])
when I text search the collection using the variable
db.City.find({$text:{$search: val1}})
it returns the _id : 4 document, since it contains Fort.
I need to do an exact match but using the variable.
The above search should return only when val1 = "Fort Coffee"
When you do text search, it has many internal steps like tokenizing your work, analyzing the lowest stem of the word(e.g. to match cooking & coocked as both are derived from basic work "cook"). If you are expecting an exact match, you are not actually looking for a texual search but you are looking for a normal query.
Hence,
If you do this :
db.City.find({$text:{$search: "Fort Minor"}})
It will try to find all documents which have fort, minor, minority, forting etc in it.
If you do below :
db.City.find({ name : "Fort Minor"})
If will give you the document with exact match.
However, here is the catch :
IF you search all lowercase like :
db.City.find({ name : "fort minor"})
It will not give you what you are expecting.
To resolve this, go for regular expression :
db.user.find( { name: /^Fort Minor$/i } );
Answers above are actually not helpful. I also came across the same issue, I found a solution with new template literals of Javascript. here is the solution:
const val1 = "Fort Minor"
const val2 = `\"${val1}\"`
myDatabase.find({$text:{$search: val2}})
For more information on template literals: https://www.w3schools.com/js/js_string_templates.asp
If you do this :
query = {"$search" :"(\"{}\"".format(val1)}
city = db.City.find({"$text": query})

Is it possible to modify find's matching criteria according to the $cond

I have a flag and a key.
If the flag is true I want to list the documents whose name field includes the key.
Otherwise- if the flag is false- I want to search the key on name or surname fields.
Here the example of collection;
{id: 1, name: "green" , surname: "brown"},
{id: 2, name: "black" , surname: "brown"},
{id: 3, name: "brown" , surname: "yellow"}
if flag: true, key: brown, I expect;
{id: 1, name: "green" , surname: "brown"},
{id: 2, name: "black" , surname: "brown"},
{id: 3, name: "brown" , surname: "yellow"}
if flag: false, key: brown, I expect;
{id: 3, name: "brown" , surname: "yellow"}
How can I do this find using $cond?
I can do it as;
if(flag){
db.collection.find({name: key})
}else{
db.collection.find({$or: [{name: key}, {surname: key}])
}
Is something like that possible? But this throws an error. How can I edit?
db.collection.find({$cond: [ if: { flag }, then: {$or: [{name: "brown"}, {surname: "brown"}]}, else: {name: "brown"} ]})
$cond is an aggregation pipeline operator and you won't be able to use as you were expecting. It would be easy to do it on client side as you have mentioned.
If you MUST do it in query itself; will have to manipulate conditions such that
a) in one query, a true value for flag results condition to be true and false results condition to be false.
b) in second query, a true value for flag results condition to be false and false results condition to be true.
And then [or] above conditions.
You could use $exists of an attribute that always exists in document to give true for a flag = true; and use $exists of an attribute that will never exist in document to give false for a flag = true.
Here is a solution using above logic:
var flag = true;
var key = "brown";
db.collection.find({
$or : [ {
$and : [ { _id : { $exists : flag } },
{ name : key } ]
}, {
$and : [ { _id_nonExist : { $exists : flag } },
{ $or : [ { name : key }, { surname : key } ] } ]
} ]
})
I have gone by your description "If the flag is true I want to list the documents whose name field includes the key.
Otherwise- if the flag is false- I want to search the key on name or surname fields."

MongoDB Regex $and $or Search Query

I am trying to construct a query that will accept multiple fields that can be searched over using regex for partial field matching that also has a hard constraint on other fields.
Example:
Collection: "Projects"
Required Information: { propertyId: "abc", clientId: "xyz" }
Fields to be Searched: name, serviceType.name, manager.name
Currently, I have a query like this, but if there are no results it returns all the results, which isn't helpful.
{
'$and': [
{ propertyId: '7sHGCHT4ns6z9j6BC' },
{ clientId: 'xyz' },
{ '$or':
[
{ name: /HVAC/gi },
{ 'serviceType.name': /HVAC/gi },
{ 'manager.name': /HVAC/gi }
]
}
]
}
If anyone has any insight into this it would be much appreciated.
Example Document:
{
_id: "abc",
propertyId: "7sHGCHT4ns6z9j6BC",
clientId: "xyz"
name: "16.000.001",
serviceType: {
_id: "asdf",
name: "HVAC"
},
manager: {
_id: "dfgh",
name: "Patrick Lewis",
}
}
The expected result is to only find documents where propertyId = 7sHGCHT4ns6z9j6BC AND one at least one of the following keys: name, serviceType.name, or manager.name match an inputted string, in this case, it's HVAC and if none of the regex fields match, then return nothing.
UPDATE
The issue was with MongoDB, after restarting it, everything worked.
Try following script:
db.collection.find({
$and:[
{propertyId:"7sHGCHT4ns6z9j6BC"},
{
$or:[
{name: /HVAC/i},
{"serviceType.name": /HVAC/i},
{"manager.name": /HVAC/i}
]
}]
})
Query above will return a document or documents if and only if propertyId matches and either of name, serviceType.name or manager.name matches desired regex.

mongodb get all keys within a string

Is it possible to search a string if I have some data stored like
Names:
{
name: 'john'
},
{
name: 'pete'
},
{
name: 'jack smith'
}
Then I perform a query like
{ $stringContainsKeys: 'pete said hi to jack smith' }
and it would return
{
name: 'pete'
},
{
name: 'jack smith'
}
I'm not sure that this is even possible in mongoDB or if this kind of searching has a specific name.
Yes, quite possible indeed through the use of the $text operator which performs a text search on the content of the fields indexed with a text index.
Suppose you have the following test documents:
db.collection.insert([
{
_id: 1, name: 'john'
},
{
_id: 2, name: 'pete'
},
{
_id: 3, name: 'jack smith'
}
])
First you need to create a text index on the name field of your document:
db.collection.createIndex( { "name": "text" } )
And then perform a logical OR search on each term of a search string which is space-delimited and returns documents that contains any of the terms
The following query searches specifies a $search string of six terms delimited by space, "pete said hi to jack smith":
db.collection.find( { "$text": { "$search": "pete said hi to jack smith" } } )
This query returns documents that contain either pete or said or hi or to or jack or smith in the indexed name field:
/* 0 */
{
"_id" : 3,
"name" : "jack smith"
}
/* 1 */
{
"_id" : 2,
"name" : "pete"
}
Starting from Mongodb 2.6 you can search mongodb collection to match any of the search terms.
db.names.find( { $text: { $search: "pete said hi to jack smith" } } )
This will search for each of the terms separated by space.
You can find more information about this at
http://docs.mongodb.org/manual/reference/operator/query/text/#match-any-of-the-search-terms
However, it will work only with individual terms. If you have to search for exact phrase which is not a single term, e.g. you want to find "jack smith', but not "smith jack", it will not work, so you will have to use search for a phrase.
http://docs.mongodb.org/manual/reference/operator/query/text/#search-for-a-phrase which searches for exact phrases in the text.
If you need more advanced text-based search features in your application, you might consider using something like Elasticsearch https://www.elastic.co/guide/en/elasticsearch/reference/1.3/query-dsl-mlt-field-query.html.
Zoran