Strange behavior in for-loop in JS/NodeJS - javascript

I'm working on a NodeJS/VueJS app and I encounter some strange behavior and I am not sure if it is something I am doing wrong or something else.
I have 2 arrays, one array of objects that contains campaigns and one array of objects that contains all promotions associated to all campaigns.
Each Campaign object contains, at this point, an empty array called promos, where I want to push all the promotions for each campaign.
for (var i = 0; i < campaigns.length; i++) {
for (var j = 0; j < campaignsPromo.length; j++) {
if (campaignsPromo.length > 0) {
if (campaigns[i].IDCampaign == campaignsPromo[j].IDCampaign) {
if ((campaignsPromo[j].associated == 1) && (campaignsPromo[j].validated == 1)) {
campaigns[i].promos.push(campaignsPromo[j]);
console.log("Validated Campaign ID " + campaigns[i].IDCampaign);
console.log("Promo ID " + campaignsPromo[j].IDPromo);
} else if ((campaignsPromo[j].associated == 1) && (campaignsPromo[j].validated == 0)) {
campaigns[i].unvalidatedPromos++;
console.log("Unvalidated Campaign ID " + campaigns[i].IDCampaign);
console.log("Promo ID " + campaignsPromo[j].IDPromo);
}
}
} else {
console.log("No promos!");
}
}
}
At first, the code seems to be doing what is supposed to do and it checks out with my test data set.
However, in the end, all campaigns end up having the same promotions.
Campaigns With No Promos: [{"IDCampaign":7,"campaignName":"dadsadafds","startDate":"2022-02-03","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[]},{"IDCampaign":3,"campaignName":"Tarzan 3","startDate":"2022-02-02","endDate":"2022-02-06","unvalidatedPromos":0,"promos":[]},{"IDCampaign":1,"campaignName":"Tarzan","startDate":"2022-02-01","endDate":"2022-03-01","unvalidatedPromos":0,"promos":[]},{"IDCampaign":2,"campaignName":"Tarzan 2","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[]},{"IDCampaign":4,"campaignName":"Tarzan 4","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[]},{"IDCampaign":5,"campaignName":"Jabe 1","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[]},{"IDCampaign":6,"campaignName":"dadsada","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[]},{"IDCampaign":8,"campaignName":"Black Friday","startDate":"2022-02-01","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[]}]
Validated Campaign ID 1
Promo ID 1119
Unvalidated Campaign ID 1
Promo ID 107
Campaigns With Promos: [{"IDCampaign":7,"campaignName":"dadsadafds","startDate":"2022-02-03","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":3,"campaignName":"Tarzan 3","startDate":"2022-02-02","endDate":"2022-02-06","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":1,"campaignName":"Tarzan","startDate":"2022-02-01","endDate":"2022-03-01","unvalidatedPromos":1,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":2,"campaignName":"Tarzan 2","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":4,"campaignName":"Tarzan 4","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":5,"campaignName":"Jabe 1","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":6,"campaignName":"dadsada","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":8,"campaignName":"Black Friday","startDate":"2022-02-01","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]}]
This is the content of campaignsPromo:
[ { IDCampaign: 1,
IDPromo: 1119,
promoType: 'CHRISTMASS',
associated: 1,
validated: 1,
promoName: 'Test Promo 1',
beginDate: '2020-11-26',
endingDate: '2020-11-29' },
{ IDCampaign: 1,
IDPromo: 107,
promoType: 'CHRISTMASS',
associated: 1,
validated: 0,
promoName: 'Test Promo 2',
beginDate: '2019-12-21',
endingDate: '2019-12-23' } ]
Any ideas? From where I'm standing, I am not doing anything wrong but it wouldn't be the first time I'm missing the obvious.
PS: Please ignore the fact my campaigns are called "Tarzan".

