FormBuilder accessing second-level attributes (dot notation) - mongodb

I'm using Angular2's FromBuilder to build a query to send to my backend (MongoDB). The problem is, I'm trying to access second-level attributes. For example I have a Song which has a Genres array in it. I want to access genres.hipHop for example but I can't set a form group to genres.hipHop. All I can do is use one word.
Here is an example of a query I want to build
{
"where": {
"and": [
{"genres.house": {"exists": "true"}}
]
},
"order": "created DESC"
};
My FormBilder looks like this:
this.queryForm = this._formBuilder.group({
where: this._formBuilder.group({
and: this._formBuilder.array([
this._formBuilder.group({
genres : this._formBuilder.group({ <-- need it be genres.hipHop
exists: true
})
})
]),
order: 'created DESC',
})
});
Which creates:
{
"where": {
"and": [
{"genres": {"exists": "true"}}
]
},
"order": "created DESC"
};
I can't add the .hipHop after genres.
I tried nesting the genres(hipHop) inside of genres but when I send it to my backend, it doesn't recognize hipHop. This is the query that was built:
{ "where": {
"and": [
{ "genres":
{ "hipHop": { "exists": true } }
}
],
"order": "created DESC" }
}
This was the error I got:
"MongoError: unknown operator: $hipHop\n
Here is an example of a song object:
{
"title": "Sweet Talk feat. Quinn XCII (Evan Gartner Remix)",
"artist": "Academy",
"audio": "https://api.soundcloud.com/tracks/270500010/stream?client_id=90d140308348273b897fab79f44a7c89",
"image": "https://i1.sndcdn.com/artworks-000168676452-qkxqul-t500x500.jpg",
"download": "http://stereoload.com/academy/quinn-xcii-x-academy-sweet-talk-evan-gartner-remix",
"url": "http://soundcloud.com/academy401/sweettalkremix",
"created": "2016-12-13T09:21:28.071Z",
"genres": {
"hipHop": 30,
"house": 30,
"pop": 40
},
"rank": 0
}
Thank you!

I don't think you'll be able to use the dot notation here, as it won't be a valid key.
Looking at this line in the source:
https://github.com/angular/angular/blob/2.4.1/modules/%40angular/forms/src/form_builder.ts#L75
It looks like Angular iterates over the keys to build the controls. As you can't do "x.y" as a key in javascript, this fails.
Example
var x = {y.z = "a"} //--> Uncaught SyntaxError: Unexpected token .
I think you'll have to make another set of keys inside your genre FormGroup -- one of the keys could be hiphop, for instance. Which might be what you're looking to do anyway if there are more genres you want to add :)
EDIT:
Example --
this.queryForm = this._formBuilder.group({
where: this._formBuilder.group({
and: this._formBuilder.array([
this._formBuilder.group({
genres : this._formBuilder.group({
hiphop : this._formBuilder.group({
exists: true
})
})
})
]),
order: 'created DESC',
})
});

Did you tried define object key as a string "genres.hipHop"?
this.queryForm = this._formBuilder.group({
where: this._formBuilder.group({
and: this._formBuilder.array([
this._formBuilder.group({
"genres.hipHop" : this._formBuilder.group({
exists: true
})
})
]),
order: 'created DESC',
})
});

Related

Issue using $pull from nested Mongoose array atomic operation

I can't seem to get this mongoose $pull operation to work at all. I have tried many solutions, but would ultimately like to use an atomic operation to achieve this as that is the design pattern in the rest of my code.
In my List schema, I have an array of Item objects. Each Item object has an array of Notes which are all string values. Creating, Reading, and Updating these Notes arrays has been no issue. But I can't seem to get the Delete functionality to work.
Schemas:
List
const listSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "Users"
},
name: {
type: String
},
items: {
type: Array,
default: [itemSchema]
}
});
Item
const itemsSchema = {
item: String,
style: String,
moved: Boolean,
notes: Array
};
Sample Document:
_id: 6186940ce10fbd7cec9fb01f
items: Array
0: Object
notes: Array
0: "Updated again x2"
1: "Test one to delete edit"
2: "New one"
_id: 6186d98dcef2ae43605becc4
item: "Test 1"
style: ""
moved: false
1: Object
notes: Array
_id: 6186d98fcef2ae43605becc5
item: "Test 2"
style: ""
moved: false
2: Object
notes: Array
_id: 6186d991cef2ae43605becc6
item: "Test 3"
style: ""
moved: false
3: Object
notes: Array
0: "Add from none"
1: "typing a really long sentence here to see what happens when I get to t..."
2: "Test"
_id: 6186d994cef2ae43605becc7
item: "Test 4"
style: ""
moved: false
user: 611083d8baed4458d8dcd273
name: "Nov-06-2021"
__v: 0
Methods:
Create
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$push": { "items.$.notes": newNote } },
{ new: true },
(err, newList) => {}
)
Update
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$set": { "items.$.notes.$[note]": newNoteText } },
{
arrayFilters: [{"note": note}],
new: true
},
(err, newList) => {}
)
Delete (not working)
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$pull": { "items.$.notes.$[note]": note } },
{
arrayFilters: [{"note": note}],
new: true
},
(err, newList) => {}
)
The current Delete code block is close to what I'd like my solution to look like. By all rights, and everything I've read, it should already work. Can anyone show me what will work, and possibly why this isn't currently working?
I have tried many solutions including using $in, $[], $elemMatch, and a bunch more outside the box solutions. I don't see why $push and $set work without issue, but $pull is deciding to do nothing.
The current response I get from MongoDb with this operation doesn't include an error message, but the returned newList is a null value, and no change is reflected in my DB. I am currently on the latest version of mongoose: 5.13.13.
The solution to this was to remove the $[note]from my operator as $pull requires an array to operate on, not the array object.
I definitely tried that before, before there was an issue with my request body that was coming from the front end. I wasn't using the correct syntax for an axios.delete, however, and that was my main issue.
Solution
List.findOneAndUpdate(
{ "user": req.user.id, "name": list, "items._id": ObjectId(id) },
{ "$pull": { "items.$.notes": note } },
{ new: true },
(err, newList) => {}
)
I removed $[note] as a variable because $pull operates on the entire notes array. Therefore, I no longer need the arrayFilters because it is already looking for the note variable.

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.

