I have done some work to do a deep comparison (via Underscore and diff) between two objects (actually a pre-save and post-save version of the same document) in order to isolate the section that is different after a save. Take this document structure as an example:
{
_id: 4d39fe8b23dac43194a7f571,
name: {
first: "Jane",
last: "Smith"
}
services: [
{
service: "typeOne",
history: [
{ _id: 121,
completed: true,
title: "rookie"
},
{ _id: 122,
completed: false,
title: "novice"
}
]
},
{
service: "typeTwo",
history: [
{ _id: 135,
completed: true,
title: "rookie"
},
{ _id: 136,
completed: false,
title: "novice"
}
]
}
]
}
If a new element is added to the history array I'm able to successfully parse out that change.
However, in addition to pulling out this changed section, I also want to be able to effectively walk up from history in order to find the value for service, because I also need to know which of the two services array elements actually changed. Is there a way I can do this with native es6 JavaScript?
If not, is there a library I can use to determine this? Right now I'm able to get the value for "service" via indexing:
if (diff.path[1] === 0) {
targetService = "typeOne";
} else if (diff.path[1] === 1) {
targetService = "typeTwo";
} else if (diff.path[1] === 2) {
targetService = "typeThree";
}
But from my understanding this isn't full proof, because there's no guarantee the order of elements within "services" couldn't change at some point. I suppose this indexing method could work if I could enforce the ordering of the elements within the services array. I'm just not sure if there's a way to do that (open to suggestions if it is possible).
deep-diff gives you the path to this change, something like this:
{
kind: 'N',
path: ['services', 1, 'history'],
// ... other properties
}
You can use this path to track the changed object:
tree.services[changes.path[1]].service // 'typeTwo'
Related
I have a MongoDB collection of documents, using a schema like this:
var schema = new Schema({
name: String,
images: [{
uri: string,
active: Boolean
}]
});
I'd like to get all documents (or filter using some criteria), but retrieve - in the images array - only the items with a specific property (in my case, it's {active: true}).
This is what I do now:
db.people.find( { 'images.active': true } )
But this retrieves only documents with at least one image which is active, which is not what I need.
I know of course I can filter in code after the find is returned, but I do not like wasting memory.
Is there a way I can filter array items in a document using mongoose?
Here is the aggregation you're looking for:
db.collection.aggregate([
{
$match: {}
},
{
$project: {
name: true,
images: {
$filter: {
input: "$images",
as: "images",
cond: {
$eq: [
"$$images.active",
true
]
}
}
}
}
}
])
https://mongoplayground.net/p/t_VxjfiBBMK
I am developing a CLI using Enquirer. I want user of the CLI to write javascript on a json.
So, i want something like this :
Create a Rule on the the data
const model = {
reviews: {
'5': [
{
customerId: 'A2OKPZ5S9F78PD',
rating: '5',
asin: 'asin2',
reviewStatus: 'APPROVED',
reviewId: 'R379DKACZQRXME',
},
],
'4': [
{
customerId: 'A2OKPZ5S9F78PD',
rating: '4',
asin: 'asin2',
reviewStatus: 'APPROVED',
reviewId: 'R379DKACZQRXME',
},
],
},
entityType: 'LIVE_EVENT',
entityId: 'event2',
};
Then user writes the rule.
Object.values(model.reviews).forEach(reviews =>
(reviews as any).forEach(review => {
if (parseInt(review.rating) < 3 && attributes.reviewId.Value.includes(review.reviewId)) {
output.push({
exceptionName: `Customer ${review.customerId} left a review ${review.reviewId} with rating ${review.rating}`,
});
}
})
);
While writing this rule, Since it is on the above json model, I want to provide autocomplete options on javascript and validate if it is correct javascript.
Is there a way to do this ?
If I'm understanding your question correctly, it sounds like you want to take the model object and write it to a JSON file.
If this is your goal, simply do the following:
import { writeFileSync } from "fs";
// Define the model
const model: any = { foo: bar };
// Transform the model object to JSON
const modelJSON: string = JSON.stringify(model, null, 4); // Indents the JSON 4-spaces
// Write the modelJSON to `model.json`
writeFileSync("./model.json", modelJSON);
The above is TypeScript, but the standard JavaScript version is basically the same. Make sure you add #types/node to your package.json file if you're using TypeScript - hope this helps!
I am trying to add an object to an array in MongoDB. I don't want it to be duplicated.
I am trying to update the user read array by using $addToset in findOneAndUpdate. However, it is inserting duplicate because of timestamp; the timestamp is an important property. I can't negate it. Can I insert based on key like userId? Please let me know.
{
_id: 'ddeecd8b-79b5-437d-9026-d0663b53ad8d',
message: 'hello world notification',
deliverToUsersList: [ '123-xxx-xx', '124-xxx-xx']
userRead: [
{
isOpened: true,
userId: '123-xxx-xx'
updatedOn: new Date(Date.now()).toISOString()
},
{
isOpened: true,
userId: '124-xxx-xx'
updatedOn: new Date(Date.now()).toISOString()
}
]
}
Add an index to the field userId and enable 'Avoid duplicates' in index settings.
I use Robo3T client to do that.
To add new objects without duplicate information into the userRead array, you have check for the duplicate information in the update method's query filter. For example, the following code will not allow adding new object with duplicate userId field value.
new_userId = "999-xxx-xx"
new_doc = { userId: new_userId, isOpened: true, updatedOn: ISODate() }
db.test_coll.findOneAndUpdate(
{ _id: 'ddeecd8b-79b5-437d-9026-d0663b53ad8d', "userRead.userId": { $ne: new_userId } },
{ $push: { "userRead" : new_doc } },
)
I'm writing a route in Express (Node.js) in which i pull some data from mongoose. Let's say that at some some point I need to compare if employee._id is in array of bad employees id::
let employees = await EmployeeModel.find().exec();
employees.forEach(function (employee) {
if (arrayOfBadEmployees.indexOf(employee._id) !== -1) {
employee.isBad = true;
}
});
console.log(employees);
console.log(employees[0].isBad);
and here's my output:
[ { __v: 0, name: 'Employee X', _id: 1 },
{ __v: 0, name: 'Employee Y', _id: 3 },
{ __v: 0, name: 'Employee Z', _id: 5 } ]
true
So when I can't see 'isBad' property when I console.log the whole array/object, but this property is still there? When i check with propertyIsEnumerable('isBad') it says true.
Mongoose, by default, returns an instance of MongooseDocument, which doesn't expose your data directly and adds convenience methods like populate or save
You can use the lean option to get raw objects instead.
MongooseDocument also exposes a toObject function if you need to get editable documents.
Below is the function I have used to fetch more photos from a service provider once clicked on more button:
showMore: function(){
this.$.resultList.collection.fetch({strategy:"merge",rpp:50});
}
This will call the fetch method defined in collection,
fetch: function(opts) {
this.params = {
feature: this.methodType || "popular",
image_size: 3,
sort: "created_at",
rpp: opts && opts.rpp || 25
};
return this.inherited(arguments);
},
This is working fine, but the problem is once more button is clicked, it should fetch another set of 25 photos and append it to my collection, but what actually happening is sometimes, it shows only say 5 photos along with previous photos .
What I understand by "merge" strategy is, if the records received is same as previous records, it will take only those records which are different from previously fetched records and updates the primarykey of the duplicate records.So one reason i am able to figure out is that, may be, it is fetching 25 photos next time, but because most of them are same as before, it is showing only those which are different from the previous fetched photos.
If i go with the "add" strategy, it works fine for one time, i.e, it shows another set of 25 photos appended to the collection, most of them are again same. But if click on more button one more time, no records are being added to the collection.No idea why this is happening.
How should i approach, if i want to fetch only new photos and append it to the collection.
Using the merge strategy is the right approach. Your description of merge is mostly accurate except that it doesn't update the primary key but instead updates the data for existing records with the same primary key.
It's difficult to say why using "add" doesn't always work. If the records don't have a primary key (which is id by default), "add" and "merge" should always add the records to the collection (unless you're using mergeKeys). If they do have a primary key, it's possible that you're trying to add duplicate records which Enyo will complain about and abort. Check your console log.
Without code, the only other suggestion is to set breakpoints and step through enyo.Collection.merge.
Here's an example of fetching records into a collection. If you comment out setting the id, merge and add strategies will always add records. If you comment out the merge strategy, the code will eventually error when requesting more records.
enyo.kind({
name: "ex.MockSource",
kind: "enyo.Source",
fetch: function(rec, opts) {
if(rec instanceof enyo.Model) {
rec.setObject(Faker.Helpers.createCard());
} else if (rec instanceof enyo.Collection) {
var count = opts && opts.count || 25;
var cards = [];
for(var i=0;i<count;i++) {
var card = Faker.Helpers.createCard();
// artificial id to force merges
card.id = enyo.now()%40;
cards.push(card);
}
opts.success(cards);
}
}
});
enyo.store.addSources({
mock: "ex.MockSource"
});
enyo.kind({
name: "ex.App",
kind: "Scroller",
bindings: [
{from: ".data", to: ".$.list.collection"},
{from: ".data.length", to: ".$.count.content", transform: function(v) {
return enyo.format("Displaying %s records", v);
}}
],
components: [
{name: "count"},
{name: "list", kind: "DataRepeater", components: [
{kind: "onyx.Item", components: [
{name: "name"},
{name: "phone"}
], bindings: [
{from: ".model.name", to: ".$.name.content"},
{from: ".model.phone", to: ".$.phone.content"}
]}
]},
{kind: "onyx.Button", content: "More", ontap: "moreTapped"}
],
create: enyo.inherit(function(sup) {
return function() {
sup.apply(this, arguments);
this.set("data", new enyo.Collection({
defaultSource: "mock"
}));
this.fetchRecords();
};
}),
fetchRecords: function() {
this.data.fetch({
count: 5,
strategy: "merge"
});
},
moreTapped: function() {
this.fetchRecords();
}
});
new ex.App().renderInto(document.body);