MongoDb Text Search On Different fields - mongodb

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.

Related

How to create a large number of documents with embedded documents in MongoDB?

I was looking at the example here:
_id: "joe",
name: "Joe Bookreader"
}
// address documents
{
patron_id: "joe", // reference to patron document
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
{
patron_id: "joe",
street: "1 Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}
Where they embed the address document inside the person document. The example makes sense if you have a single item in your person document, but how would you scale this if you had a person document with 100 items and addresses for each of them? Is there a way to create a schema and upload the data or do you have to manually embed the address documents for each person?
You should read about the Data Model Design section in their documentation. For this pattern the Relationships with Document References would match.
In short your User document would have an array containing the IDs of the referenced Address documents. But the Addresses are stored in a different collection. You would need a second query to select them.
Example from the documentation:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [123456789, 234567890, ...]
}
{
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English"
}
{
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English"
}

Mongodb Mongoose Schema.Types.Mixed Text Search

good day all,
need some help figuring out mongodb, mongoose and text search.
i have this as a schema
var itemSchema = new schema({
itemID: Number,
description: {type: Schema.Types.Mixed}
}
and in the collection in mongodb i have something like this
[
{
itemID:1
description: {
en: "car string in itemid 1"
fr: "voiture string in itemid 1"
es: "spanish string in itemid 1"
}
},
{
itemID:2
description: {
en: "motorcycle string in itemid 2"
fr: "motocyclette string in itemid 2"
es: "spanish string in itemid 2"
}
}
]
how do i do a text search like let's say i'm looking for "voiture" in description.
is the Schema.Types.Mixed appropriate for this kind of search or should it be changed. and how should i index such a field for better performance
thank you
You can only have one text index per collection, so rather than have an object with properties as strings I would suggest an array of objects that have the text and the language:
description: [
{ lang: "en", text: "car string in itemid 1" }
]
Then you could create an index like createIndex({ "description.lang": 1, "description.text": "text" }). Then query like query({ "description.lang": "fr", { $text: {$search: "voiture" } }). This assumes the language is known ahead of time which would be a more efficient query, but you could also create the index without the lang in which case all of the "description.text" properties would be searched.

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

Text query through referenced objects with 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})

Using functions for creating an index in MongoDB?

I have a blog that allow users to vote for articles. The documents of an article looks like:
{
title: "title of post",
body: "text text text text text text",
votes: [
{user: "user1", points: 2},
{user: "user13", points: 1},
{user: "user30", points: 1},
{user: "user2", points: -1},
{user: "user51", points: 3},
],
sum_of_votes: 6
}
I'd like to sort by the number of votes the post received. At the moment I added a field sum_of_votes that needs to be updated every time somebody voted. Since votes already contains the raw-data for sum_of_votes I thought there might be a more elegant way. I came up with two ideas:
Using a function while creating an index, for example:
db.coll.ensureIndex({{$sum: votes.points}: 1})
Having a dynamic field. The document could look like this:
{
title: "title of post",
body: "text text text text text text",
votes: [
{user: "user1", points: 2},
{user: "user13", points: 1},
{user: "user30", points: 1},
{user: "user2", points: -1},
{user: "user51", points: 3},
],
sum_of_votes: {$sum: votes.points}
}
In those cases I only had to update the votes-array. Is something like this possible in MongoDB?
Depending on how you do the update on the votes array, either as updating the document by inserting a new vote in the votes array or updating an existing vote, an approach you could take is use the $inc operator on the sum_of_votes with the value you are using to update the votes.points with. For example, in the case where you are inserting a new vote for a user with value in variable userId and the actual vote points in variable points (which must be a number), you could try:
db.articles.update(
{"_id" : article_id},
{
"$addToSet": {
"votes": {
"user": userId,
"points" : points
}
},
"$inc": { "sum_of_votes": points }
}
)
And you will need to factor in Neil Lunn's point where you wouldn't want the same user to vote more than once in your updates.