I have an object, and I want to set one of the fields to the return of querying the database using the value of another field in the object. The approach below returns null, as Im sure it's not right, but I can't figure out the best way to do this.
shared_user_email should be blank if shared_user is empty, but it should represent the return of a database query if shared_user is populated.
I know that javascript variables are hoisted to the top of the application at runtime, but I seem to be falling victim to order of operations here.
var newList = {
title: $(ev.target).find('[name=title]').val(),
description: $(ev.target).find('[name=description]').val(),
dateCreated: today.toDateString(),
owner: Meteor.userId(),
owner_email: Meteor.user().emails[0].address,
shared_user: $(ev.target).find('[name=shared_user]').val(),
shared_user_email: shared_user_email_field
}
var shared_user_email_field = (newList.shared_user != '') ? Meteor.users.find({_id: $(ev.target).find('[name=shared_user]')
.val()}).fetch()[0].emails[0].address : '';
EDIT
here is the server side method that is invoked right after the object is created, which passes newList to the server:
Meteor.call('addList', newList, function(err, list){
console.log(shared_user_email_field);
return list;
});
var newList = {
title: $(ev.target).find('[name=title]').val(),
description: $(ev.target).find('[name=description]').val(),
dateCreated: today.toDateString(),
owner: Meteor.userId(),
owner_email: Meteor.user().emails[0].address,
shared_user: $(ev.target).find('[name=shared_user]').val(),
shared_user_email: ''
}
newList.shared_user_email = (newList.shared_user != '') ? Meteor.users.find({_id: $(ev.target).find('[name=shared_user]')
.val()}).fetch()[0].emails[0].address : '';
Related
I am building a little shop for a client and storing the information as an array of objects. But I want to ensure that I am not creating "duplicate" objects. I have seen similar solutions, but perhaps it is my "newness" to coding preventing me from getting the gist of them to implement in my own code, so I'd like some advice specific to what I have done.
I have tried putting my code in an if look, and if no "part", my variable looking for part number, exists in the code, then add the part, and could not get it to function.
Here is the function I am working on:
function submitButton(something) {
window.scroll(0, 0);
cartData = ($(this).attr("data").split(','));
arrObj.push({
part: cartData[0],
description: cartData[1]
});
}
arrObj is defined as a global variable, and is what I am working with here, with a "part" and a "description", which is the data I am trying to save from elsewhere and output to my "#cart". I have that part working, I just want to ensure that the user cannot add the same item twice. (or more times.)
Sorry if my code is shoddy or I look ignorant; I am currently a student trying to figure these things out so most of JS and Jquery is completely new to me. Thank you.
You can create a proxy and use Map to hold and access values, something like this
let cart = new Map([{ id: 1, title: "Dog toy" }, { id: 2, title: "Best of Stackoverflow 2018" }].map(v=>[v.id,v]));
let handler = {
set: function(target,prop, value, reciver){
if(target.has(+prop)){
console.log('already available')
} else{
target.set(prop,value)
}
},
get: function(target,prop){
return target.get(prop)
}
}
let proxied = new Proxy(cart, handler)
proxied['1'] = {id:1,title:'Dog toy'}
proxied['3'] = {id:3,title:'Dog toy new value'}
console.log(proxied['3'])
Assuming the 'part' property is unique on every cartData, I did checking only based on it.
function submitButton(something) {
window.scroll(0, 0);
cartData = ($(this).attr("data").split(','));
if(!isDuplicate(cartData))
arrObj.push({
part: cartData[0],
description: cartData[1]
});
}
const isDuplicate = (arr) => {
for(obj of arrObj){
if(arr[0] === obj.part)
return true;
}
return false;
}
If you want to do the checking on both 'part' and 'description' properties, you may replace the if statement with if(arr[0] === obj.part && arr[1] === obj.description).
Thanks everyone for their suggestions. Using this and help from a friend, this is the solution that worked:
function submitButton(something) {
window.scroll(0,0);
cartData = ($(this).attr("data").split(','));
let cartObj = {
part: cartData[0],
description: cartData[1],
quantity: 1
}
match = false
arrObj.forEach(function(cartObject){
if (cartObject.part == cartData[0]) {
match = true;
}
})
console.log(arrObj);
if (!match) {
arrObj.push(cartObj);
}
Okay, you have multiple possible approaches to this. All of them need you to specify some kind of identifier on the items which the user can add. Usually, this is just an ID integer.
So, if you have that integer you can do the following check to make sure it's not in the array of objects:
let cart = [{ id: 1, title: "Dog toy" }, { id: 2, title: "Best of Stackoverflow 2018" }];
function isInCart(id) {
return cart.some(obj => obj.id === id);
}
console.log(isInCart(1));
console.log(isInCart(3));
Another approach is saving the items by their id in an object:
let cart = { 1: { title: "Dog toy" }, 2: { title: "Best of Stackoverflow 2018" } };
function isInCart(id) {
if(cart[id]) return true;
return false;
}
Try to use indexOf to check if the object exists, for example:
var beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];
console.log(beasts.indexOf('aaa'));
// expected output: -1
New info at EDIT 3
So far I've had no issues with timestamps as I've added them on the client side with simple System.currentTimeMillis().
Now I am moving some of my functions to the cloud, and it seems like I can't generate a timestamp in a simple milliseconds Long like before.
Searching how to set it I found out that the method is using admin.firestore.FieldValue.serverTimestamp() but I am facing an issue.
Some of my objects get time stamped when the user creates them. For example, if I need to compare it to SO, when a user uploads a new question then I place the timestamp of creation and also the timestamp of last interaction as one of the fields in the question object and then save it to the server.
My problem starts when I try to edit this object from the cloud. If for example someone posts an answer to this question, I want to edit the timestamp of the last interaction and update it to the current time. But the time stamp generated by the server is in a completely different format (Map<String, String>) than the one I generated on the client side.
What's the right way to handle this? Is there a way to convert this admin.firestore.FieldValue.serverTimestamp() to a a Long before saving it to the object?
Or should I somehow generate on the client side a timestamp that fits the one generated by the server? For my understanding this isn't even possible as this timestamp is just a place holder?
Should I maybe upload the question with an empty Map<String, String> and have a function triggered when a new question is created, that edits the timestamp to the correct one?
Would love to know what the right approach would be as timestamps are something I'd like to have consistent as I move forward and don't want to change later.
For example, this is my Question object:
#Parcelize
class Question(
val id: String,
val title: String,
val details: String,
val timestamp: Long,
val author_ID: String,
val author_name: String,
val author_image: String,
val last_interaction: Long,
) : Parcelable {
constructor() : this("", "", "", 0, "", "", "", 0)
}
And I would post a question like this:
val questionRef = db.collection("questions").document()
questionRef.set(
Question(questionRef.id,
"My title",
"My details",
System.currentTimeMillis(),
"My author Id",
"My author name",
"My author image",
System.currentTimeMillis())
)
But now when I want to edit this object from the cloud and update the last_interaction field, how can I set it in Long?
For my understanding, I can convert whatever the timestamp is into the milliseconds format when reading the data, but the problem I am facing is how to save the data in a consistent way.
EDIT:
This is the function I am trying to use at the cloud.
exports.updateLastInteraction = functions.firestore.document('communities_data/{community}/answers/{answerID}').onCreate((snap, context) => {
const newAnswer = snap.data();
if (newAnswer !== undefined) {
const promises = [];
//I got a few more promises here
promises.push(db.doc('communities_data/' + context.params.community + '/questions/' + newAnswer.question_ID).update({ last_interaction: admin.firestore.FieldValue.serverTimestamp() })
);
return Promise.all(promises);
} else {
return null;
}
});
So at first the timestamp was set with just a simple Long, but now it been updated to be whatever format firebase sets it to be which is different.
So I need to check what format it is in my code every time to handle it properly?
Edit 2: Maybe I got this. So this format firebase saves to is a date format? So if from my code I'll convert the milliseconds to a date before saving, I should be alright and consistent? Trying to check that now.
Edit 3: Ok, this took me some time because I had to reset all the database and try to start all over. The first operation in which the app breaks is caused because of the user object I'm creating, which contains a timestamp.
This is my user class:
class UserObject {
uid: string;
first_name: string;
last_name: string;
communities_list: Array<string>;
lang_list: Array<string>;
reputation: Number;
join_date: FieldValue;
last_activity: FieldValue;
constructor(uid: string, first_name: string, last_name: string, communities_list: [], lang_list: ["en"], reputation: Number, join_date: FieldValue, last_activity: FieldValue) {
this.uid = uid;
this.first_name = first_name;
this.last_name = last_name;
this.communities_list = communities_list;
this.lang_list = lang_list;
this.reputation = reputation;
this.join_date = join_date;
this.last_activity = last_activity;
}
}
Then I have this cloud function that should create a new user whenever a new user is authenticated. This is the function:
exports.userCreated = functions.auth.user().onCreate((user) => {
const currentTime = admin.firestore.FieldValue.serverTimestamp();
const newUser = new UserObject(user.uid, "Friend", "", [], ["en"], 0, currentTime, currentTime);
return db.doc('users/' + user.uid).set(JSON.parse(JSON.stringify(newUser)));
});
The functions seemed to work, kinda. The fields of join_date and last_activity seem to be empty.
But, the real problem is that the app crashes when opening because it can't convert type HashMap to Date.
This is the class I use in my app that I try to convert the data into:
#Parcelize
class User(
val uid: String,
val first_name: String,
val last_name: String,
val communities_list: MutableList<String>,
val lang_list: MutableList<String>,
val reputation: Long,
val join_date: Date,
val last_activity: Date
) : Parcelable {
constructor() : this("","", "", mutableListOf(), mutableListOf(), 0, Date(), Date())
}
After I saw this error, I tried to change the type for joind_date and last_activity from Date to HashMap thinking that maybe the date is the value of the hashMap?
I did that and tried to print the value of those fields, and while the app didn't crash, it indeed returned an empty value:
2019-07-30 16:10:55.252 24401-24401/com.myapp D/what is this timestamp: {}
So I am still very confused, how do I get the timestamp?
I would like to set a value into a list field in NetSuite, but I am finding that I receive an error if that value does not exist.
How can I search for all values to check if the value I am trying to place is good or not? I would also be open to this just failing silently without showing an error and instead just not populating the value.
ERROR_CODE:INVALID_KEY_OR_REF MESSAGE:Invalid custentity_esc_industry
reference key Wholesale. STATUS_TYPE: ERROR
try {
//var rec = scriptContext.newRecord;
var integrationFieldValue = scriptContext.newRecord.getValue(integrationFieldName);
if(integrationFieldValue != null && integrationFieldValue != ''){
scriptContext.newRecord.setText({
fieldId: actualFieldName,
text: integrationFieldValue
});
}
} catch(e){
log.error({
title: "setNonIntegrationFieldValue() has encountered an error.",
details: e.message
});
}
You may try the getSelectOptions in SuiteScript 2.0. It will return an array of the first 1000 available options.
For Example:
var objRecord = record.load({
type: record.Type.SALES_ORDER,
id: 275
});
var objSublist = objRecord.getSublist({
sublistId: 'item'
});
var options = objField.getSelectOptions({
filter : 'C',
operator : 'startswith'
});
For more details, you can search 'Field.getSelectOptions' in Netsuite Help center
I have pouchdb/couchbase data with equipment that has user assigned to them.
Equipment with _id and in the equipment doc there is a checkedOutBy with the user._id as the value. Within the employee object there is user.name. When I get the equipment objects how do I also get the user.name and display with the equipment.
I have searched and read about map/reduce that uses emit and do not grasp the idea. My code that i wrote from what i learned is:
by the way I am also using Angularjs.
field = "eq::"
this.getAllEquip = function(field){
function map(doc) {
if (doc.checkedOutBy !== undefined) {
emit(doc.checkedOutBy, {empName : doc.name});
}
}
var result = database.query(map, {include_docs: true,
attachments: true,
startkey: field,
endkey: field + '\uffff'})
.catch(function (err) {
//error stuff here
});
return result
};
I don't see where the two docs would get together. What am i missing? My result is empty.
The equipment json looks like:
{checkedOutBy: "us::10015", description: "3P Microsoft Surface w/stylus & power cord", equipId: "SUR1501", purchaseDate: "", rCost: 1000, id:"eq::10001"}
Emlpoyee json:
{"firstname":"Joe","gender":"male","lastname":"Blow","status":"active","title":"office","type":"userInfo","_id":"us::10015","_rev":"2-95e9f34784094104ad24bbf2894ae786"}
Thank you for your help.
Something like this should work, if I understood the question correctly:
//Sample Array of Objects with Equipment
var arr1=[{checkedout:"abc1",desc:"item1",id:1},
{checkedout:"abc2",desc:"item2",id:2},
{checkedout:"abc3",desc:"item3",id:3},
{checkedout:"abc1",desc:"item1",id:4},
{checkedout:"abc4",desc:"item3",id:5},
{checkedout:"abc6",desc:"item3",id:6}];
//Sample array of objects with Employee - the "id" in arr2 matches with "checkout" in arr1
var arr2=[{name:"john",id:"abc1"},
{name:"jack",id:"abc2"},
{name:"alice",id:"abc3"},
{name:"james",id:"abc4"}];
var result = []; //final result array
//loop through equipment array arr1
arr1.forEach(function(obj) {
var tempObj = obj;
var checkedout_id=obj.checkedout;
//do array.find which will return the first element in the array which satisfies the given function. This is absed on the assumption that that the id is unique for employee and there wont bwe multiple employees with same id (which is the "checkedout" field in equipment. If the employee is not found, it will return undefined.
var foundname = arr2.find(function(obj) {
if (obj.id == checkedout_id)
return obj.name
})
//Create the object to be inserted into the final array by adding a new key called "name", based on the result of above find function
if (foundname != undefined) {
tempObj.name=foundname.name
}
else {
tempObj.name = "Not found";
}
result.push(tempObj);
})
This is my Pouchdb solution, thank you Vijay for leading me to this solution.
First I get all my equipment. Then I use Vijay's idea to loop through the array and add the name to the object and build new array. I found there is a need to go into the .doc. part of the object as in obj.doc.checkedOutBy and tempObj.doc.name to get the job done.
$pouchDB.getAllDocs('eq::').then(function(udata){
var result = [];
//loop through equipment array
udata.rows.forEach(function(obj) {
var tempObj = obj;
var checkedout_id=obj.doc.checkedOutBy;
if (checkedout_id != undefined) {
$pouchDB.get(checkedout_id).then(function(emp){
return emp.firstname + " " + emp.lastname
}).then(function(name){
tempObj.doc.name = name;
});
}
result.push(tempObj);
})
in my service I have:
this.get = function(documentId) {
return database.get(documentId);
};
and:
this.getAllDocs = function(field){
return database.allDocs({
include_docs: true,
attachments: true,
startkey: field,
endkey: field + '\uffff'});
};
I'm looking into adding full text search to a Meteor app. I know MongoDB now supports this feature, but I have a few questions about the implementation:
What's the best way to enable the text search feature (textSearchEnabled=true) in a Meteor app?
Is there a way to add an index (db.collection.ensureIndex()) from within your app?
How can you run a Mongo command (i.e. db.quotes.runCommand( "text", { search: "TOMORROW" } )) from within a Meteor app?
Since my goal is to add search to Telescope, I'm searching for a "plug-and-play" implementation that requires minimal command line magic and could even work on Heroku or *.meteor.com.
The simplest way without editing any Meteor code is to use your own mongodb. Your mongodb.conf should look something like this (on Arch Linux it is found at /etc/mongodb.conf)
bind_ip = 127.0.0.1
quiet = true
dbpath = /var/lib/mongodb
logpath = /var/log/mongodb/mongod.log
logappend = true
setParameter = textSearchEnabled=true
The key line is setParameter = textSearchEnabled=true, which, as it states, enables text search.
Start mongod up
Tell meteor to use your mongod not its own by specifying the MONGO_URL environmental variable.
MONGO_URL="mongodb://localhost:27017/meteor" meteor
Now say you have collection called Dinosaurs declared say in collections/dinosaurs.js
Dinosaurs = new Meteor.Collection('dinosaurs');
To create an text index for the collection create a file server/indexes.js
Meteor.startUp(function () {
search_index_name = 'whatever_you_want_to_call_it_less_than_128_characters'
// Remove old indexes as you can only have one text index and if you add
// more fields to your index then you will need to recreate it.
Dinosaurs._dropIndex(search_index_name);
Dinosaurs._ensureIndex({
species: 'text',
favouriteFood: 'text'
}, {
name: search_index_name
});
});
Then you can expose the search through a Meteor.method, for example in the file server/lib/search_dinosaurs.js.
// Actual text search function
_searchDinosaurs = function (searchText) {
var Future = Npm.require('fibers/future');
var future = new Future();
Meteor._RemoteCollectionDriver.mongo.db.executeDbCommand({
text: 'dinosaurs',
search: searchText,
project: {
id: 1 // Only take the ids
}
}
, function(error, results) {
if (results && results.documents[0].ok === 1) {
future.ret(results.documents[0].results);
}
else {
future.ret('');
}
});
return future.wait();
};
// Helper that extracts the ids from the search results
searchDinosaurs = function (searchText) {
if (searchText && searchText !== '') {
var searchResults = _searchEnquiries(searchText);
var ids = [];
for (var i = 0; i < searchResults.length; i++) {
ids.push(searchResults[i].obj._id);
}
return ids;
}
};
Then you can publish only documents that have been searched for in 'server/publications.js'
Meteor.publish('dinosaurs', function(searchText) {
var doc = {};
var dinosaurIds = searchDinosaurs(searchText);
if (dinosaurIds) {
doc._id = {
$in: dinosaurIds
};
}
return Dinosaurs.find(doc);
});
And the client side subscription would look something like this in client/main.js
Meteor.subscribe('dinosaurs', Session.get('searchQuery'));
Props to Timo Brinkmann whose musiccrawler project was the source of most this knowledge.
To create a text index and try to add like this I hope so it will be useful if there is still problem comment
From docs.mongodb.org:
Append scalar index fields to a text index, as in the following
example which specifies an ascending index key on username:
db.collection.ensureIndex( { comments: "text",
username: 1 } )
Warning You cannot include multi-key index field or geospatial index
field.
Use the project option in the text to return only the fields in the
index, as in the following:
db.quotes.runCommand( "text", { search: "tomorrow",
project: { username: 1,
_id: 0
}
}
)
Note: By default, the _id field is included in the result set. Since the example index did not include the _id field, you must
explicitly exclude the field in the project document.