Mongo Query: Copy field+value from one document to another - javascript

I cant find an efficient solution for my query. Here is what I'd like to do.
I have a collection called "releases"
I have a collection called "tracks"
"releases" has the field "releaseDate", which is filled for every "releases" document.
I now would like to have the corresponding releaseDate of "releases" in every "tracks" document under db.tracks.releases.releaseDate
(db.tracks.releases exists already, but without the releaseDate).
To find the corresponding releaseDate, "releases" and "tracks" both have an ICPN number:
db.releases.icpn
db.tracks.releases.icpn
Those can be compared to find the correct releaseDate for every track.
To summarize it:
I need to go through every track, look at the ICPN, search for the release with the same ICPN und copy the releaseDate from the release to the track.
I could only make it work like this:
db.releases.find().forEach(function(doc) {
db.tracks.update(
{ "releases.0.ICPN" : doc.ICPN},
{
$set: { "releases.0.releaseDate": doc.releaseDate},
$currentDate: {"lastModified": true}
},
{ multi: true}
)
})
That worked for my test DB, but it is ultra inefficient to go through every track for every single release.
Do you have any hints to get me on the right track?
Kind regards,
Alex

Currently that query only finds docs where the first element in releases is ICPN
You want to query without an absolute index like:
{ "releases.ICPN" : doc.ICPN}
and update with the positional operator so it updates the one it found to match:
$set: { "releases.$.releaseDate": doc.releaseDate},

Well, the query partially works now, but it stops after editing 1700 tracks (of 160000), although I set multi to true and the condition is true for all other tracks/releases (I checked some unmodified tracks manually if ICPNs match).

Related

How should I write this conditions to make the button onClick function do what I want with my array?

