One element of my mongo collection has the following schema (and represents one poker hand).
{
"players":
[
{"active":true,"seat":1,"stack":1769,"screenName":"schla"},
{"active":true,"seat":3,"stack":2000,"screenName":"galgegla"},
{"active":true,"seat":4,"stack":483,"screenName":"khrier"},
{"active":true,"seat":5,"stack":2813,"screenName":"frrou4535"},
{"active":true,"seat":6,"stack":4002,"screenName":"Guabounai"}
],
"rounds":[],
"gameNum":"1030564654564",
"time":"2013/12/21 21:12:03"
}
I'd like to search for all hands in my collection that have at least one time the player with screenName "galgegla" inside the players array.
Supposing your collection is called 'hands', you can do following:
db.hands.find( { 'players.screenName': 'galgegla' } )
See example under 'Match a Field Without Specifying Array Index' here: http://docs.mongodb.org/manual/tutorial/query-documents/#match-a-field-without-specifying-array-index
Try this:
a.filter(function(arr){
return arr.players.filter(function(player){
return player.screenName == 'galgegla';
}).length;
});
Assuming a is a array of those objects.
It filters the array of objects based on weather or not a filter on the object's players array returns any results.
... But of course I didn't consider mongo's native functionality, so the other answer'd be more effective. (But possibly not as efficient)
Related
I have documents with field (of array type) for period (from, to):
and I would like to filter documents by item in array of "period" field ... for example:
where('period.0', '>=', startOfYear(new Date()))
but it seems that this doesn't work ...
For example if period is a Map instead of Array, then this thing works:
where('period.start', '>=', startOfYear(new Date()))
But, I store this as an array, because output of UI component is an array and I don't have to map array to object, and later from object for array.
So the question is why this doesn't work with array, and if there is any plan, that this will be supported for arrays to?
There is no operator to filter based on an item at a specific index in an array. If you need that, as you already said, you will need to put the relevant value from the array in a separate field and filter on that.
There are currently no plans to add such an operation, but you can always weigh in with a feature request.
I'm attempting fetch multiple items by an id property based on a list of ids. To give an example lets use the function below.
findItems(idList: string[]){
let fetchedItems: SomeType[] = [];
idList.forEach(a=>{
fetchedItems.push(DATASOURCE.find(b=> b.id === a));
}
return fetchedItems;
}
As you can see this function takes a list of strings and uses a forEach() method to individually find each item and push it to the fetchedItems array, which means if there's 10 items in that list there will be 10 consecutive calls to the same database.
Looking further into this curiosity it hit me that filter() is designed to do this exact thing. I looked at the documentation on MDN and googled around a bit but I guess I don't know what to specifically search for to get the results I need to study into. Can anyone show me how this can be done?
Make idList into a Set (for less computational complexity), and then you can .filter on DATASOURCE by whether the id is included in the set:
findItems(idList: string[]){
const ids = new Set(idList);
return DATASOURCE.filter(({ id }) => ids.has(id));
}
This assumes that you don't have multiple items in DATASOURCE with duplicate IDs.
Set.has has O(1) complexity, whereas array methods like .find (or .includes, etc) have O(n) complexity.
How about
findItems(idList: string[]){
return DATASOURCE.filter(data => idList.includes(data.id));
}
I am writing a REST api which I want to make idempotent. I am kind of struggling right now with nested arrays and idempotency. I want to update an item in product_notes array in one atomic operation. Is that possible in MongoDB? Or do I have to store arrays as objects instead (see my example at the end of this post)? Is it for example possible to mimic the upsert behaviour but for arrays?
{
username: "test01",
product_notes: [
{ product_id: ObjectID("123"), note: "My comment!" },
{ product_id: ObjectID("124"), note: "My other comment" } ]
}
If I want to update the note for an existing product_node I just use the update command and $set but what if the product_id isn't in the array yet. Then I would like to do an upsert but that (as far as I know) isn't part of the embedded document/array operators.
One way to solve this, and make it idempotent, would be to just add a new collection product_notes to relate between product_id and username.
This feels like violating the purpose of document-based databases.
Another solution:
{
username: "test01",
product_notes: {
"123": { product_id: ObjectID("123"), note: "My comment!" },
"124": { product_id: ObjectID("124"), note: "My other comment" } }
}
Anyone a bit more experienced than me who have anything to share regarding this?
My understanding of your requirement is that you would like to store unique product ids (array) for an user.
You could create an composite unique index on "username" and "username.product_id". So that when the same product id is inserted in the array, you would an exception which you could catch and handle in the code as you wanted the service to be Idempotent.
In terms of adding the new element to an array (i.e. product_notes), I have used Spring data in which you need to get the document by primary key (i.e. top level attribute - example "_id") and then add a new element to an array and update the document.
In terms of updating an attribute in existing array element:-
Again, get the document by primary key (i.e. top level attribute -
example "_id")
Find the correct product id occurrence by iterating the array data
Replace the "[]" with array occurrence
product_notes.[].note
I'm just getting started with JavaScript objects. I'm trying to store catalog inventory by id and locations. I think a single object would look something like this:
var item = {
id: number,
locations: ["location1", "location2"]
};
I've started to read a bit about it but am still trying to wrap my head around it. Not sure what is the fastest way add new items to a list with a location, add a new location to an existing item, all while checking for dupes. Performance of getting the locations later isn't as critical. This is part of a process that is running thousands of checks to eventually get items by id and location, so performance is key.
Final question, I'm not even sure if it's possible to store this in local storage. From another similar question, I'm not sure.
Using lodash, something like this should work to determine if an item id exists and append either a new item to the array, or just add a new location:
var item = [{
id: 1,
locations: ["location1", "location2"]
},{
id: 2,
locations: ["location2", "location4"]
}];
function findItem(id){
return _.findIndex(item, function(chr) {
return chr.id == id;
});
}
function addItem(id,locations) {
var position = findItem(id);
if (position<0) {
item.push({
id: id,
locations: locations
})
} else {
item[position].locations = _.uniq(item[position].locations.concat(locations));
}
}
addItem(2,['location292']);
addItem(3,['location23']);
console.log(item);
What it basically does is to search the array of objects (item) for an id as the one we are passing to the addItem() function, if it is found we add the new locations array to the existing item, if not it's creating a new object with a new id and location.
You've asked a question that contains some tradeoffs:
The simplest and fastest way to retrieve a list of locations is to store them in an array.
The fastest way to check something for a duplicates is not an array, but rather a map object that maintains an index of the key.
So, you'd have to discuss more about which set of tradeoffs you want. Do you want to optimize for performance of adding a non-duplicate or optimize for performance of retrieving the list of locations. Pick one or the other.
As for localStorage, you can store any string in LocalStorage and you can convert simply non-reference objects to a string with JSON.stringify(), so yes this type of structure can be stored in LocalStorage.
For example, if you want to use the array for optimized retrieval, then you can check for duplicates like this before adding:
function addLocation(item, newLocation) {
if (item.locations.indexOf(newLocation) === -1) {
item.locations.push(newLocation);
}
}
Also, you can store an array of items in LocalStorage like this:
localStorage.setItem("someKey", JSON.stringify(arrayOfItems));
And, then some time later, you can retrieve it like this:
var arrayOfItems = JSON.parse(localStorage.getItem("someKey"));
The resulting data of a multiple table query (see http://www.breezejs.com/documentation/query-examples#Lookups) is not exactly what I expected. I expected an array (item for each table) of arrays (of entity fields). But, what I get is an array of sort-of arrays. The outer array is -- as expected -- filled with an item for each table. But, the inner objects are not typed as Array. They do actually have members named like indexed items (0, 1, 2, ...), but since it's not an array, I cannot use length or forEach. Is this a bug in Breeze?
Note that when I query for items from one table, the result is typed as array (of entity fields).
Here's my server code:
[HttpGet]
public object _Combo()
{
return new IOrderedQueryable[] { _contextProvider.Context.Company, _contextProvider.Context.Project, _contextProvider.Context.ProjectArea };
}
Here's my client code:
entityManager.executeQuery(breeze.EntityQuery.from("_Combo"))
.then(function (data) {
var companies = data.results[0];
//companies is not an array, but does have numbered members!
}).fail(queryFailed);
I'm not completely sure what "shape" you want returned from looking at your "_Combo" code? But if it is an anon type that contains Companies, Projects and ProjectAreas, then the following should work. You don't need to return a IQueryable unless you plan to have a client side filter applied on the server.
[HttpGet]
public object _Combo()
{
return new { Companies = ContextProvider.Context.Company.ToList(),
Projects = ContextProvider.Context.Project.ToList(),
ProjectAreas = ContextProvider.Context.ProjectAreas.ToList() }
}
This will return a single element array with its one item containing an object with three properties: "Companies", "Projects" and "ProjectAreas". Each of which is an array of the corresponding entities. All of these entities will have been added to the entityManager. Hope this helps.