How to link records with lawnchair js - javascript

I am using Lawnchair JS from http://brian.io/lawnchair/ in my Phonegap Application to manage my database records. I am new to document stores but experienced with traditional relational databases. How should I go if I wanted to reference one record on another record. For example: I have multiple records for food ingredients and I have multiple records for recipes, in a recipes JSON, is there a way to reference an ingredient record?

To achieve what you want with a json document store like lawnchair, you just need to store the key name of the referenced document inside your document.
For instance you will have those food ingredient documents:
{key:'fi_1', name: 'rice', id: 1}
{key:'fi_2', name: 'corn', id: 2}
and those recipe documents:
{key:'r_332', ingredients: ['fi_1', 'fi_54'], name: 'risotto', id: 332}
{key:'r_333', ingredient: ['fi_12'], name:'cookie', id: 333}
you could store the list of all your recipes inside one document:
{key:'cookbook', recipes: ['r_1', 'r_2', .... 'r_332', 'r_333', .... ] }
you can then retrieve the cookbook document:
store.get('cookbook', function(data) {
var recipes = data.recipes;
// do something with the list of all recipes
});
and look for a recipe to retrieve its ingredients:
store.get('r_332', function(data) {
var ingredients = data.ingredients;
for (ingredient_key in ingredients) {
store.get(ingredient_key, function(ingredient_data) {
var name = ingredient_data.name;
var id = ingredient_data.id;
// do something with each ingredient
alert('ingredient: ' + name + ' - id: ' + id);
}
}
});
In the lists above, instead of storing the full key name of the referenced documents you could just store their ids as you are supposed to know their type and how to re-create the keyname from that (food ingredient : 'fi_' prefix followed by id, recipe : 'r_' followed by id..).

Related

Excluding results from Objection/Knex query based on withGraphFetched results

I have two models in Objection - "brands" and "offers".
Brand:
const { Model } = require('objection')
class Brand extends Model {
static get tableName() {
return 'brands'
}
...
static get relationMappings() {
const Offer = require('./offer-model')
return {
offer: {
relation: Model.HasManyRelation,
modelClass: Offer,
join: { from: 'brands.id', to: 'offers.brand_id' }
}
}
}
}
Offer:
const { Model } = require('objection')
class Offer extends Model {
static get tableName() {
return 'offers'
}
}
A brand has many offers, but I want to get brands which have at least 1 offer using withGraphFetched, excluding brands which have no offers. Here's what I have so far:
const brandModel = this.objection.models.brand
const query = brandModel.query().withGraphFetched('offer')
query.page(page, page_size)
const offers = await query
This returns the "joined" data, but also returns brands which don't have offers. For example:
[{
id:1,
name: 'brand 1',
offers: [{offerId: 1, offerName: 'offer 1'}]
},{
id:2,
name: 'brand 2',
offers: []
}]
In the above data, I don't want the brand with ID 2 to be in the result set.
I am using Objection/Knex to paginate the results, so I can't just exclude the brands with empty object arrays after the query has been executed.
I can achieve this using raw queries, but that means I can't use the Objection dynamic attributes and a few other key parts of Objection.
Thanks!
You can just tack a whereExists onto the query; something like
const query = brandModel.query()
.withGraphFetched('offer')
.whereExists(
(qb) => qb.select('id').from('offers')
.where('offers.brand_id', knex.ref('brands.id'))
);
Even though the whereExists bit is directly Knex, the query still goes through your models so stuff you've defined there should still apply (maybe unless you're doing something very wild that directly affects the columns used inside the whereExists)

Realm Delete an Object in One to Many relationship

I have a ONE TO MANY schema like this:
SHOP SCHEMA
const Shop = {
name: "Shop",
properties: {
_id: "objectId",
products:"Products[]"
}
}
PRODUCTS SCHEMA
const Products = {
name: "Products",
properties: {
_id: "objectId",
name : "string",
}
}
A shop has many products and as it can be seen 'pictorially' below
_id:'60f73ca7c1a70278596cc7d0',
products:[
{_id:1, name:'product1'},
{_id:2, name: 'product2'},
{_id:3, name: 'product3'}
]
Now, say I want to delete product2, How do I do it with mongodb realm?
What I have tried so far
const obj = realm.objects('Shop').filtered("_id == $0 AND products._id == $1", ObjectId('60f73ca7c1a70278596cc7d0'), ObjectId('2'))
realm.write(() => {
realm.delete(obj)
})
But this doesn't delete the item in the products array.
How can I achieve deleting a specific element in products array in this One to Many relationshiop using realm?
The code in the question is very close to being correct, you just need to filter for the product you want to delete instead of the shop. It's not clear if you know the product _id or name but you can filter by either one.
Here's the code to filter for products with an _id of 1 and then delete it (which will also remove it from any lists that contain a reference to it.
const prod = realm.objects('Products').filtered("_id == 1");
realm.write(() => {
realm.delete(prod);
prod == null;
})
The above is taken from the documentation Filter Query and Delete An Object
Keep in mind this will delete all products with id = 1 so as long as _id's are unique it's fine.

