I want to change from WebSql to Indexeddb. However, how would one do SQL queries like
SELECT * FROM customers WHERE ssn = '444-44-4444' and emal = 'bill#bill#company.com'
SELECT * FROM customers WHERE ssn = '444-44-4444' and emal = 'bill#bill#company.com' and age = 30
SELECT * FROM customers WHERE ssn = '444-44-4444' and emal = 'bill#bill#company.com' and name = 'Bill'
etc
with IndexedDB ?
For example, I noticed while reading the documentation of indexedDb, that all the examples only query one index at the time. So you can do
var index = objectStore.index("ssn");
index.get("444-44-4444").onsuccess = function(event) {
alert("Name is " + event.target.result.name);
};
But I need to query multiple indexes at the same time!
I also found some interesting posts about compound indexes, but they only work if you query for all the fields in the compound index.
For your example, compound index still work, but requires two compound indexes
objectStore.createIndex('ssn, email, age', ['ssn', 'email', 'age']); // corrected
objectStore.createIndex('ssn, email, name', ['ssn', 'email', 'name'])
And query like this
keyRange = IDBKeyRange.bound(
['444-44-4444', 'bill#bill#company.com'],
['444-44-4444', 'bill#bill#company.com', ''])
objectStore.index('ssn, email, age').get(keyRange)
objectStore.index('ssn, email, age').get(['444-44-4444', 'bill#bill#company.com', 30])
objectStore.index('ssn, email, name').get(['444-44-4444', 'bill#bill#company.com', 'Bill'])
Indexes can be arranged in any order, but it is most efficient if most specific come first.
Alternatively, you can also use key joining. Key joining requires four (single) indexes. Four indexes take less storage space and more general. For example, the following query require another compound index
SELECT * FROM customers WHERE ssn = '444-44-4444' and name = 'Bill' and age = 30
Key joining still work for that query.
Related
I'm trying to save the information I get from my inmatefirst column with my SQL query into the variable "first'. I was wondering what the correct syntax/approach was to doing this.
let sql = `SELECT * FROM inmates WHERE inmatefirst = ? AND inmatelast = ? AND dob = ?`;
let query = db.query(sql, [search.first, search.last, search.dob], (err, result) => {
if (err) throw err;
if (result.length != 0) {
res.render(path.resolve('./myviews/modify'), {
good: 'Person Found - Please Enter Updated Information',
first: result.inmatefirst,
last: result.last,
dob: result.dob,
sex: result.sex
});
});
});
Your SQL says, in part, this:
SELECT * FROM inmates WHERE inma...
You've asked for every column from the table. That can make life confusing.
Instead, spell out the columns you want. Your Javascript indicates that you want these ones:
SELECT inmatefirst, last, dob, sex FROM inmates WHERE inma...
(It's possible that is the wrong set of columns. Check it.
Then, as #barmar pointed out, use result[0].first etc, because result is an array of objects, one for each row of your resultset.
That title might not make a lot of sense but please bear with me and I'll try to explain what I'm after.
I'm creating an Angular filter component that I can plug and play into various portions of my app. However, the question itself is more of a JavaScript question than an Angular one.
What I want to achieve is quite simple in theory but seems to be hard in practice (at least for me).
I want to give the user the ability to input an array of objects, and an array of object property names. I then want to filter the array of objects by either property 1 OR property 2.
Lets say we have the following setup:
inputtedObjects = [
{name: 'Bruce', gamerTag: 'BruceyBoi', email: 'b.banner#email.com'},
{name: 'Frank', gamerTag: 'BruceIsMyNemesis', email: 'f.francis#yahoo.com'},
{name: 'Steve', gamerTag: 'ScubaSteve', email: 'superstevo#live.com'}
];
filterProperties = ['name', 'gamerTag']
What I then want to do is essentially this operation:
let filteredObjects = inputtedObjects.filter(object =>
object[filterProperties[0]].toLowerCase().includes(searchTerm) ||
object[filterProperties[1]].toLowerCase().includes(searchTerm)
Where the searchTerm is a user inputted field from an input tag in HTML.
This would result in if the user was typing in "bruce" in the input, he would get the top two filtered results returned to him.
I have tried the following code:
let currentObjects = this.objects;
this.filterProperties.forEach(field => {
this.filteredObjects = currentObjects.filter(
object =>
object[field]
.toLowerCase()
.includes(searchTerm.toLowerCase())
);
});
However, the issue with the code above is that it filters as an AND and not an OR in the sense that it would work but if the user wrote "bruce" it would only return the first object as both of the properties must include "bruce" for the above code to work.
Now I can do this with some kind of switch case, as in if the filterProperties array is length 1 then we do:
let filteredObjects = inputtedObjects.filter(object =>
object[filterProperties[0]].toLowerCase().includes(searchTerm)
and if it's of length 2 we do:
let filteredObjects = inputtedObjects.filter(object =>
object[filterProperties[0]].toLowerCase().includes(searchTerm) ||
object[filterProperties[1]].toLowerCase().includes(searchTerm)
Etc.
Now obviously this is not very clean code, nor does it seem very efficient whatsoever. It's also not easy to scale and it would require some kind of error message if the user attempted to input too many "filterProperties" as it would depend on the amount of hardcoded switch case statements (bad code smell already).
What I would want to achieve then is for the user to be able to provide an array of infinite objects of a certain type with potentially hundreds of properties per object. Then the user says, I want to filter on these 6 property names, and then begins to type "test", it would then evaluate objects that match test on any one of these 6 properties provided. Not only objects that match test on all of these properties.
Any ideas on how I could potentially achieve this outcome?
You can use Array.some on the filterProperties array to see if one (or more) of the object properties contains the searchTerm:
inputtedObjects = [{
name: 'Bruce',
gamerTag: 'BruceyBoi',
email: 'b.banner#email.com'
},
{
name: 'Frank',
gamerTag: 'BruceIsMyNemesis',
email: 'f.francis#yahoo.com'
},
{
name: 'Steve',
gamerTag: 'ScubaSteve',
email: 'superstevo#live.com'
}
];
filterProperties = ['name', 'gamerTag'];
searchTerm = 'bruce';
filteredObjects = inputtedObjects.filter(object =>
filterProperties.some(p => object[p].toLowerCase().includes(searchTerm))
);
console.log(filteredObjects);
Try this, adds into a list when a match is found on any field instead of overwriting it.
let filtered = [];
inputtedObjects.forEach(obj => {
console.log(obj.name)
for (let field of filterProperties) {
if (obj[field].toLowerCase().includes(searchTerm.toLowerCase())) {
console.log(obj.name, field)
filtered.push(obj);
break;
}
}
});
https://jsfiddle.net/k1bj8zof/
I have a users collection and Companies collection. Right now the output looks like this:
"_id":"5ce543a5390d87567f523153",
"updatedAt":"2019-10-18T15:01:53.812Z",
"createdAt":"2019-05-22T12:42:13.046Z",
"associatedCompany":{
"_id":"5ce543a4390d87567f523148",
"companyName":"Company Two"},
"accountStatus":1,
"roleInfo":{"roleType":1},
"role":2,
"personalInfo":{
"fullName":"SomeName",
"firstName":"Name",
"lastName":"Last",
"email":"email#email.com",
"phone":{"countryCode":"SE","number":"9876543210"}}}
Where company field is populated from Company collection.
Is there a way to display just a company name right away with firstName and lastName field without "personalInfo" field so the output will look like:
{"data":[{
"_id":"5ce543a5390d87567f523153",
"updatedAt":"2019-10-18T15:01:53.812Z",
"createdAt":"2019-05-22T12:42:13.046Z",
"companyName":"Company Two",
"accountStatus":1,
"roleInfo":{"roleType":1},
"role":2,
"firstName":"Name",
"lastName":"Last",
"email":"email#email.com"
Query I am using
const listUsers = (skip, limit = 10) => {
let config = {
populate: {
'path': 'associatedCompany',
'select': 'companyName'
},
limit: Number(limit),
skip: Number(skip),
};
I have tried to do it with aggregation and lookup but without success.
Thanks is advance.
Use projection to filter the fields that you want to be displayed in your output.
Refer:
mongoDB projection
I have an application that displays services upon which I would like the user to be able to filter by service name while always being sorted (descending) by date (no sorting on service name).
Also the following question doesn't contain the appropriate solution:
Couch DB filter by key and sort by another field
Below is a simplistic version of the code to give an idea of what I am currently trying (serviceName must be first key as I cannot filter exclusively with the second key):
function(doc) {
if(doc.type && doc.type=="incident"){
emit([doc.serviceName, doc.date], doc)
}
};
Here is a Snippet where from my API writen in Node, that builds the parameters for the query:
if (req.query.search !== "") {
const search = req.query.search;
params = {
limit: limit,
skip: skip,
startkey: [search+"Z"],
endkey: [search],
descending:true,
include_docs: true
};
} else {
params = { limit: limit, skip: skip, descending:true, include_docs: true };
The above code currently filters service name but also sorts by service name before sorting by date. Is there anything I can add like (check snippet below) to force the query to sort the results by date without me having to do it in the code after I get the result.
const sort = [{'date': 'desc'}];
What I want is something like this when filtering by servicename:
SELECT * FROM incidents
WHERE serviceName LIKE #search+'%'
ORDER BY date DESC
But when not filtering by servicename:
SELECT * FROM incidents
ORDER BY date DESC
One way to do this is to ensure that the second element of the array you emit (the date-related bit) goes down as time proceeds. This can be achieved with a bit of hacking in the map function
function(doc) {
if(doc.type && doc.type=="incident") {
// get the date from the document
var d = doc.date;
// remove dash characters
var d1 = d.replace(/\-/g,'');
// convert to integer
var d2 = parseInt(d1)
// Subtract from number representing the year 3000
emit([doc.serviceName, 30000000 - d2], doc)
}
}
You may now query this view in ascending order (without descending=true) and the data will be sorted by serviceName and the time (reversed).
I am a mongodb newbie and would greatly appreciate help on this problem described below.
I have two collections "users" and "bags". The user collections scheme has {username, firstname, lastname} and the bag collection schema has {username, bagname, bagimage}.
While fetching users bags, I also want to display the firstname and lastname. My problem is that I cant seem to be able to frame a query correctly. I am using nodejs and mongojs driver. Below is my query for fetching all bags
thmConfig.db.bags.find({status: "1"}).sort({$natural:-1}, function(err, data)
{
var bagList = '{"bags":[';
if( err || !data) res.send('[{"status": "0"}]');
else data.forEach( function(innerData) {
console.log(innerData.username);
bagList += JSON.stringify(innerData)+",";
/*
This is where I would lke to also append the firstname from the
users collection
*/
});
console.log(bagList.slice(0,1));
res.write(magList.slice(0,-1));
res.end(']}');
});
I would greatly appreciate any help or pointers about this. I dont have a choice about changing the driver, so I specifically want to implement this using mongojs for now.
Thanks and regards,
Titash
I don't think that you have much choice other than reading from the users collection and doing this "join" operation programmatically.
You can either read the user document per each bag (inside your loop), or read the entire users collection into an object in advance, and do lookups by username
You could use the $in operator for that.
Pseudo-code(ish):
// get an array of bags matching your query
db.bags.find({status: "1"}).sort({$natural:-1}, function(err, bags) {
// get a list of usernames from the bags:
var usernames = bags.map(function(bag) { return bag.username; });
// perform query on user table: find all users for which we have a bag
db.users.find({ username : { $in : usernames } }, function(err, users) {
// create a mapping of username -> first name for easy lookup
var usernames = {};
users.forEach(function(user) {
usernames[user.username] = user.firstname;
});
// map first names to bags
bags.forEach(function(bag) {
bag.firstname = usernames[bag.username];
});
// done: return it as JSON (no need to build a JSON string ourselves)
res.send({ bags : bags });
});
});