Text query through referenced objects with MongoDB - mongodb

I have the following structure.
books collection:
{
_id: "book_1",
title: "How to build a house",
authorId: "author_1"
}
{
_id: "book_2",
title: "How to plant a tree",
authorId: "author_2"
}
authors collection:
{
_id: "author_1",
name: "Adam Adamson"
}
{
_id: "author_2",
name: "Brent Brentson"
}
I want to make a case insensitive free text search with the string "b" through the books collection and find all books that either has the "b" in the title or has an author with "b" in the name.
I can embed the author in the book object just to be able to make the query. But if the author name changes in the authors collection, the embedded authors object will have the wrong name.
{
_id: "book_2",
title: "How to plant a tree",
authorId: "author_2",
author:
{
name: "Brent Brentson"
}
}
What would be a good way to solve this problem?

You could use the following queries where the first gets the array of author ids that match the given regex expression query on the authors collection (using the map() method of the find() cursor) and the second query applies that array in the books collection query using the $in operator as well as using the regex pattern to find books that have "b" in the title:
var authorIds = db.authors.find({"name": /b/i}).map(function (doc) {return doc._id});
db.books.find({$or: [{"title": /b/i}, {"authorId": {"$in": authorIds} }]})
Result:
/* 0 */
{
"_id" : "book_1",
"title" : "How to build a house",
"authorId" : "author_1"
}
/* 1 */
{
"_id" : "book_2",
"title" : "How to plant a tree",
"authorId" : "author_2"
}
-- UPDATE --
Thanks to #yogesh for suggesting another approach which uses the distinct() method to get the author ids list:
var authorIds = db.authors.distinct("_id", {"name": /b/i})

Related

MongoDb Text Search On Different fields

My collection looks this:
{ _id: 1, author: "Jack London", bookName: "The Sea-Wolf" }
{ _id: 2, author: "Alex Kershaw", bookName: "Jack London" }
I want to be able to search by author or by bookName only, but MongoDB allows you to create only 1 text index. Thanks for help.
For Example:
I wanna search for author only :
db.collection.find({$text:{$search:"Jack London"}})//search in 'author' field
Or by bookName only :
db.collection.find({$text:{$search:"Jack London"}})//search in 'bookName' field
If I have compound index {author: "text", bookName: "text"} it will return me both entries, which is not what I want.

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

How to return the results of a query using find() for a deep search level with MongoDB?

I have the following structure of a collection:
teams: [{
name: String,
address: String,
players: [{
name: String,
age: Integer
}]
}]
I am trying to search by 'name' of the player. I am trying this:
db.teams.find({ players: { $elemMatch: { name: /john/ } } }, { 'players.$':1 })
This returns some results, but not all. And this:
db.teams.find({ players: { $elemMatch: { name: /john/ } } })
This last returns all the records of the collection.
I need to find by a player with its name as parameter.
What is right query?
This is a common misconception about MongoDB. Queries in MongoDB do not match subdocuments; they do not return subdocuments. Queries match and return documents in the collection, with returned fields adjustable via projection. With your document structure, you cannot write a query that matches and returns one of the subdocuments in the array. You can (and have, in your examples), written a query to match documents containing an array element that matches a condition, and then project just the first matching element in the array. It's important to understand the difference. If you want to match and return player documents, your collection should contain player documents:
{
"name" : "John Smalls",
"age" : 29,
"team_name" : "The Flying Sharks",
"team_address" : "123 Fake Street"
}
The player documents reference their team. To find a player by name,
db.players.find({ "name" : /^John"/ })
To find all the players on a team,
db.players.find({ "team_name" : "The Flying Sharks" })
Your life will be much easier if you search for players by querying for player documents instead of trying to match team documents.

Update Subdocument in Mongodb that stores id(s)

I am developing an application and we have decided to implement database with Mongodb, so I'm truly new to it. In database there is a collection for each company and we need to stores the ID(s) of each category of products of the company in a subdocument of company collection, Let's say we need to insert the following Object into collection:
{
name : "comapnyX"
address : {
"street" : "main street",
"ZipCode" : "12345"
},
categories : [
{ "name" : "category1" },
{ name" : "category2" }
]
}
An then if later on we decide to update the categories, we need add one more category to this subdocument, how do I have to create that update?
please also let me know if this is not a good practice from Datamodeling point of view in Mongodb
You can use the update method with the $push operator to add a new category:
db.collection.update( <query>,
{ $push: { categories: {name: "category3" } }
)
MongoDB Docs on $push: http://docs.mongodb.org/manual/reference/operator/update/push/

mongodb query without field name

I would like query all objects that have a field containing a specific value. For example, I have two documents:
{"123": "apple", "217": "pear", "179": "orange"}
{"831": "pear", "189": "grapes"}
and would like to get all objects that has a field whose value is "apple", but I do not know the name of the field. Is it possible to specify a query in MongoDB to achieve this? (The numerical keys in the objects are children ids, and the values in the objects are long GUIDs)
Unfortunately, MongoDB does not support any method of querying all fields with a particular value. There is an existing Jira ticket requesting this enhancement: https://jira.mongodb.org/browse/SERVER-1248 . Feel free to comment, vote, or follow that ticket.
In the meantime, the usual way that this is handled is to change the MongoDB schema. For your example, you would change your existing schema:
{"123": "apple", "217": "pear", "179": "orange"}
{"831": "pear", "189": "grapes"}
And you might structure it something like this:
{ tags: [
{ cid: "123", value: "apple" },
{ cid: "217", value: "pear" },
{ cid: "179", value: "orange" },
]
}
{ tags: [
{ cid: "831", value: "pear" },
{ cid: "189", value: "grapes" },
]
}
Once you've done this, you can perform the follwing query to find all of the desired documents:
db.docs.find( {'tags.value': "apple" } )
Note that this schema allows you to index the 'tags.cid' and 'tags.value' fields, which your original schema does not.
I hope this helps.
-William
You can use Full-text Index on all fields.
use dbname
db.collectionname.ensureIndex(
{ "$**": "text" },
{ name: "TextIndex" }
)
Then Search Using :
DB db = mongoClient.getDB("dbname");
DBCollection coll = db.getCollection("collectionname");
BasicDBObject query = new BasicDBObject();
query.append("$text", new BasicDBObject("$search", searchstring));
DBCursor cursor = coll.find(query);