Why am I unable to specify a field to return with the projection parameter in MongoDB? [duplicate]

dbo.collection("users")
.findOne({email: emailGiven, "friends.email": element.email},
{ friends: { $elemMatch: { email: element.email } } },
function(errT, resultT) {})
I have the query above in node.js but for some reason the query's elemMatch part doesn't work on node.js but when I execute the same query in mongo terminal it works, so I'm thinking maybe node.js doesn't support $elemMatch? If this is the case, could anyone tell me what would be the equivalent of this query in node.js?
Sample data from my DB:
/* 4 */
{
"_id" : ObjectId("5ad20cef8248544860ce3dc1"),
"username" : "test",
"email": "",
"fullName" : "",
"friends" : [{email: "",
status :""}],
"alarms": [ {"id":111,
"title": "TITLE",
"location": "",
"startTime": "10-10-1996 10:18:00",
"endTime": "10-10-1996 10:18:00" }, {},{}
],
"pnumber" : ""
}
The node.js driver findOne has a different call signature than the findOne in the MongoDB shell. You pass the field selection object as the projection element of the options parameter:
dbo.collection("users")
.findOne({"friends.email": email},
{projection: { friends: { $elemMatch: { email: email } } } },
function(errT, resultT) {...});
If you want to find for any values which is stored in some variable, you use regex for that. Your query should look like
dbo.collection("users").findOne({
email: new RegExp(emailGiven, 'i'),
"friends.email": new RegExp(element.email, 'i')
}, {
friends: {
$elemMatch: {
email: new RegExp(element.email, 'i')
}
}
}, function(errT, resultT) {})
i is for case insensitive comparison here

How to replace prefetch source in Bloodhound with a 'real time' alternative?

I've been using Bloodhound with the prefetch [docs] option defined.
This works fine, except when I add content to the json file being prefetched, it is not available as a search result unless I restart the browser.
So I am trying to make the search results reflect the updated file content in 'real time'.
I tried simply replacing prefetch with remote but this causes the search functionality not to work as intended (it shows non-matched results).
Below is the code I am using with prefetch.
Version info: typeahead.bundle.min.js at v0.10.5.
function searchFunction() {
var template =
"<p class=\"class_one\">{{area}}</p><p class=\"class_two\">{{title}}</p><p class=\"class_three\">{{description}}</p>";
var compiled_template = Hogan.compile(template);
var dataSource = new Bloodhound({
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.tokens.join(
' '));
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: '/static/my_file.json'
# remote: '/search'
});
dataSource.initialize();
$('.my_lookup .typeahead').typeahead({}, {
source: dataSource.ttAdapter(),
name: 'courses',
displayKey: 'title',
templates: {
suggestion: compiled_template.render.bind(
compiled_template)
}
}).focus().on('typeahead:selected', function(event, selection) {
var title = selection.title
// do things with the title variable
});
}
Edit:
I started thinking perhaps I need some server side logic to perform a search on a database that contains the content previously within the local json file.
Using the code posted below, the following works:
Searches database in real time.
All matches are returned.
The following does not work:
It does not offer suggestions, you have to type the full token name.
If searching for apple, it will search after typing a, then p etc, if it doesn't get any results, it shows this error in Firebug: TypeError: data is null. After a few of these errors, it stops triggering searches and no error is displayed.
And, the results from the database are in the following format, and I don't know how to apply the Hogan template for the suggestions to each result:
{
"matches": [{
"tokens": ["apple", "orange"],
"area": "Nautical",
"_id": {
"$oid": "4793765242f9d1337be3d538"
},
"title": "Boats",
"description": "Here is a description"
}, {
"tokens": ["apple", "pineapple"],
"area": "Aviation",
"_id": {
"$oid": "4793765242f9d1337be3d539"
},
"title": "Planes",
"description": "Here is a description."
}]
}
JS
function searchFunction() {
var engine = new Bloodhound({
remote: {
url: '/search?q=%QUERY%',
wildcard: '%QUERY%'
},
datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
});
engine.initialize();
$('.my_lookup .typeahead').typeahead({
}, {
source: engine.ttAdapter(),
name: 'courses',
displayKey: 'title',
templates: {
suggestion: function (data) {
return "// not sure how to apply markup to each match"
}
}
}).focus().on('typeahead:selected', function(event, selection) {
console.log(selection);
var title = "// again not sure how to access individual match data"
// do things with the title variable
});
}
MongoDB Schema
Database: courses
Collection: courses
Documents:
{
"_id" : ObjectId("4793765242f9d1337be3d538"),
"tokens" : [
"apple",
"orange"
],
"area" : "Nautical",
"title" : "Boats",
"description" : "Here is a description."
}
and:
{
"_id" : ObjectId("4793765242f9d1337be3d539"),
"tokens" : [
"apple",
"pineapple"
],
"area" : "Aviation",
"title" : "Planes",
"description" : "Here is a description."
}
etc
Python (using Bottle routes)
#route('/search')
def search():
"""
Query courses database for matches in tokens field.
"""
# get the query
query = request.GET.q
# define the database
dbname = 'courses'
db = connection[dbname]
# define the collection
collection = db.courses
# make the query
matches = collection.find({"tokens":query})
# send back results
results = {}
results['matches'] = matches
response.content_type = 'application/json'
return dumps(results)