You didn't share the offending code, so I'll give a generic answer.
When the symptom is that a bunch of all objects seem to have a property that is the same array of items, it is likely caused by exactly that. Each object may be sharing the same array instance.
A typical mistake is something like the following:
var items = [];
var containers = [];
for (let i = 0; i < 3; ++i) {
items.push(i);
let container = {};
container.items = items;
containers.push(container);
}
console.log(containers);
Although one might expect, or even have intended to get 3 objects like this:
[
{ items: [ 0 ] },
{ items: [ 0, 1 ] },
{ items: [ 0, 1, 2 ] }
]
The items array is actually the same instance of an array. And what you actually get is more like:
[
{ items: [ 0, 1, 2 ] },
{ items: [ 0, 1, 2 ] },
{ items: [ 0, 1, 2 ] }
]
In fact, the stack snippet visualizer actually does a better job visualizing this because it outputs:
[
{
"items": [
/**id:3**/
0,
1,
2
]
},
{
"items": /**ref:3**/
},
{
"items": /**ref:3**/
}
]
In this way, it tries to inform you that it is actually the same array by giving it a ref:3 label and just marking the values of the other properties with the same ref:3 label as a comment.
Causes I see typically stem from a misunderstanding of what it means to assign an array to a property. Doing so does not create a copy of the array. Both objects refer to the same array.
This can be strange because: it can appear to be correct in a debugger by inspecting the contents of your array as your are stepping through a loop. Even if we had added log messages like console.log(items) or even console.log(container) inside the loop, we'd probably still not have enough of a clue that something went wrong because the contents of that one array instance change with each iteration of the loop and we can dump versions of it as text that appear to be correct, but then unwittingly change the contents of the array on the next iteration.
But if we log the entire containers array after the loop, you'll find that each object has the same instance of the array. Assining an array isn't bad if it's only one object that gets the array assigned to it, but if you assign the same array to properties of multiple objects, or in a loop, you may run into this problem.
One possible habit-breaker you can try is to inspect all objects in a second loop after your primary loop rather than sneaking your logging code directly into the first loop. It's less efficient, but if you're often making these kinds of mistakes, it can help you find problems and achieve correctness.
Another habit breaker is to console.log(JSON.stringify(foo)) rather than console.log(foo) because console.log in the browser actually remembers the reference of the object and shows you its current contents, rather than its contents at the time it was logged. This is different depending on the platform, where node.js will log as text instead of by reference.
Good luck and be careful!

Related

for loop in an array

I ran into a rather weird problem when I try to code a specific part of my bot! To make it simple and short , I have an object array that contains various properties ( see code for examples). What i want to do is I want to get all the specific property in that array ( name for example ) and print that out in a console. I tried looping using a simple for loop , and every successful loop, i asked the console to log arrayname[i].name! See code for clarification!
module.exports = {
commands: ["test"],
callback: (message, arguments, text) => {
const data = [
{ name: "Ren", id: "NURE1" },
{ name: "Isa", id: "SCIS1" }
]
for (var i = 0; i <= data.length; i++) {
console.log(data[i].name)
}
}
}
the problem is it does log the code as expected - Ren and Isa in this case for example but as soon as the console log, the app crashes throwing the error - property name is undefined ;-; - the same goes when i used id. Is there something I am doing wrong??
This is because data only has 2 items (index 0 and index 1), but you're trying to access 3 items (index 0, 1, and 2). Index 2 is undefined, so it's throwing an undefined error. You should use i < data.length instead of i <= data.length in your for loop.
You can also use a for ... of loop to avoid issues like this with indices:
for (let item of data) {
console.log(item.name);
}

Select Random Item (shown in pairs) from Array & Remove It, Restart Once Array is Empty

