Related
I have a wordpress/woocommerce website which uses the GTM4WP plugin to send the website data to the GTM datalayer. In GTM I have successfully setup a purchase event which sends all data to the GA4 interface. So far so good.
Now there are two items (ID 224112 and 159324) on my website for which I do NOT want to see the revenue in my GA4 account. (The fact that they trigger a purchase event is not so much the problem but the revenue value should be ZERO.)
I have been searching for hours and found out I can do a custom Javascript function which should do the trick. Except I'm not very skilled with Javascript...
This is the instruction I found:
//This field should be a JavaScript function that returns a value using the 'return' statement.
//If the function does not explicitly return a value, it will return undefined and your container may not behave as expected.
//Below is an example of this field:
function() {
var now = new Date();
return now.getTime();
}
The value of both of these items can be different, I also run a multicurrency website, so the only thing we can go by is the ID.
When I do a test-purchase through the GTM preview mode I can see the following values in the data layer of the purchase event (I XXXed out a few things which are not needed here):
{
event: "purchase",
gtm: {uniqueEventId: 11, start: 1654376017109},
visitorLoginState: "logged-in",
visitorType: "XXX",
pagePostType: "page",
pagePostType2: "single-page",
browserName: "Chrome",
browserVersion: "102.0.5005.61",
browserEngineName: "Blink",
browserEngineVersion: "",
deviceType: "desktop",
deviceManufacturer: "Apple",
deviceModel: "Macintosh",
cartContent: {
totals: {
applied_coupons: [],
discount_total: 0,
subtotal: 0,
total: 0
},
items: []
},
orderData: {
attributes: {
date: "2022-06-04T22:53:18+00:00",
order_number: "226813",
order_key: "wc_order_Z6HrzhCqQ3pTS",
payment_method: "pensopay",
payment_method_title: "Credit/Debit card",
shipping_method: "3-5 days",
status: "processing",
coupons: ""
},
totals: {
currency: "DKK",
discount_total: "0",
discount_tax: "0",
shipping_total: "40",
shipping_tax: "0",
cart_tax: "0",
total: "90.00",
total_tax: "0",
total_discount: "0",
subtotal: "50",
tax_totals: []
},
customer: {
id: 3,
billing: {
first_name: "XXX",
last_name: "XXX",
company: "",
address_1: "XXX",
address_2: "",
city: "XXX",
state: "",
postcode: "XXX",
country: "DK",
email: "XXX",
emailhash: "dafb4416fe8aa737d5b09733b4d3310f6ca31276bebe6dd24" +
"bcbe1651fe4abde",
phone: "XXX"
},
shipping: {
first_name: "XXX",
last_name: "XXX",
company: "",
address_1: "XXX",
address_2: "",
city: "XXX",
state: "",
postcode: "XXX",
country: "DK"
}
},
items: [
{
id: 224112,
name: "Materials",
sku: 224112,
category: "XXX",
price: 50,
stocklevel: null,
brand: "XXX",
quantity: 1
}
]
},
new_customer: false,
ecommerce: {
currencyCode: "DKK",
purchase: {
actionField: {
id: "226813",
affiliation: "",
revenue: 90,
tax: 0,
shipping: 40,
coupon: ""
},
products: [
{
id: 224112,
name: "Materials",
sku: 224112,
category: "XXX",
price: 50,
stocklevel: null,
brand: "XXX",
quantity: 1
}
]
},
currency: "DKK",
items: [
{
item_id: 224112,
item_name: "Materials",
item_brand: "XXX",
price: 50,
item_category: "XXX",
quantity: 1,
google_business_vertical: "retail",
id: "224112"
}
],
transaction_id: "226813",
affiliation: "",
value: 90,
tax: 0,
shipping: 40,
coupon: ""
}
}
Well, it would be best to first not allow things that you don't want to have in DL. Since you just use plugins, then I would suggest to either make a change in the plugin logic to ignore specified products, or change the data. Just set the price for those products to the appropriate values.
Another approach to it would be through GA reporting. Just exclude the product ids you're not interested in from the reports.
I suggest these approaches because this is not a GTM issue really. Fixing this through GTM would be a hack and we try to avoid hacks in production.
Now that we went through that, yes, there are a few ways to fix it through GTM. An obvious one would be just to add a CJS blocker to your EEC trigger that would check if these products are in the ecommerce object and block the thing from firing if they're there. But this means blocking it for any other products that may be tracked in the same call.
You can also rebuild the whole ecommerce object with a CJS variable, the detailed explanation can be found here: https://www.simoahava.com/analytics/enhanced-ecommerce-with-a-custom-javascript-variable/ in case you're using Universal Analytics.
There's a similar solution for GA4. A simpler one. In GA4, you override your DL EEC push by just setting an event parameter for your EEC tag that sets the items property. You would use a CJS as a value. In your CJS you should return a proper array of items by taking what's in the DL and sanitizing it.
I have two simple models defined in Mongoose, composed of two schema Client and City, I have the property city defined in Client as a ObjectId, ref: 'City', so far so good.
If I query for a client and want also to filter by the 'province' property of City, I do it like this:
const client = await Client
.find({ name: "Gérard" })
.populate([{
path: 'city',
model: City,
match: { province: 'BA' }
}]);
And the output is just fine:
{
"id": "627264e3ec261a883d42ead9",
"name": "Gérard",
"email": "gerard#depardieu.fr",
"date": "1948-12-27",
"active": true,
"city": {
"id": "627264e3ec261a883d42ead1",
"name": "Buenos Aires",
"province": "BA"
}
}
Howerver, if I input a province code of a nonexistent city:
const client = await Client
.find({ name: "Gérard" })
.populate([{
path: 'city',
model: City,
match: { province: 'CA' }
}]);
It returns me this:
{
"id": "627264e3ec261a883d42ead9",
"name": "Gérard",
"email": "gerard#depardieu.fr",
"date": "1948-12-27",
"active": true,
"city": null
}
I don't want in this particular scenario, any instance of Client to be returned, and I don't know how to avoid this behavior with Mongoose, a behavior I never had to worry about with Spring Data for instance.
Somebody here told me to do something like this in my code:
const client = await Client
.find({ name: "Gérard" })
.populate([{
path: 'city',
model: City,
match: { province: 'CA' }
}]);
if(client && client.city){
return client; //or pass it to the response... whatever...
}
However, lets suppose this query is done over a collection with millions of clients, the code above is such a waste of resources that I won't even start the on what's wrong with it(ok, you can imagine how many clients not in province 'CA' the database server will send over to my app server just to be turned down....).
What I want is for no client to be returned at all, if that city/province doesn't match.
An example would be the analog SQL:
SELECT * FROM CLIENT JOIN CITY ON CITY.ID=CLIENT.CITY_ID WHERE CITY.PROVINCE = 'CA'
No client returned here, once the only client in database has its province = 'BA'.
So, how can it be done?
Thanks in advance.
I solved it myself, I had to go a little lower level with Mongoose and use aggregates and lookups.
const client = await Client.aggregate([
{
$match: { name: "Gérard" }
},
{
$lookup: {
from: City.collection.name,
pipeline: [
{
$match: {
province: 'BA'
}
}
], as: "city"
}
},
{
$unwind: "$city"
},
{
$match: {
city: { $ne: [] }
}
}
]);
Expected result:
{
"id": "627264e3ec261a883d42ead9",
"name": "Gérard",
"email": "gerard#depardieu.fr",
"date": "1948-12-27",
"active": true,
"city": {
"id": "627264e3ec261a883d42ead1",
"name": "Buenos Aires",
"province": "BA"
}
}
Witch is ok, client name "Gérard" lives in "Buenos Aires", situated in province "BA".
On the other hand:
const client = await Client.aggregate([
{
$match: { name: "Gérard" }
},
{
$lookup: {
from: City.collection.name,
pipeline: [
{
$match: {
province: 'CA'
}
}
], as: "city"
}
},
{
$unwind: "$city"
},
{
$match: {
city: { $ne: [] }
}
}
]);
Returns nothing, once the city of "Buenos Aires" is not located in province "CA".
Notice here the last parameter (as an object) the array passed to Client.aggregate() receives:
{
$match: {
city: { $ne: [] }
}
}
This tells MongoDB that in order to return data city must be not equal to an empty array.
And with that the issue is solved.
I have a JSON data with something like this,
{
applylink: "https://www.techgig.com/jobs/Senior-Knowledge-Analyst-CKA/59843",
companyname: "Boston Consultancy Group",
created: "",
enddate: "",
experience: "4-6 yrs",
jd: "",
location: "Bengaluru/Bangalore",
salary: "",
skills: "cassandra",
source: "techgig",
startdate: "",
timestamp: 1528959791.958316,
title: "Senior Knowledge Analyst CKA",
type: ""
}
I want to query it with multiple key values like
var query = {
skills: [],
location: ["Bengaluru/Bangalore","Pune"],
experience: ["4-6 yrs"]
};
This is the function which I sought to write, there were a few ans regarding this, but I was unable to understand them,
function find_in_object(my_array, my_criteria) {
}
How should I write this function so that, I can implement this feature
Thanks
Edit 1:
Able to make something like this:
function find_in_object(my_array, my_criteria) {
var x = my_array.map((data)=>{
var filter=[];
var keys = Object.keys(query);
keys.map((imf)=>{
if(query.imf.indexOf(data.imf)){
}
})
})
A straight-forward and performant (in most cases) approach is to iterate the entries of the query, looking at each corresponding prop in the objects being searched. Fail aggressively if any aspect of the query doesn't match.
There's no getting around iterating all of the input data to test for match, nor can we avoid iterating all props in the query until a mismatch. One place to improve speed is in array.includes() (by better indexing those arrays), but I'd test this for acceptable performance first.
let data = [{
applylink: "https://www.techgig.com/jobs/Senior-Knowledge-Analyst-CKA/59843",
companyname: "Boston Consultancy Group",
created: "",
enddate: "",
experience: "4-6 yrs",
jd: "",
location: "Bengaluru/Bangalore",
salary: "",
skills: "cassandra",
source: "techgig",
startdate: "",
timestamp: 1528959791.958316,
title: "Senior Knowledge Analyst CKA",
type: ""
},
{
applylink: "some other link",
companyname: "Acme Inc",
created: "",
enddate: "",
experience: "6-8 yrs",
jd: "",
location: "Pittsburg",
salary: "",
skills: "javascript",
source: "techgig",
startdate: "",
timestamp: 1528959791.958316,
title: "Senior Knowledge Analyst CKA",
type: ""
}
]
// matches the first job
let query1 = {
location: ["Bengaluru/Bangalore", "Pune"],
experience: ["4-6 yrs"]
};
// matches the second job
let query2 = {
location: ["London", "Pittsburg"],
experience: ["6-8 yrs"]
};
// matches neither
let query3 = {
location: ["Bengaluru/Bangalore", "Pune"],
experience: ["6-8 yrs"]
};
function dataMatchingQuery(data, query) {
return data.filter(datum => datumMatchesQuery(datum, query))
}
// true if the datum's values can be found in the query arrays for every prop
function datumMatchesQuery(datum, query) {
for (let [k, v] of Object.entries(query)) {
if (!v.includes(datum[k])) return false
}
return true
}
console.log(dataMatchingQuery(data, query1))
console.log(dataMatchingQuery(data, query2))
console.log(dataMatchingQuery(data, query3))
I'm trying to loop through a JSON object using javascript. The reason that it is an object and not an array is because the same data is accessed using XAMARIN and C#'s NewtonJSON works better with just JSON Objects instead a mix of objects and arrays.
Below is the JSON object that I am trying to work with, I've cut bits out so you can see the main part of the object that I'm having issues with.
CODE: NULL
VALUE:{
USER24:{id: "24", business: "25", username: "test1", firstName: "test1", lastName: "test1", level: "0",…}
USER25:{id: "25", business: "25", username: "test2", firstName: "test2", lastName: "test2", level: "0",…}
USER26:{id: "26", business: "25", username: "test3", firstName: "test3", lastName: "test3", level: "0",…}
USER27:{id: "27", business: "25", username: "test4", firstName: "test4", lastName: "test4",…}
USER28:{id: "28", business: "25", username: "test5", firstName: "test5", lastName: "test5",…}
USER29:{id: "29", business: "25", username: "test6", firstName: "test6", lastName: "test6", level: "0",…}
USER30:{id: "30", business: "25", username: "test7", firstName: "test7", lastName: "test7", level: "0",…}
}
EXTRA: NULL
The issue I'm having is when I loop through data.VALUE I get the keys and values of USER24 instead of the keys and values of data.VALUE. When I try and loop through just data I get the keys CODE, VALUE, EXTRA like I would expect.
No matter what I place in the loop I won't get the keys and values USER24, USER25, USER26, ... which is what I want. Below I have snippet of the Javascript loop I'm using:
for(var key in data.VALUE){
if(data.VALUE.hasOwnProperty(key)){
console.log('key:'+key+', val:'+data.VALUE[key]);
}
}
All I get back from this is:
key:id, val:24
key:business, val:25
key:username, val:test1
key:firstName, val:test1
key:lastName, val:test1
key:level, val:0
key:email, val:test1#test.co.uk
key:phone, val:null
key:isAdmin, val:true
Which is completely wrong!
I'm not sure what else I am to do for this, I've tried using the $.each from JQuery, I've not used the new let[key, value] as it's far too new at the moment to rely on.
If you need anymore information or code snippets feel free to ask! Thanks in advanced for any help!
Here is a picture of the JSON in the preview tab in chrome (minus some sensitive data):
You can done it by $.each from JQuery like this
$.each(data.VALUE, function (i, key) {
$.each(key, function (j,k) {
console.log('key:'+j+', val:'+k);
});
});
If you want to access it from javascript You are get each key in data.VALUE.USER24 object.
for (var key in data.VALUE.USER24) {
if(data.VALUE.USER24.hasOwnProperty(key)){
console.log('key:' + key + ', val:' + data.VALUE.USER24[key]);
}
}
I'm using the mongodb driver for node.js to query my MongoDB document store.
I have the following data in a document store named companies:
{
companyName: "My Company",
users: [
{
first: "Nick",
last: "Kewney",
email: test#user.com,
username: "this.username",
company: "Test Company",
}
],
_id: ObjectId("54a0831fcad79dbf082d65e0")
}
I want to query the store and find any users with the username 'this.username' and return the first.
My attempt below returns the single result but as an array.
db.companies.findOne({ "users.username": "this.username" }, {_id: 0, 'users.$': 1}, next);
Returns...
{
"users": [
{
"first": "Nick",
"last": "Kewney",
"email": "test#test.com",
"username": "this.username",
"company": "Test Company"
}
]
}
My desired result is only the first item, e.g.
{
"first": "Nick",
"last": "Kewney",
"email": "test#test.com",
"username": "this.username",
"company": "Test Company"
}
There are limitations to what can be done with the basic projection operations available to the .find() method of MongoDB. ,findOne() essentially just wraps .find() to return a single document and not a cursor. But these are basically the same.
If you want first manipulation in the server returned result, you need the .aggregate() method. This has a more detailed $project stage than can do further manipulation, along with other stage operators to get the result:
db.companies.aggregate(
[
// Query argument to match document
{ "$match": {
"users.username": "this.username"
}},
// Flatten array out
{ "$unwind": "$users" },
// Filter array
{ "$match": {
"users.username": "this.username"
}},
// Project wanted fields
{ "$project": {
"_id": 0,
"first": "$users.first",
"last": "$users.last",
"username": "$users.username",
"email": "$users.email",
"company": "$users.company"
}}
],
function(err,result) {
}
);
That will give you the result with a restructured document, and also return possible matches for multiple array items as separate results if you needed to.
But for my money, generally just live with what you get from the basic projection. Sure it's nested and also still an array with only 1 item in it, but this is easy to code to manipulate your response. Especially if you truly are using .findOne() in your intended code, as one result is not hard to manipulate there.