My webapp consists of just a list of topics and a search field to filter them with.
I started by inserting an object into my MongoDB:
$meteor mongo
meteor:PRIMARY> db.topics.insert({title:"Hello World!"});
I wrote this in my .coffee file to verify that it showed up as expected:
topics = new Mongo.Collection "topics"
if Meteor.isClient
Template.body.helpers {
topics: -> topics.find {}
}
That worked fine. So I decided to move to having a fixed filter of "Hello". I replaced topics.find {} with:
topics.find {$text: {$search: "Hello"}}
This caused my list to appear empty. I also tried this:
topics.find {title: {$text: {$search: "Hello"}}}
But that also did not work. What am I missing here?
(Also, this is my first time using CoffeeScript. The generated JavaScript file looked right to me, but if you see any unnecessary punctuation or other bad habits in here, please point it out to me. As someone who tends to help Python programmers, I know how obnoxious it is for people new to the language to be littering it with unnecessary semicolons.)
Update
I have added in the following server code as directed by Blakes Seven in his answer:
if Meteor.isServer
Meteor.startup -> topics._ensureIndex {title: "text"}
If I query the MongoDB from the command line as below, it works fine:
$meteor mongo
meteor:PRIMARY> db.topics.find({$text: {$search: "Hello"}})
{ "_id" : ObjectId("559495cf7b8f68a693d8a3a8"), "title" : "Hello World!" }
However, within my application, in the client code, topics.find {$text: {$search: "Hello"}} is still not working.
Update 2
Blakes Seven wanted to see this:
$meteor mongo
meteor:PRIMARY> db.topics.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "meteor.topics"
},
{
"v" : 1,
"key" : {
"_fts" : "text",
"_ftsx" : 1
},
"name" : "title_text",
"ns" : "meteor.topics",
"safe" : true,
"weights" : {
"title" : 1
},
"default_language" : "english",
"language_override" : "language",
"textIndexVersion" : 2
}
]
You need to create a "text index" on your collection before you can perform $text search queries.
You can create this index to deploy in Meteor.startup under server/indexes as:
Meteor.startup(function() {
topics._ensureIndex({ "title": "text" })
]);
MongoDB 3.x series recommends it's method .createIndex(), but the current meteor methods are like still calling .ensureIndex() at present.
This isn't really that much of a concern as the preferred method has expanded sytax to support additional features mostly, and none of these should be a general concern.
If you find a need for those features, you can always script "index" deployment outside of your Meteor application code.
Just as a sidenote, it's a general convention to refer to a collection model both in the "singular" rather than "plural" form, as the collection itself is the "plural" and not the model. Also a general convention is a "capital" first.
Related
Given a collection of MongoDb documents with a property "myContacts" like this:
{
"_id": 123,
"myContacts" : {
"contacts" : {
"10" : {
"_id" : NumberLong(10),
"name" : "c1",
"prop" : true
},
"20" : {
"_id" : NumberLong(20),
"name" : "c2"
},
}
}
}
I want to select all documents, where at least one contact lacks the "prop" field.
I figured out a general query:
db.getCollection('xyz').find({ 'myContacts.contacts.???.prop': { $exists: false } })
The problem is that IDs of the contacts are part of the path and I cannot know them ahead. I want sth like 'myContacts.contacts.$anyChild.prop', but cannot find sth similar in the mongo docs.
Does it mean there is no way to do it?
PS: I cannot change the document structure, a live app uses it. I've spent some time with Google and my bet it's not possible. I however would like an opinion from people who have experience with Mongo.
Thank you guys for helpful comments, this got me going! I could get the results I wanted with:
db.getCollection('xyz').aggregate([{$project: {_id:1, contacts:{$objectToArray: "$myContacts.contacts"}}}, {$match: {"contacts.v.prop" : null}}])
I am developing a web platform for people who is learning languages, I have a collection for users like that:
{
"_id" : ObjectId("54a15e7e3453b5741a6c7be0"),
"name" : "Miguel Lopez",
"lng" : [
{
"ES" : 5
},
{
"EN" : 4
},
{
"IT" : 3
}
],
"email" : "miguel#gmail.com",
"password" : "123456"
}
In "lng" field I save the languages that this user is learning, each language have a level between 1 and 5. To get the list of users who are learning "ES" and have level 5, I use this query:
db.users.find({lng : {ES:5}})
And it´s working perfectly, but when I want the get users who have a specific language (for example "EN") with a level less than 5, I use this query:
db.users.find({lng : {EN:{$lt: 5}}})
But this query doesn´t work, and I dont know the reason, when I try to execute this query in Robomongo I get this message: "Script executed succesfully, but there is no results to show"
You need to use $elemMatch or dot notation
db.users.find({lng : {$elemMatch: {EN:{$lt: 5}}}})
db.users.find({ "lng.EN" : { "$lt" : 5 } })
This my code:
db.test.find() {
"_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
"title" : "Sir",
"name" : {
"_id" : ObjectId("4d3ed089fb60ab534684b7ff"),
"first_name" : "Farid"
},
"addresses" : [
{
"city" : "Baku",
"country" : "Azerbaijan"
},{
"city" : "Susha",
"country" : "Azerbaijan"
},{
"city" : "Istanbul",
"country" : "Turkey"
}
]
}
I want get output only all city. Or I want get output only all country. How can i do it?
I'm not 100% about your code example, because if your 'find' by ID there's no need to search by anything else... but I wonder whether the following can help:
db.test.insert({name:'farid', addresses:[
{"city":"Baku", "country":"Azerbaijan"},
{"city":"Susha", "country":"Azerbaijan"},
{"city" : "Istanbul","country" : "Turkey"}
]});
db.test.insert({name:'elena', addresses:[
{"city" : "Ankara","country" : "Turkey"},
{"city":"Baku", "country":"Azerbaijan"}
]});
Then the following will show all countries:
db.test.aggregate(
{$unwind: "$addresses"},
{$group: {_id:"$country", countries:{$addToSet:"$addresses.country"}}}
);
result will be
{ "result" : [
{ "_id" : null,
"countries" : [ "Turkey", "Azerbaijan"]
}
],
"ok" : 1
}
Maybe there are other ways, but that's one I know.
With 'cities' you might want to take more care (because I know cities with the same name in different countries...).
Based on your question, there may be two underlying issues here:
First, it looks like you are trying to query a Collection called "test". Often times, "test" is the name of an actual database you are using. My concern, then, is that you are trying to query the database "test" to find any collections that have the key "city" or "country" on any of the internal documents. If this is the case, what you actually need to do is identify all of the collections in your database, and search them individually to see if any of these collections contain documents that include the keys you are looking for.
(For more information on how the db.collection.find() method works, check the MongoDB documentation here: http://docs.mongodb.org/manual/reference/method/db.collection.find/#db.collection.find)
Second, if this is actually what you are trying to do, all you need to for each collection is define a query that only returns the key of the document you are looking for. If you get more than 0 results from the query, you know documents have the "city" key. If they don't return results, you can ignore these collections. One caveat here is if data about "city" is in embedded documents within a collection. If this is the case, you may actually need to have some idea of which embedded documents may contain the key you are looking for.
I am trying to filter mongo data by using the below query in mongodb version 2.6.1 but getting error.
MongoDB version 2.4.6 (Working):
> db.BC_1839.find({data: {$elemMatch:{$where : "this.First_name.toLowerCase().indexOf('kim') ==0"}}});
output:
{
"_id" : ObjectId("53719a9d5b9e5c8c110001b9"),
"data" : [
{
"First_name" : "Kimberely",
"Last_name" : "Weyman",
"Company_name" : "Scientific Agrcltl Svc Inc",
"Address" : "7721 Harrison St",
"City" : "Kingsway West",
"State" : "NS",
"Post" : "2208",
"Phone1" : "02-7091-8948",
"Phone2" : "0441-151-810",
"Email" : "kweyman#weyman.com.au",
"Web" : "http://www.scientificagrcltlsvcinc.com.au",
"active" : "true"
}
],
"history" : [
{
"timestamp" : "2014-05-13 06:07:55",
"event": "creation",
"createdby" : "Srikesh Infotech",
"creation_data" : [
{
"crm_base_contact_id" : "1839",
"crm_imported_files_id" : "1464"
}
]
},
{
"timestamp" : "2014-05-13 06:09:05",
"event" : "Task",
"createdby" : "Srikesh Infotech",
"Task_data" : [
{
"Campaign ID" : "193",
"Campagin Name" : "Test Campa1"
}
]
}
],
"ref" : [
{ "crm_base_contact_id" : "1839", "crm_imported_files_id" : "1464" }
]
}
MongoDB version 2.6.1(Not Working):
> db.BC_1839.find({data: {$elemMatch:{$where : "this.First_name.toLowerCase().indexOf('kim') ==0"}}});
output:
error: {
"$err" : "Can't canonicalize query: BadValue $elemMatch cannot contain $
where expression",
"code" : 17287
}
Same query executes in mongodb version 2.4.6 but not in mongodb version 2.6.1 Why???
It shouldn't have worked in earlier versions at all, as at the very least you have modified the scoping of this to now refer to "data" as a top level element. In short, this is no longer allowed and you really should not be using JavaScript methods unless you absolutely have to. Even then, there is probably still a better way in most cases.
But in fact this is an un-necessary use of JavaScript matching as it is not required when there are other operators existing that will do this.
You should be using a $regex form instead:
db.docs.find({ "data.First_name": /^kim/i })
Or anywhere within the field, remove the caret ^:
db.docs.find({ "data.First_name": /kim/i })
Which is pretty much as inefficient as JavaScript execution but not as much as there is not the overhead of processing through that interpreter engine. And of course it works everywhere.
Also think about what a query relying on JavaScript to resolve is actually doing:
Invokes a JavaScript interpreter instance
Converts BSON document types per document to JavaScript types
Evaluates JavaScript code in the interpreter per document
Casts JavaScript true|false back as a result per document
Considering that $regex ( but with a case insensitive match which is not optimal ) is doing the same operations but using the "pcre" C library natively without conversion and recasting per document, then it is clearly the sane choice of the two.
I'm using the Java driver withe document that looks like this (a real test example):
{
"_id" : ObjectId("5207fe359b88bfa6f90a82b0"),
"meta_id" : "d6eb1b13-50c7-473f-8348-b5a638a542a0",
"name" : "Fake Name Inc.",
"created" : ISODate("2013-08-11T21:12:21.533Z"),
"members" : {
"5207fe359b88bfa6f90a82af" : [
"Admin",
"User"
]
}
}
I want to select the string array at the path "members.5207fe359b88bfa6f90a82af" (which is a list of roles).
I'm at a loss as to how to do that. It looks like a projection would work here, but I'm new enough to Mongo that the way the projection is written is not obvious.
I can of course load the whole object or maybe even just the "members" field, but I think I should be able to select just exactly the data I'm after.
So, does anyone have an idea of how such a query would be written?
Note: This question suggests that maybe I need to change the structure of the document to make things easier: MongoDB - Query by sub-tree
You can use dot notation in the projection parameter of find to do this. In the shell:
db.test.find(
{_id : ObjectId("5207fe359b88bfa6f90a82b0")},
{'members.5207fe359b88bfa6f90a82af': 1, _id: 0})
Returns:
{
"members": {
"5207fe359b88bfa6f90a82af": [
"Admin",
"User"
]
}
}