How do I stop MongoDB from sorting my data? - mongodb

I am trying to create a cards game but I am stuck at the point of returning cards with MongoDB: player has 30 cards, when clicking on a card the card does an animation with translate and rotate to reveal the value of the card , and since I only need to reveal the value of 3 cards, I need them to end up in the order in which they were chosen, but when I choose them, it returns them to me sorted by the value that the card has; so, for example, if I choose 1, 2, 3; no problem, but if I choose 2, 1, 3; it returns 1, 2, 3.
I've tried sort() and it doesn't work, because as I said I need it to return them in the chosen order, neither ascending nor descending (anyway it is sorting the cards without sort). I have tried with Express Handlebars, but apparently it creates an array, so when I put for example cards.[0].number; in the case of 2,1,3 it still returns 1 and not 2.
This is my code:
router.post('/cards', (req, res) =>{
let zahl1 = req.body.zahl1;
let zahl2 = req.body.zahl2;
let zahl3 = req.body.zahl3;
cards.find({"zahl": {$in:[zahl1, zahl2, zahl3]}}, { _id: 0}, function(err, cards) {
return res.render('cardsGame', {
cards: cards
});
});
});
Since I am having this issue I am working with a simple HTML to find out how to solve this issue:
</form>
<form method="post" action="/cards" id="bilden">
<input type="text" id="hallo1" class="w-25">
<input type="text" id="hallo2" class="w-25">
<input type="text" id="hallo3" class="w-25">
<a id="funktioniert" onclick="hinrichten()"><input type="submit" value="cards" class="btn btn-primary"></a>
<input type="text" name="zahl1" id="zahl1" target="zahl1" class="w-25">
<input type="text" name="zahl2" id="zahl2" target="zahl2" class="w-25">
<input type="text" name="zahl3" id="zahl3" target="zahl3" class="w-25">
</form>
<script>
let newTextInput1 = document.getElementById('hallo1');
let newTextInput2 = document.getElementById('hallo2');
let newTextInput3 = document.getElementById('hallo3');
let newAncla = document.getElementById('funktioniert');
let inputResult1 = document.getElementById('zahl1');
let inputResult2 = document.getElementById('zahl2');
let inputResult3 = document.getElementById('zahl3');
function hinrichten(){
inputResult1.value = newTextInput1.value;
inputResult2.value = newTextInput2.value;
inputResult3.value = newTextInput3.value;
}
</script>
Can someone help me find a way to do this? Thanks!

A single MongoDB node returns documents in the order they are encountered. If an index is used to optimize the query, the documents will be encountered in the sorted order of the index.
If you need documents in the order they were inserted into the database, you could hint the $natural index, but that would mean every such query would be a collection scan.
To get them in the order they appear in the request, you will need to sort them on the client side.
Perhaps something like:
router.post('/cards', (req, res) =>{
let zahl1 = req.body.zahl1;
let zahl2 = req.body.zahl2;
let zahl3 = req.body.zahl3;
let zahls = [zahl1, zahl2, zahl3];
cards.find({"zahl": {$in:zahls}}, { _id: 0}, function(err, cards) {
let cardsSort = cards.sort(function(a,b){
aidx = zahls.indexOf(a.zahl);
bidx = zahls.indexOf(b.zahl);
return (aidx - bidx);
});
return res.render('cardsGame', {
cards: cardsSort
});
});
});

Related

Creating fields based on what's returned, meteor pub/sub