Mongoose query where value is not null

Looking to do the following query:
Entrant
.find
enterDate : oneMonthAgo
confirmed : true
.where('pincode.length > 0')
.exec (err,entrants)->
Am I doing the where clause properly? I want to select documents where pincode is not null.
You should be able to do this like (as you're using the query api):
Entrant.where("pincode").ne(null)
... which will result in a mongo query resembling:
entrants.find({ pincode: { $ne: null } })
A few links that might help:
The mongoose query api
The docs for mongo query operators
$ne
selects the documents where the value of the field is not equal to
the specified value. This includes documents that do not contain the
field.
User.find({ "username": { "$ne": 'admin' } })
$nin
$nin selects the documents where:
the field value is not in the specified array or the field does not exist.
User.find({ "groups": { "$nin": ['admin', 'user'] } })
I ended up here and my issue was that I was querying for
{$not: {email: /#domain.com/}}
instead of
{email: {$not: /#domain.com/}}
Ok guys I found a possible solution to this problem. I realized that joins do not exists in Mongo, that's why first you need to query the user's ids with the role you like, and after that do another query to the profiles document, something like this:
const exclude: string = '-_id -created_at -gallery -wallet -MaxRequestersPerBooking -active -__v';
// Get the _ids of users with the role equal to role.
await User.find({role: role}, {_id: 1, role: 1, name: 1}, function(err, docs) {
// Map the docs into an array of just the _ids
var ids = docs.map(function(doc) { return doc._id; });
// Get the profiles whose users are in that set.
Profile.find({user: {$in: ids}}, function(err, profiles) {
// docs contains your answer
res.json({
code: 200,
profiles: profiles,
page: page
})
})
.select(exclude)
.populate({
path: 'user',
select: '-password -verified -_id -__v'
// group: { role: "$role"}
})
});
total count the documents where the value of the field is not equal to the specified value.
async function getRegisterUser() {
return Login.count({"role": { $ne: 'Super Admin' }}, (err, totResUser) => {
if (err) {
return err;
}
return totResUser;
})
}
Hello guys I am stucked with this. I've a Document Profile who has a reference to User,and I've tried to list the profiles where user ref is not null (because I already filtered by rol during the population), but
after googleing a few hours I cannot figure out how to get this. I
have this query:
const profiles = await Profile.find({ user: {$exists: true, $ne: null }})
.select("-gallery")
.sort( {_id: -1} )
.skip( skip )
.limit(10)
.select(exclude)
.populate({
path: 'user',
match: { role: {$eq: customer}},
select: '-password -verified -_id -__v'
})
.exec();
And I get this result, how can I remove from the results the user:null colletions? . I meant, I dont want to get the profile when user is null (the role does not match).
{
"code": 200,
"profiles": [
{
"description": null,
"province": "West Midlands",
"country": "UK",
"postal_code": "83000",
"user": null
},
{
"description": null,
"province": "Madrid",
"country": "Spain",
"postal_code": "43000",
"user": {
"role": "customer",
"name": "pedrita",
"email": "myemail#gmail.com",
"created_at": "2020-06-05T11:05:36.450Z"
}
}
],
"page": 1
}
Thanks in advance.