I'm super newbie in coding and I need help to achieve this code.
I'm trying to get a random item (in pairs) from an array and then remove it from this array until user gets to the last item or 60 days have gone from using the service (cookie?)... I have build a script with the help of other questions here in stackoverflow and here is my results so far.
`<script>
var randomizer = document.getElementById("getImgBut");
var dog1 = '/app/wp-content/mediaApp/yo-creo-mi-realidad/01F.jpg';
var dog2 = '/app/wp-content/mediaApp/yo-creo-mi-realidad/01B.jpg';
var dogpics=[dog1,dog2];
var yourPics = [
dogpics,
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/02F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/02B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/03F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/03B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/04F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/04B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/05F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/05B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/06F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/06B.jpg' ] //This array has 52 cards but I cutted it for example purposes
];
function get_random_number(array){
return Math.floor(Math.random() * array.length |0);
} // here is where I have tried to modify with other scripts like the one in this page https://stackoverflow.com/questions/38882487/select-random-item-from-array-remove-it-restart-once-array-is-empty with no success
randomizer.addEventListener("click", function() {
var rand_number = get_random_number(yourPics);
console.log(rand_number);
document.getElementById('img1').src = yourPics[rand_number][0];
document.getElementById('img2').src = yourPics[rand_number][1];
});
var card = document.querySelector('.card');
card.addEventListener( 'click', function() {
card.classList.toggle('is-flipped');
});
</script>`
Thank you for your help!
I don't fully understand what you mean by "remove in pairs", but I'll answer presuming you mean you wish to remove the image ending in 02F.jpg at the same time as removing the image ending in 02B.jpg, and then 03F.jpg at the same time as 03B.jpg.
The solution to this that I will propose is that we will structure your data a bit differently to begin with. That is, if those images, the "B image" and "F image" are linked, we could keep them in the same `javascript object. This would look like:
var yourPics = [
{
bImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/02F.jpg',
fImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/02B.jpg'
},
{
bImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/03F.jpg',
fImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/03B.jpg'
}...]
This would then be an array of objects, rather than strings. We can access the bImage property of an object with just
myObject = yourPics[0]
myObject.bImage
We could delete one of those objects those at random via splice.
myRandomlyRemovedObject = yourPics.splice(myIndexToDeleteFrom, 1) would remove 1 object from yourPics at position of myIndexToDeleteFrom, which you presumably would choose randomly. myRandomlyRemovedObject would be assigned to the one object we removed.
I think this object based approach is safer since you will know for a fact that you will removed both matching strings at the same time.

Understanding the role of a variable accessing an array item/s using [ ]. (WatchAndCode related)

I am currently working my way through a WatchAndCode course.
We are building a reminders/todo-style JavaScript app.
I am struggling to understand how the [i] in the this.todos[i].todoText section of the code works. The code does not function properly without it, so it is needed.
Please can somebody explain its role? I understand you can target a specific item in an array using brackets, but I am still confused how i works exactly.
Any help would be so appreciated. If possible, can there be no alterations to the code. I am a beginner so I'm taking it step-by-step, I understand there are probably more efficient ways to go about writing the code.
Here is the full code below:
var todoList = {
todos: [],
displayTodos: function() {
if (this.todos.length === 0) {
console.log('Your todo list is empty!');
} else {
console.log('My todos:');
for (var i = 0; i < this.todos.length; i++) {
console.log(this.todos[i].todoText);
}
}
},
addTodo: function(todoText) {
this.todos.push({
todoText: todoText,
completed: false
});
this.displayTodos();
},
changeTodo: function(position, todoText) {
this.todos[position].todoText = todoText;
this.displayTodos();
},
deleteTodo: function(position) {
this.todos.splice(position, 1);
this.displayTodos();
},
toggleCompleted: function(position) {
var todo = this.todos[position];
todo.completed = !todo.completed;
this.displayTodos();
}
};
Thanks again
Basically the array consists of multiple objects, to access those objects you need to access their position in the array. The "i" in the for loop is just a variable that is going through numbers 0 to the length of the array. So the "i" will have the value 0 at start and todos[i] will actually access todos[0] (the first element of the array) and add the text from todoText to that element in the array, then the "i" becomes 1 because the for loop adds i++, it could have been written e.g i+2 if you need every 2nd element, etc.
Loops are used so you don't have to do that manually, it might be easier for you to understand if it was written manually at first :
this.todos[0].todoText
this.todos[1].todoText
this.todos[2].todoText
this.todos[3].todoText
this.todos[4].todoText
this.todos[5].todoText
this.todos[6].todoText
That's the code that happens in the loop basically so you don't have to write it all by yourself, accessing every object of the array and doing something with it. (in my example the array has 7 elements)
Hope this clarify's it a bit for you.
You can access a property's value via:
object.an_indentifier_that_matches_the_name
or
object["an_expression_that_evaluates_as_a_string_that_matches_the_name"]
Arrays are just a type of object where property names that are integers (like "1") are given special meaning.
You can use any expression (a string literal, a variable, a function call, etc) between the square brackets, so long as you get something which can be converted to a string at the end of it.
var foo = ["a", "b", "c"];
var i = 2;
console.log(foo[2]);
console.log(foo["2"]);
console.log(foo[4 / 2]);
console.log(foo[i]);
You have i in the loop:
for (var i = 0; i < this.todos.length; i++) {
console.log(this.todos[i].todoText);
}
So you have to know how does loops works. First, you declare a variable i at the first part of the loop: var i = 0;. Then, in the middle, you have a condition, it will be checked in every loop iteration. If it evaluates to true, current iteration will be executed (this code will be executed: console.log(this.todos[i].todoText);). And finally, after one iteration, variable i will be increased by 1 (it happens here: i++).
So i is just a counter, it starts from 0 and increasing by 1 after every iteration. And in first iteration the actual console.log in the body of loop will be console.log(this.todos[0].todoText);, in the second it will be console.log(this.todos[1].todoText);, and so on, while this condition: i < this.todos.length; evaluates to true.
When you're doing todos[0], it gets the first element from todos array.
For more info about for loops: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for
Firstly you should read up on arrays here: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Arrays
Lets assume this.todos equals this array:
[
{todoText: 'Foo', completed: false},
{todoText: 'Bar', completed: false},
]
If you view this like:
[
0: {todoText: 'Foo', completed: false},
1: {todoText: 'Bar', completed: false},
]
The number is the index for that entry in the array. Run snippet below to see output:
let todos = [
{todoText: 'Foo', completed: false},
{todoText: 'Bar', completed: false},
];
console.log('if `i` equals 0 then the output of `todos[i]` is:')
var i = 0;
console.log(todos[i]);
console.log('if you want to get the `todoText` value for this entry then the value of `todos[i].todoText` is:')
console.log(todos[i].todoText);
Let's suppose we have this array:
this.todos = [
/* Index "0" */ { todoText: 'my-text', otherProperty: 'blabla', },
/* Index "1" */ { todoText: 'another-text', otherProperty: 'blabla', },
/* Index "2" */ { todoText: 'last-text', otherProperty: 'blabla', },
];
// The line "this.todos[0].todoText" will show "my-text"
// The line "this.todos[2].todoText" will show "last-text"
In order to display the content of you todo-list, you have to pass through all of your todo elements, so you will make a loop through all of them with the for statement:
// Pass through your todo list.
// We began to the first element (with index "0"), the current step is stocked into "i" (var i = 0)
// Until we reach the last one (i < this.todos.length)
// When a step is over, we increment it of one (i++, or i += 1)
for (var i = 0 ; i < this.todos.length ; i++)
For each one, you write its content:
console.log(this.todos[i].todoText);
You precised that your first element in the index "0", so it's like you remplaced "i" by "0":
console.log(this.todos[0].todoText); // Remember, "this.todos[0].todoText" show "my-text".
And so on, until you loop through your last element:
console.log(this.todos[1].todoText); // Remember, "this.todos[1].todoText" show "another-text".
console.log(this.todos[2].todoText); // Remember, "this.todos[2].todoText" show "last-text".
Your loop is executed as long as i < this.todos.length is true. It ends after that because this.todos[3] does not exist and return false.

Problems using loop index to extract data from object

I have some JSON data that I'm trying to extract.
I'm trying to extract and reformat some of the data to an array. I'm looping through the data, but am having problems extracting the data within a nested submeaning object.
JSON data:
var data = [
{
"meaning": "a procedure intended to establish the quality, performance, or reliability of something, especially before it is taken into widespread use.",
"examples": [
"no sparking was visible during the tests"
],
"submeanings": [
{
"meaning": "a short written or spoken examination of a person's proficiency or knowledge.",
"examples": [
"a spelling test"
]
},
{
"meaning": "an event or situation that reveals the strength or quality of someone or something by putting them under strain.",
"examples": [
"this is the first serious test of the peace agreement"
]
},
{
"meaning": "an examination of part of the body or a body fluid for medical purposes, especially by means of a chemical or mechanical procedure rather than simple inspection.",
"examples": [
"a test for HIV",
"eye tests"
]
},
{
"meaning": "a procedure employed to identify a substance or to reveal the presence or absence of a constituent within a substance."
}
]
},
{
"meaning": "a movable hearth in a reverberating furnace, used for separating gold or silver from lead."
}
]
Algorithm:
// array to hold definitions
var definitions = [];
for (var i = 0; i < data.length; i++) {
// push first
definitions.push(data[i]['meaning']);
// push second, if submeaning data exists
if (data[i]['submeanings'].length >= 1) {
definitions.push(data[i]['submeanings'][i]['meaning']);
}
}
When I run this code, I'm getting the following error:
Uncaught TypeError: Cannot read property 'length' of undefined(…)
Any help is appreciated.
Check if submeanings exists, before you ask for it's length.
// push second, if submeaning data exists
if (data[i] && data[i]['submeanings'] && data[i]['submeanings'].length >= 1) {
definitions.push(data[i]['submeanings'][i]['meaning']);
}
You have to check if the objects have submeanings before checking their length. Change
if (data[i]['submeanings'].length >= 1)
to
if (data[i]['submeanings'] && data[i]['submeanings'].length >= 1)
Furthermore, if there are multiple submeanings, you'll need a separate loop to extract the submeanings, like so:
if (data[i]['submeanings'] && data[i]['submeanings'].length >= 1) {
for(var j = 0; j < data[i]['submeanings'].length; j++) {
definitions.push(data[i]['submeanings'][j]['meaning']);
}
}
Keeping track of multiple indices is difficult though, so I'd suggest looking into using forEach instead: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
That because you don't have attribute submeanings in the second object of your JSON data, so you should check if the object has property using hasOwnProperty() then get it :
if (data[i].hasOwnProperty('submeanings')) {
definitions.push(data[i]['submeanings'][i]['meaning']);
}
Hope this helps.

Meteor 1.0 - Mongo queries using variables as key, including $inc

I'm working with a large dataset that needs to be efficient with its Mongo queries. The application uses the Ford-Fulkerson algorithm to calculate recommendations and runs in polynomial time, so efficiency is extremely important. The syntax is ES6, but everything is basically the same.
This is an approximation of the data I'm working with. An array of items and one item being matched up against the other items:
let items = ["pen", "marker", "crayon", "pencil"];
let match = "sharpie";
Eventually, we will iterate over match and increase the weight of the pairing by 1. So, after going through the function, my ideal data looks like this:
{
sharpie: {
pen: 1,
marker: 1,
crayon: 1,
pencil: 1
}
}
To further elaborate, the value next to each key is the weight of that relationship, which is to say, the number of times those items have been paired together. What I would like to have happen is something like this:
// For each in the items array, check to see if the pairing already
// exists. If it does, increment. If it does not, create it.
_.each(items, function(item, i) {
Database.upsert({ match: { $exist: true }}, { match: { $inc: { item: 1 } } });
})
The problem, of course, is that Mongo does not allow bracket notation, nor does it allow for variable names as keys (match). The other problem, as I've learned, is that Mongo also has problems with deeply nested $inc operators ('The dollar ($) prefixed field \'$inc\' in \'3LhmpJMe9Es6r5HLs.$inc\' is not valid for storage.' }).
Is there anything I can do to make this in as few queries as possible? I'm open to suggestions.
EDIT
I attempted to create objects to pass into the Mongo query:
_.each(items, function(item, i) {
let selector = {};
selector[match] = {};
selector[match][item] = {};
let modifier = {};
modifier[match] = {};
modifier[match]["$inc"] = {};
modifier[match]["$inc"][item] = 1
Database.upsert(selector, modifier);
Unfortunately, it still doesn't work. The $inc breaks the query and it won't let me go more than 1 level deep to change anything.
Solution
This is the function I ended up implementing. It works like a charm! Thanks Matt.
_.each(items, function(item, i) {
let incMod = {$inc:{}};
let matchMod = {$inc:{}};
matchMod.$inc[match] = 1;
incMod.$inc[item] = 1;
Database.upsert({node: item}, matchMod);
Database.upsert({node: match}, incMod);
});
I think the trouble comes from your ER model. a sharpie isn't a standalone entity, a sharpie is an item. The relationship between 1 item and other items is such that 1 item has many items (1:M recursive) and each item-pairing has a weight.
Fully normalized, you'd have an items table & a weights table. The items table would have the items. The weights table would have something like item1, item2, weight (in doing so, you can have asymmetrical weighting, e.g. sharpie:pencil = 1, pencil:sharpie = .5, which is useful when calculating pushback in the FFA, but I don't think that applies in your case.
Great, now let's mongotize it.
When we say 1 item has many items, that "many" is probably not going to exceed a few thousand (think 16MB document cap). That means it's actually 1-to-few, which means we can nest the data, either using subdocs or fields.
So, let's check out that schema!
doc =
{
_id: "sharpie",
crayon: 1,
pencil: 1
}
What do we see? sharpie isn't a key, it's a value. This makes everything easy. We leave the items as fields. The reason we don't use an array of objects is because this is faster & cleaner (no need to iterate over the array to find the matching _id).
var match = "sharpie";
var items = ["pen", "marker", "crayon", "pencil"];
var incMod = {$inc:{}};
var matchMod = {$inc:{}};
matchMod.$inc[match] = 1;
for (var i = 0; i < items.length; i++) {
Collection.upsert({_id: items[i]}, matchMod);
incMod.$inc[items[i]] = 1;
}
Collection.upsert({_id: match}, incMod);
That's the easy part. The hard part is figuring out why you want to use an FFA for a suggestion engine :-P.

Categories