Probably a bad title, I don't know how to precisely describe this.
Template.body.helpers({
messages: function () {
return Messages.find({}, {
sort: {createdAt: -1}
});
}
});
This is the code I have.
On the client side, it takes
{{#each messages}}
<span class="text"> {{messageText}} </span>
{{/each}}
Each message contains "text" and "username".
How would I go about, in the "return Messages" part, modifying what it returns?
So I would do something like
Template.body.helpers({
messages: function () {
Messages.find().forEach(function(thismsg){
messageText = slugify(thismsg.messageText)
};
}
});
Get messages, modify the fields and then return them. Could I perhaps do this in subscriptions instead? Please help.
You can pass your message in the helper and then you can modify the message and pass back to the template like this.
Your template code.
{{#each messages}}
<span class="text"> {{slugifyMessage text}} </span>
{{/each}}
You helper code.
Template.body.helpers({
messages: function () {
return Messages.find({}, {
sort: {createdAt: -1}
});
}
slugifyMessage: function(messageText){
return slugify(messageText);
}
});
Please make sure your text that I am passing to slugifyMessage will same name as database you mentioned that you have only two field named username and text, So I took text you can replace this with your doc field that you want to modify.

mongodb query field in object that is being edited

How can I query a field in an object? My html retrieves all the objects in array called 'postcards'
Meteor.user.profile.postcards [
{
_id: 84fh83f,
field_one: "a name",
field_two: " winter whether",
field_three: " lost more writing"
},
{
_id: 6jsf58s,
field_one: "another name",
field_two: " topical issues ",
field_three: " lost more writing"
}
]
Note: I used random.Id() so each object in the array can be uniquely identified.
Setting a session value to this._id when the user is focused on the input field will retrieve this unique id, however, I would like to query the actual field in focus. The value in these fields are projected within the text input area by using the spacebars syntax within the html.
Can I somehow assign the name within the curly braces of the value attribute to a variable? Then query?
Is there a whole new way to achieve this?
I want to update that specific field in this object instead updating the entire object.
HTML:
{{#with currentUser.profile}}
{{#each postcards}}
<input id="one" value="{{field_one}}" type="text">
<input id="two" value="{{field_two}}" type="text">
<input id="three" value="{{field_three}}" type="text">
{{/each}}
{{/with}}
client.js
Within events, I would like to update the field on focus upon keyup.
Templates.myTemplate.events({
'keyup input[type=text]': _.throttle(function(event) {
Meteor.users.update(this._id, {$set: {**fieldbeingedited**: event.target.value}});
}, 500);
});
What you want to have is an ES6 capability named 'Computed property names'.
This is what is looks like :
var x = 'hello',
obj = {
[x]: 'world'
};
console.log(obj); // Object {hello: "world"}
You have two options :
- you use the meteor harmony package to transpile your es6 to es5 (https://github.com/mquandalle/meteor-harmony)
- you build your object first
To build you object first :
var obj = {};
obj[ this.targetField ] = event.target.value
Meteor.users.update(this._id, {$set: obj});

How can I query mongodb for a list of credit cards that are about to expire?

My collection has documents with card as a subdocument array holding each card. Each card is an object with many keys. The two keys for expiration are expiration_month and expiration_year.
Here is an example.
{
_id: '1',
card: [
{
'expiration_month': 10,
'expiration_year': 2017
},
{
'expiration_month': 01,
'expiration_year': 2015
},
]
}
How would I publish the right list to the subscription for this route so that I can use an each loop to get the right list? I just want to get any cards that expire in the next three months.
{{#each expiring_cards}}
{{> ExpiringCards}}
{{/each}}
Meteor.publish('card_expiring', function () {
var before_date = moment(new Date()).add(3, 'month');
return Donate.find( { moment(new Date('card[0].expiration_year' + '/' + 'card[0].expiration_month'))._d : { $lte: before_date} });
});
I know the above code for publish doesn't work, how could I change it to make it work, or is there a better way to do this?
Thanks
Easiest way seems to be to join these two fields into a Date object, then run the below query.
How to combine the two fields - answer here
Meteor.publish('card_expiring', function () {
var today = new Date();
var future_date = new Date(new Date(today).setMonth(today.getMonth()+3));
return Donate.find({'card.expires' : {$lte : future_date }}, { card : true } );
});
Of course this just gets me any card subdocument that matches this query, which means that would include the cards that don't match, but that are in a subdocument that does.
To filter even further I used this in my helper.
Template.ListExpiringCards.helpers({
list: function () {
//The subscription filters out the records with subdocument card,
//which contain cards expiring in the next 3 months
return Donate.find();
},
expiring_cards: function () {
//This further filters only to the cards that match the above criteria
// then returns an array to the Meteor template for the each loop
var returnThisValue = [];
this.card.forEach(function(entry) {
var today = new Date();
var future_date = new Date(new Date(today).setMonth(today.getMonth()+3));
if (entry.expires <= future_date) {
returnThisValue.push(entry);
}
});
return returnThisValue;
}
});
And this in my template
{{#each list}}
{{#each expiring_cards}}
{{> ExpiringCards}}
{{/each}}
{{/each}}

Get html attribute through $scope in angularjs

Alright, so I'm making a recursive list in AngularJS using ng-include and ng-repeat, something like this:
<div id="wrapper">
<div id="text" ng-click="DivClick()">
<ul>
<li id="{{$index}}" ng-repeat="data in layer" ng-include="'recursion.html'"></li>
</ul>
</div>
<script type="text/ng-template" id="recursion.html">
<textarea myd-keypress cols="63" rows="1">{{data.content}}</textarea>
<input type="text" myd-numbers value="{{data.price}}"></input>
<ul>
<li ng-repeat="data in data.layer" ng-include="'recursion.html'" id="{{$parent.$attr('id') + '-' + $index}}"></li>
</ul>
But of course, it doesn't work. So basically what I need is that every < li> element in dom has id that corresponds like this:
0
1
1-0
1-1
1-1-0
1-2
1-3
2
2-0
so that every new < li> has id that equals "id of its parent" + "-$index".
What I know that will work is that scope.layer[] and its layer descendants contain a field that will save ID of the < li> and display it, but I want to avoid saving that extra field in the DB because solution to this is really simple (as shown in the example), but I can't get over syntax. Any suggestions?
Before passing data to $scope.label you could loop over it with function that recursively loops branches and adds an id property
var data = [{
name: 'foo',
children: [{
name: 'foo child',
children: [{
name: 'foo child grandchild'
}, {
name: 'foo child grandchild2'
}]
}, {
name: 'foo child2'
}]
}, {
name: 'bar'
}]
function createID(arr, parentID) {
for (var i = 0; i < arr.length; i++) {
var id = (i + 1).toString();
var thisID = parentID ? parentID + '-' + id : id;
arr[i].id = thisID;
if (arr[i].children) {
createID(arr[i].children, thisID)
}
}
}
createID(data, null)
DEMO http://jsfiddle.net/z4RPz/
What I think you're wanting to do is reach up and grab the $index from the parent element.
You can do that using $parent.$index - or in this case, it may be $parent.$parent.$index, as you're using ng-repeat -> ng-include -> ng-repeat
See this answer for more information on a similar situation:
https://stackoverflow.com/a/15257501/317180

Express/Node.js - How to save an array of objects within one Model [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Mongoose - Form to save model with embedded documents
I have a simple Mongoose schema with BlogPosts and Feeds embedded within those BlogPosts. So each BlogPost can have several Feed objects. Right now my HTML form allows me to correctly save one 'Feed' when I create a BlogPost, BUT I would like to be able to add many Feeds. How should I change my HTML/controller? Thanks much!
web.js
app.post('/blogpost/new', function(req, res){
var post = new BlogPost(req.body.post)
post.feeds.push(req.body.feed);
post.save(function() {
res.redirect('/blogposts');
});
});
/blogpost/new
<form method="post">
<input type="text" name="blogpost[title]"/>
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
</form>
schema
var Feed = new Schema({
name : { type: String }
, key : { type: String }
});
var BlogPost = new Schema({
title : { type: String, required: true, index: { unique: true } }
, feeds : [Feed]
});
How do I make it so that this form stores two 'Feeds' in the blogpost object it creates? right now it would save One Feed in the data with two name values and two key values.
Thanks much!
When you do something like this:
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
<textarea name="feed[name]"></textarea>
<textarea name="feed[key]"></textarea>
You are basically saying "I need a textbox for feed's name, feed's key, feed's name, feed's key which is obviously incorrect. If you did feed[0][name]...feed[1][name] that would say "i need a textbox for the first feed...i need a textbox for the second feed"
Since you are breaking the parent and child up, rather than handling them as sub objects, you'll need to push each into the blogPost.feeds and save it.
Just be careful with that approach, especially when editing, because you are simply adding new objects ontop of what could already be an existing array of feeds.