Add a filter to GTM custom javascript variable array

We have a ecommerce system that pushes through a datalayer. Unfortunately, the datalayer includes the 'building blocks' of our products. Resulting in tags that register more information than I would like. I would like to exclude these 'building blocks' through a custom javascript variable. See an example of the datalayer below.
checkout: {
actionField: {step: 1},
currencyCode: 'EUR',
products: [
{
name: 'product name',
id: '40291',
price: '149,00',
category: 'Transportation/Other',
quantity: 2
},
{
name: 'building block 1',
id: '20231',
price: '0,00',
category: 'Transportation/Other',
quantity: 2
},
{
name: 'building block 2',
id: '12302',
price: '0,00',
category: 'Transportation/Other',
quantity: 2
I've made a CJS variables that makes the products.id, .name, .price and .quantity an array (see below), but I am unable to figure out how to add a filter() that excludes from the array the values where corresponding product.name = "building block 1" && "building block 2".
var products = {{dlv - product}};
return products.reduce(function(arr, prod) { return arr.concat(prod.id); }, []);
Would anybody be able to help me out with this part of the function? Alternatively, I was thinking I might be able to achieve the same with a conditional if() statement.
Thanks in advance!
I'm not sure how GTM works, but if you want to filter an array with JS you could use something like this:
checkout.products.filter(product => !product.name.includes('building block'))
Let me know if this helps or I'm missing something.
I had the same challenge. My solution:
1. Create data layer variable with yours products array
In my case it was ecommerce.impressions data layer variable from productImpression event.
2. Choose parameters for your filter
I had:
variable with products array from step 1 - {{products array}}
term - only products with this word will be in my filtered array
custom dimension - 'dimensionXX' - this parameter will be check
3. Create custom javascript variable with filtered products array
Paste this function with yours filter parameters
function() {
var items = {{products array}};
var filtered_items = items.filter(function(filter) {
return filter.dimensionXX.includes('term');
}
);
return filtered_items;
}
4. Add your custom javascript variable from step 3 to your tag configuration
Replace your original product array with this custom javascript variable to send filtered product array to GA.

Azure cosmosDB default stored procedure did not work

When I created the stored procedure, this is the default sample code provided by Azure. My container(Students) is very simple and has only 2 items. The partition key is id.
{name: "aaa", id: "1"}
{name: "bbb", id: "2"}
Here is the sample stored procedure code provided by Azure CosmosDB.
// SAMPLE STORED PROCEDURE
function sample(prefix) {
var collection = getContext().getCollection();
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM root r',
function (err, feed, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found', 
// else take 1st element from feed
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
var response = getContext().getResponse();
var body = { prefix: prefix, feed: feed[0] };
response.setBody(JSON.stringify(body));
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
Why it always return "no docs found"? I have tried different sql queries like "select * from Students" or "select * from root" or "select * from c" but none of them work.
When you want to execute stored procedure, you need to pass value of partition key. In your case, you need to pass "1" or "2" as value of partition key(not "id") like this:

mongoose find() sorting and organizing returned results from a product database in js

I have a problem with organizing my mongoDB data to send to my page in my res and cant figure out how to do the correct js. Here is a simplified version of my schema
var productSchema = new mongoose.Schema({
medium: String,
brand: String,
group: String
});
Here is what a typical entry looks like
medium :"Acrylic",
brand :"liquitex",
group :"heavy body"
there are many more entries in the schema, but these are the only ones I need to be able to sort and organize the returned results with. The problem is I have a route that returns all colors in my database and I want to be able to display them in sections on my page that are grouped under Brand, and then has the individual colors listed under the correct group.
The problem is there are paints from other brands that fall into the heavy body group and so when I use a filter function to sort my data by group, some brands get mixed together. I cant filter by brand, because some brands have acrylic and watercolor so then those get lumped together.
I need some way to filter the returned results of a
mongoose.find({})
that can use the group data as a filter, but then filter those results by the brands so they get separated into the correct brand categories.
I have this so far:
this is all a stripped down version of my app.js file:
//finds all colors in the DB
Color.find({}).lean().exec(function( err, colors)
var groups = [];
// find all groups in the databse
colors.forEach( function(color){
groups.push(color["group"]);
});
//returns only unique names to filter out duplicates
var groupTypes = Array.from(new Set(groups));
var tempVariableBrands = [];
// this sorts all returned paints into their respective group, but we get paints from multiple brands under the same group and that is not good
groupTypes.forEach( function(group){
var name = group;
var result = colors.filter(obj => { return obj.group === group });
tempVariable.push( {group : name, result } );
});
// the tempVariable gets sent to my page like so
res.render("landing", {colorEntry:tempVariable} );
and this works fine to allow me to display each paint by its grouping, but that fails when there is more than one paint from a different manufacturer that is considered the same group like a "heavy body". This is my ejs on my page that works fine:
<% colorEntry.forEach( function(entry){ %>
<div class="brandBlock">
<div class="brandTitle">
<span><%=entry.result[0].brand%> - <%=entry.result[0].group%></span>
I for the life of me cant seem to figure out the combination of filter() and maybe map() that would allow this kind of processing to be done.
My database has like 600 documents, colors from a number of different manufacturers and I don't know how to get this as a returned structure: lets say this is a few colors in the DB that get returned from a mongoose find:
[{ medium: "Oil",
brand: "Gamblin",
group: "Artists oil colors"},
{ medium: "Acrylic",
brand: "Liquitex",
group: "Heavy Body"},
{ medium: "Acrylic",
brand: "Golden",
group: "Heavy Body"}
]
i need to organize it like this or something similar. It can be anything that just sorts this data into a basic structure like this, I am not confined to any set standard or anything, this is just for personal use and a site I am trying to build to learn more.
returnedColors = [ { brand: "Gamblin", group: "Artists oil colors", { 50 paints colors returned} },
{ brand: "liquitex" , group: "heavy body", { 20 paint colors returned } },
{ brand: "golden" , group: "heavy body",{ 60 paint colors returned} }
];
I am not a web developer and only write some web code every 6 months or so and have been trying how to figure this out for the last 2 days. I can't wrap my head around some of the awesome filter and map combo's i have seen and cant get this to work.
Any help or advice would be great. I am sure there are many areas for improvement in this code, but everything was working up until I entered paints that were from different brands that had the same group type and i had to try to rewrite this sorting code to deal with it.
It boils down to needing to be able to iterate over the entire set of returned documents from the DB and then sort them based off 2 values.
UPDATE:
I was able to get something that works and returns the data in the format that I need to be able to send it to my ejs file and display it properly. The code is rather ugly and probably very redundant, but it technically works. It starts off by using the group value to run over paints since each set of paints will have a group name, but can sometimes share a group name with a paint from another brand like "heavy body".
groupTypes.forEach( function(group){
var name = group;
var result = colors.filter(obj => { return obj.group === group });
// this gets brand names per iteration of this loop so that we will know if more than one brand of paint
// has the same group identity.
var brands = [];
result.forEach( function(color){
brands.push(color["brand"]);
});
// This filters the brand names down to a unique list of brands
var brandNames = Array.from(new Set(brands));
// if there is more than one brand, we need to filter this into two separate groups
if( brandNames.length > 1){
//console.log("You have duplicates");
brandNames.forEach( x => {
var tmpResult = [...result];
var resultTmp = result.filter(obj => { return obj.brand === x });
result = resultTmp;
//console.log("FILTERED RESULT IS: ", result);
tempVariable.push( {brand: x ,group : name, result } );
result = [...tmpResult];
});
}else{
tempVariable.push( {brand: result[0].brand ,group : name, result } );
}
});
if anyone can reduce this to something more efficient, I would love to see the "better" way or "right" way of doing something like this.
UPDATE2
Thanks to the answer below, I was put on the right track and was able to rewrite a bunch of that long code with this:
Color.aggregate([
{
$sort: { name: 1}
},
{
$group: {
_id: { brand: '$brand', group: '$group' },
result: { $push: '$$ROOT' }
}
},
{ $sort: { '_id.brand': 1 } }
], function( err, colors){
if(err){
console.log(err);
}else{
res.render("landing", {colorEntry:colors, isSearch:1, codes: userCodes, currentUser: req.user, ads: vs.randomAds()} );
}
});
Much cleaner and appears to achieve the same result.
Since you're using MongoDB, "right" way is to utilize an Aggregation framework, precisely, $group stage.
Product.aggregate([{
$group: {
_id: { group: '$group', brand: '$brand' },
products: { $push: '$$ROOT' }
}
}])
This will output array of objects containing every combination of brand and group, and push all relevant products to corresponding subarray.
Combine it with $project and $sort stages to shape your data further.

Categories

Resources