I have an array of objects that is basically like this.
const characters = [
{
id: 1
name: batman
biography: {
born-in: gotham
good-or-bad: good
}
stats: {
strength: 85
speed: 90
intelligence: 95
}
}
{
id: 2
name: superman
biography: {
born-in: krypton
good-or-bad: good
}
stats: {
strength: 90
speed: 85
intelligence: 80
}
}
{
id: 3
name: joker
biography: {
born-in: gotham
good-or-bad: bad
}
stats: {
strength: 70
speed: 95
intelligence: 100
}
}
]
Then, after mapping and displaying the objects in my page, I add a button that allows the user to mark the character as a favorite. The user can only add up to 6 favorites.
const [favorites, setFavorites = useState([]);
const addFavorite = () => {
favorites.length === 6 ?
console.log("favorites' list is full!") :
setFavorites(favorites.concat(character))
}
{characters.map((character)=>{
const {props} = character;
return (
<div>{props}</div>
<button onClick={addFavorite}>add to favorites</button>
);
})}
Now, what I want to do (and I don't know how to, after many attempts)
preventing the user to add the same character twice to favorites. (I have tried with favorites.contains(character)? or favorites.contains({character})? but it didn't work.)
if the character is already a favorite, make the button change to a button that removes the favorite instead (changing both the function and the button text.)(I have no idea how to do this).
Make an average score of all favorites' each stat. (For example, with your chosen favorites your average speed is xxx and your average strength is xxx).
Last, but not least, favorites list must have up to 3 good characters and 3 bad characters. So, if the good or bad characters in the favorites' list are already 3, user cannot choose another good or bad character as favorite. I also don't know how to proceed with this one.
I'm working in a school project and I found my way through most of it, but I realise I still have things to learn, mostly about object props and how to access to them. Thank you. If anything is not clear, please say so and I will add the required data.
So, since you've already figured it out yourself, here is the detailed explanation.
find is a function ( or what fancy developers like to say a higher order function ) available for javascript arrays that accepts a function which must return a boolean value i.e., either true or false.
---Quick detour---
A function which must return a boolean value is called a predicate, and this is exactly what's available in the IDE hints if you hover over find
---Detour end---
It accepts multiple parameters, with only the predicate being mandatory, and the rest are optional, i'm skipping all the optional ones, so that's your homework, read the docs or the articles at the end of this answer.
As you can read in the hint itself, it will call the predicate, once for each element in the array, until it can find one which will return true & return the value of the element, undefined otherwise.
Which means : that the first parameter in the predicate is going to be your object and the same predicate will be executed on it for all the elements.
Now observe your solution carefully:
find( savedChar => savedChar.id === character.id )
savedChar is one of the objects in the array, and it needs to be compared with the character object, and id which is the short form of identity will always find it accurately.
Finally, quick answers to your problems.
Use find to see if the character is already available, if yes, simply don't add it in your collection.
This will require you to change your render logic, find if the object is in the favorites and render it differently.
just like find, there is a method called reduce, why don't you give it a shot? but that might be a little difficult, so you can use a simple for loop instead.
find(savedChar => savedChar["good-or-bad"]) <- what would this result? figure it out.
And for more reading material :
https://medium.com/swlh/array-helper-methods-in-es6-28fc5e5a5dc9
same but more detailed :
https://codeburst.io/learn-and-understand-es6-helpers-easily-d41401184487

JavaScript Objects using this keyword

I'm currently working on some practice for JavaScript and am really confused about what I am to do here. Any help would be appreciated!
Define a method named orderOfAppearance() that takes the name of a role as an argument and returns that role's order of appearance. If the role is not found, the method returns 0. Ex: orderOfAppearance("Elizabeth Swann") returns 3. Hint: A method may access the object's properties using the keyword this. Ex: this.title accesses the object's title property.
// Code will be tested with different roles and movies
let movie = {
title: "Pirates of the Caribbean: At World's End",
director: "Gore Verbinski",
composer: "Hans Zimmer",
roles: [ // Roles are stored in order of appearance
"Jack Sparrow",
"Will Turner",
"Elizabeth Swann",
"Hector Barbossa"
],
orderOfAppearance: function(role) {
/* Your solution goes here */
if (!(role in this.roles)) {
return 0;
}
return this.role;
/*Solution ends here */
}
};
A doubly linked list has 2 links. Let's use common sense for a moment instead of blind deferral to whatever you think your TA said: "Doubly linked list" obviously implies "2 links", otherwise that'd be an extremely silly name, no?
So, there must be 2 links. You seem to be under the impression that 'you must not have a pointer to the tail' implies 'there must be only one link'. That can't be right, what with 'doubly linked list' and all.
The right answer is presumably that each node has 2 links, but these links aren't 'head and tail', but 'next' and 'previous'.
For a ringed list (where moving forward from the last element gets you back to the first, and moving backwards from the first element gets you to the last), the last node's "next" link goes back to the first node, and conversely, the first node's "previous" link goes to the last (so it points at the same thing a hypothetical 'tail' would point to).
However, that'd be only true for the first node in your linked list structure (that would be the only node for which 'prev' points at the tail).
Your TA either means 'have next/prev links, not next/tail links' as per above, or has no idea what they are talking about. Let's give em the benefit of the doubt ;) – as it's homework I'll leave the actual writing of the data structure, using a definition of DoublyLinkedNode<T> that includes fields DoublyLinkedNode<T> next, prev;, as an exercise for you.
What I was looking for was to refer to the last use first.getPrev(), since its a circle the last is before the first.
I have the same question. This is found in a zybooks online textbook. The textbook's portion of JavaScript is horrible!! It explains little about what we need to do to solve the questions posed. I have been so frustrated and had to look to online sources to try to solve the problems in the book. Was there a solution to this question?
Define a method named orderOfAppearance() that takes the name of a role as an argument and returns that role's order of appearance. If the role is not found, the method returns 0. Ex: orderOfAppearance("Elizabeth Swann") returns 3. Hint: A method may access the object's properties using the keyword this. Ex: this.title accesses the object's title property.
// Code will be tested with different roles and movies
let movie = {
title: "Pirates of the Caribbean: At World's End",
director: "Gore Verbinski",
composer: "Hans Zimmer",
roles: [ // Roles are stored in order of appearance
"Jack Sparrow",
"Will Turner",
"Elizabeth Swann",
"Hector Barbossa"
],
orderOfAppearance: function(role) {
\\your solution goes here
return this.roles;
}
};
I entered return this.roles; but everything else in the code came from the computer. the answer comes up wrong and says:
Testing orderOfAppearance("Elizabeth Swann")
Yours and expected differ. See highlights below.
Yours
Jack Sparrow,Will Turner,Elizabeth Swann,Hector Barbossa
Expected
3
I also entered:
return this.roles[3]
but that gives Hector Barbossa. I don't know what to do!
If the role is not found, the method returns 0. Ex: orderOfAppearance("Elizabeth Swann") returns 3.
You can use .indexOf() to get the index of the role passed to the orderOfAppearance() method and then add 1 to it. .indexOf() will return -1 if the role is not found and it will become 0 after adding 1.
let movie = {
title: "Pirates of the Caribbean: At World's End",
director: 'Gore Verbinski',
composer: 'Hans Zimmer',
roles: ['Jack Sparrow', 'Will Turner', 'Elizabeth Swann', 'Hector Barbossa'],
orderOfAppearance: function(role) {
return this.roles.indexOf(role) + 1
},
}
console.log(movie.orderOfAppearance('Jack Sparrow'))
console.log(movie.orderOfAppearance('Will Turner'))
console.log(movie.orderOfAppearance('Elizabeth Swann'))
console.log(movie.orderOfAppearance('Hector Barbossa'))
console.log(movie.orderOfAppearance('Something that does not exist in roles'))

Mongodb check If field exists in an sub-document of an array

I am trying to check If a field exists in a sub-document of an array and if it does, it will only provide those documents in the callback. But every time I log the callback document it gives me all values in my array instead of ones based on the query.
I am following this tutorial
And the only difference is I am using the findOne function instead of find function but it still gives me back all values. I tried using find and it does the same thing.
I am also using the same collection style as the example in the link above.
Example
In the image above you can see in the image above I have a document with a uid field and a contacts array. What I am trying to do is first select a document based on the inputted uid. Then after selecting that document then I want to display the values from the contacts array where contacts.uid field exists. So from the image above only values that would be displayed is contacts[0] and contacts[3] because contacts1 doesn't have a uid field.
Contact.contactModel.findOne({$and: [
{uid: self.uid},
{contacts: {
$elemMatch: {
uid: {
$exists: true,
$ne: undefined,
}
}
}}
]}
You problems come from a misconception about data modeling in MongoDB, not uncommon for developers coming from other DBMS. Let me illustrate this with the example of how data modeling works with an RDBMS vs MongoDB (and a lot of the other NoSQL databases as well).
With an RDBMS, you identify your entities and their properties. Next, you identify the relations, normalize the data model and bang your had against the wall for a few to get the UPPER LEFT ABOVE AND BEYOND JOIN™ that will answer the questions arising from use case A. Then, you pretty much do the same for use case B.
With MongoDB, you would turn this upside down. Looking at your use cases, you would try to find out what information you need to answer the questions arising from the use case and then model your data so that those questions can get answered in the most efficient way.
Let us stick with your example of a contacts database. A few assumptions to be made here:
Each user can have an arbitrary number of contacts.
Each contact and each user need to be uniquely identified by something other than a name, because names can change and whatnot.
Redundancy is not a bad thing.
With the first assumption, embedding contacts into a user document is out of question, since there is a document size limit. Regarding our second assumption: the uid field becomes not redundant, but simply useless, as there already is the _id field uniquely identifying the data set in question.
The use cases
Let us look at some use cases, which are simplified for the sake of the example, but it will give you the picture.
Given a user, I want to find a single contact.
Given a user, I want to find all of his contacts.
Given a user, I want to find the details of his contact "John Doe"
Given a contact, I want to edit it.
Given a contact, I want to delete it.
The data models
User
{
"_id": new ObjectId(),
"name": new String(),
"whatever": {}
}
Contact
{
"_id": new ObjectId(),
"contactOf": ObjectId(),
"name": new String(),
"phone": new String()
}
Obviously, contactOf refers to an ObjectId which must exist in the User collection.
The implementations
Given a user, I want to find a single contact.
If I have the user object, I have it's _id, and the query for a single contact becomes as easy as
db.contacts.findOne({"contactOf":self._id})
Given a user, I want to find all of his contacts.
Equally easy:
db.contacts.find({"contactOf":self._id})
Given a user, I want to find the details of his contact "John Doe"
db.contacts.find({"contactOf":self._id,"name":"John Doe"})
Now we have the contact one way or the other, including his/her/undecided/choose not to say _id, we can easily edit/delete it:
Given a contact, I want to edit it.
db.contacts.update({"_id":contact._id},{$set:{"name":"John F Doe"}})
I trust that by now you get an idea on how to delete John from the contacts of our user.
Notes
Indices
With your data model, you would have needed to add additional indices for the uid fields - which serves no purpose, as we found out. Furthermore, _id is indexed by default, so we make good use of this index. An additional index should be done on the contact collection, however:
db.contact.ensureIndex({"contactOf":1,"name":1})
Normalization
Not done here at all. The reasons for this are manifold, but the most important is that while John Doe might have only have the mobile number of "Mallory H Ousefriend", his wife Jane Doe might also have the email address "janes_naughty_boy#censored.com" - which at least Mallory surely would not want to pop up in John's contact list. So even if we had identity of a contact, you most likely would not want to reflect that.
Conclusion
With a little bit of data remodeling, we reduced the number of additional indices we need to 1, made the queries much simpler and circumvented the BSON document size limit. As for the performance, I guess we are talking of at least one order of magnitude.
In the tutorial you mentioned above, they pass 2 parameters to the method, one for filter and one for projection but you just passed one, that's the difference. You can change your query to be like this:
Contact.contactModel.findOne(
{uid: self.uid},
{contacts: {
$elemMatch: {
uid: {
$exists: true,
$ne: undefined,
}
}
}}
)
The agg framework makes filtering for existence of a field a little tricky. I believe the OP wants all docs where a field exists in an array of subdocs and then to return ONLY those subdocs where the field exists. The following should do the trick:
var inputtedUID = "0"; // doesn't matter
db.foo.aggregate(
[
// This $match finds the docs with our input UID:
{$match: {"uid": inputtedUID }}
// ... and the $addFields/$filter will strip out those entries in contacts where contacts.uid does NOT exist. We wish we could use {cond: {$zz.name: {$exists:true} }} but
// we cannot use $exists here so we need the convoluted $ifNull treatment. Note we
// overwrite the original contacts with the filtered contacts:
,{$addFields: {contacts: {$filter: {
input: "$contacts",
as: "zz",
cond: {$ne: [ {$ifNull:["$$zz.uid",null]}, null]}
}}
}}
,{$limit:1} // just get 1 like findOne()
]);
show(c);
{
"_id" : 0,
"uid" : 0,
"contacts" : [
{
"uid" : "buzz",
"n" : 1
},
{
"uid" : "dave",
"n" : 2
}
]
}

Dialogflow composite entity parameter is undefined when it shouldn't be

Long story short, I'm making a real estate agent chatbot and I just implemented a filter allowing the user to search within a range of numbers (e.g. at least one bedroom, under $2500). In order to do this, I made an entity_range composite entity composed of the range type (e.g. at most, exactly) and the entity itself (unit-currency for price, plus some custom entities like the number of bedrooms). Prior to creating entity_range, the entities themselves worked fine. But now, it seems as though the entity part of entity_range is undefined. See a sample of my code below:
function get_count(req, res) {
console.log("price: " + req.queryResult.parameters["entity_range"]["unit-currency"])
var price, beds, baths, num_filter_funct
if(req.queryResult.parameters["entity_range"]["unit-currency"] != undefined) {
price = req.queryResult.parameters["entity_range"]
console.log("price: " + price)
} else {
console.log("could not find parameter")
}
Before creating entity_range, my code looked exactly the same, except without ["entity_range"] between parameters and ["unit-currency"]. Anyway, this code logs:
price: undefined
could not find parameter
after the input "How many for $2500," with the following diagnostic info:
...
"queryResult": {
"queryText": "how many for $2500",
"parameters": {
"entity_range": [
{
"unit-currency": {
"amount": 2500,
"currency": "USD"
}
}
]
}...
So the entity "unit-currency" is recognized by Dialogflow, but not by my program. entity_range does allow users to not specify a range, so that's not the issue:
see screenshot here.
I would greatly appreciate any advice you have to offer!
That JSON shows entity_range being an array instead of an object. an object.
parameters.entity_range[0][“unit-currency”] should work. Note the [0]. You’ll also want to add some checks before this to make sure enitiy_range exists and it’s length is > 0.
And this part is just a guess but perhaps you mistakenly clicked the “Is List” box for this parameter in dialogflow? I’m checking it would probably make it be an object instead of an array and your existing code would work.

CouchDB find paired documents and list remaining unpaired documents

I'm relatively new to NoSQL, but I have been enjoying the journey very much! I am however finding the map-reduce way of life a bit tricky! I need some help with a problem!
I have a database with two types of documents, opening transactions and closing transactions. For replication and offline functionality reasons I cannot merge the data into one document. The opening transaction document looks something like :
{
_id: "transaction-open-randomgeneratedstring",
type: "transactions-open",
vehicle: "vehicle-id",
created: "date string"
}
The closing documents looks something like:
{
_id: "transaction-close-randomgeneratedstring",
type: "transactions-close",
openid: "transaction-open-randomgeneratedstring",
created: "date string"
}
The randomgeneratedstring of a closing transactions match the randomgeneratedstring of the corresponding opening transaction.
I need a map-reduce to give me the list of open transactions that does not have a corresponding closing transaction. This will basically give me a list of outstanding transactions.
This is the map-reduce I have thus far, but it is not doing the job.
{
"map": function(doc) {
if(doc.type == "transactions-open") {
emit([doc._id, 0], "OPEN");
}
if(doc.type == "transactions-close"){
emit([doc.openid, 1], "CLOSE");
}
},
"reduce": function(keys, values, rereduce) {
var unique_labels = {};
var open = {};
keys.forEach(function(label) {
if(!unique_labels[label[0]]) {
unique_labels[label[0]] = true;
} else {
open[label[0]] = true;
}
});
return open;
}
}
I am open for changes in the _id naming / structure, but I cannot combine the two documents into one.
Thanks!
EDIT
Based on response from Hod, I changed the reduce to look like:
function(keys, values, rereducer)
{
if(values.length == 1)
return true;
}
This is certainly a step in the right direction, but the unwanted transactions are still in the result set, the value is only null. Is there no way to get those out of the result set?
As described - what you would do with a Join in SQL you do with a reduce in CouchDB. Code something like this - not tested:
{
"map": function(doc) {
if(doc.type == "transactions-open") {
emit([doc._id], 1);
}
if(doc.type == "transactions-close"){
emit([doc.openid], -1);
}
},
"reduce": "_sum";
}
So we emit a 1 for an open transaction under an ID and a -1 for a close under the same ID. Now when you reduce you will get a result for each ID of:
-1 = Closed with no record of an open (error condition).
0 = Opened and Closed
1 = Open and not yet closed.
The problem is with the keys parameter in your reduce function. The reduce phase is not called once with all possible keys. It's called per distinct key, and based on the group_level you specify.
Looking at your code, if you haven't specified any group_level, your reduce function is going to get called for every document separately.
Because you're emitting the id of the open transaction doc for both open and close markers, if you grouped at the first level, you'd get open or open/close pairs. You're still only getting a reduction on a limited set of docs at a time.
You could fix this either in your logic calling the query, or by emitting a key that let's you reduce on the entire set at once. (I imagine there are other ways too. These are the ones that come to mind.)
If you use the key approach, you'd need to emit something that looked like ["transaction", doc._id, 0]. Then a first level grouping would give you the whole transaction set like you're current code expects.
EDIT (Adding information based on edit of question.)
The reduce function is going to get called with whatever grouping you set up. It's always going to return something, even if it's just no results emitted (i.e. null).
If you don't want to handle that in the logic that's running the queries and processing the results, you need to use an approach that will allow you to group all the transaction documents together, instead of just the documents for a single transaction.
Based on what you've done so far, another approach would be to forgo the reduce phase and just look at the number of results returned by a query that's limited to the unique doc id.

Categories