Arangodb removing subitems from document - arangodb

How does one remove subitems from a document. So say I have a document called sales with each sale has a sale.item which contains {name,price,code}.
I want to remove each item which is not valid, by checking the code for blank or null.
Trying something like below fails with errors, am not sure if I need to use sub-query and how.
FOR sale in sales
FOR item in sale.items
FILTER item.code == ""
REMOVE item IN sale.items
Another attempt
FOR sale in sales
LET invalid = (
FOR item in sale.items
FILTER item.code == ""
RETURN item
)
REMOVE invalid IN sale.items LET removed = OLD RETURN removed

The following query will rebuild the items for each document in sales. It will only keep item whose code is not null and not the empty string:
FOR doc IN sales
LET newItems = (
FOR item IN doc.items
FILTER item.code != null && item.code != ''
RETURN item
)
UPDATE doc WITH { items: newItems } IN sales
Here is the test data I used:
db.sales.insert({
items: [
{ code: null, what: "delete-me" },
{ code: "", what: "delete-me-too" },
{ code: "123", what: "better-keep-me" },
{ code: true, what: "keep-me-too" }
]
});
db.sales.insert({
items: [
{ code: "", what: "i-will-vanish" },
{ code: null, what: "i-will-go-away" },
{ code: "abc", what: "not me!" }
]
});
db.sales.insert({
items: [
{ code: "444", what: "i-will-remain" },
{ code: null, what: "i-will-not" }
]
});

There's a better way to do this, without sub-queries. Instead, a function for removing an array element, will be used:
FOR doc IN sales
FOR item IN doc.items
FILTER item.code == ""
UPDATE doc WITH { items: REMOVE_VALUE( doc.items, item ) } IN sales
REMOVE_VALUE takes an array as the first argument, and an array item inside that array as the second argument, and returns an array that has all the items of the first argument, but without that specific item that was in the second argument.
Example:
REMOVE_VALUE([1, 2, 3], 3) = [1, 2]
Example with subdocuments being the values:
REMOVE_VALUE( [ {name: cake}, {name: pie, taste: delicious}, {name: cheese} ] , {name: cheese}) = [ {name: cake}, {name: pie, taste: delicious} ]
You cannot just use REMOVE_VALUE separately, the way you use the REMOVE command separately. You must use it as part of an UPDATE command not as part of a REMOVE command. Unfortunately, the way it works is to make a copy of the "items" list inside your one specific "doc" that you are currently dealing with, but the copy has the subdocument you don't like, removed from the "items" list. That new copy of the list replaces the old copy of the items list.
There is one more, most efficient way to remove subdocuments from a list - and that is by accessing the specific cell of the list with items[2] - and you have to use fancy array functions even fancier than the one I used here, to find out the specific cell in the list (whether it's [2] or [3] or [567]) and then to replace the contents of that cell with Null, using the UPDATE command, and then to set the options to KeepNull = false. That's the "most efficient" way to do it but it would be a monstrous looking complicated query ): I might write that query later and put it here but right now .. I would honestly suggest using the method I described above, unless you have a thousand subdocuments in each list.

Related

Nodejs Elasticsearch query default behaviour

On a daily basis, I'm pushing data (time_series) to Elasticsearch. I created an index pattern, and my index have the name: myindex_* , where * is today date (an index pattern has been setup). Thus after a week, I have: myindex_2022-06-20, myindex_2022-06-21... myindex_2022-06-27.
Let's assume my index is indexing products' prices. Thus inside each myindex_*, I have got:
myindex_2022-06-26 is including many products prices like this:
{
"reference_code": "123456789",
"price": 10.00
},
...
myindex_2022-06-27:
{
"reference_code": "123456789",
"price": 12.00
},
I'm using this query to get the reference code and the corresponding prices. And it works great.
const data = await elasticClient.search({
index: myindex_2022-06-27,
body: {
query: {
match: {
"reference_code": "123456789"
}
}
}
});
But, I would like to have a query that if in the index of the date 2022-06-27, there is no data, then it checks, in the previous index 2022-06-26, and so on (until e.g. 10x).
Not sure, but it seems it's doing this when I replace myindex_2022-06-27 by myindex_* (not sure it's the default behaviour).
The issue is that when I'm using this way, I got prices from other index but it seems to use the oldest one. I would like to get the newest one instead, thus the opposite way.
How should I proceed?
If you query with index wildcard, it should return a list of documents, where every document will include some meta fields as _index and _id.
You can sort by _index, to make elastic search return the latest document at position [0] in your list.
const data = await elasticClient.search({
index: myindex_2022-*,
body: {
query: {
match: {
"reference_code": "123456789"
}
}
sort : { "_index" : "desc" },
}
});

Is it possible to delete the firsts elements in an array?

I'm coding an application, and in one of the parts of the code I need to delete a certain amount of elements from an array, but I don't have much experience with MongoDB/Mongoose.
For example, my query needs to delete the first n elements of an array:
let array = ["a", "b", "c", "d", "e"];
if (n < array.length) {
array = array.slice(n); // Delete the first n values
}
I could loop my code, but it will do a lot of queries, deleting one by one, which is bad for performance. My other alternative is to query the array elements, slice in the code, and update the database array with another query. It's also a good alternative, but I prefer to ask here because maybe there is a way to do it with just one query.
I know in MongoDB there is a way to just remove the first/last element, but I haven't found anything about deleting more than one.
Model.updateOne(filter, { $pop: { list: -1 } });
Hey you can check out the $slice operator when you query. That said, it works well, if you're removing multiple elements from the end or multiple elements from the start of the array. But if you're removing from the middle of the array, it may be easier to find the document first, apply a standard .slice() operation in JavaScript, and then save the document with the new array, by calling the .save() method on the document.
As examples for the $slice operator:
const document = { "_id" : 3, "scores" : [ 89, 70, 100, 20 ] }
db.students.update(
{ _id: 3 },
{
$push: {
scores: {
$each: [ ],
$slice: -2
}
}
}
)
/*
Our result is the last two elements, and we removed the first two.
*/
const result = { "_id" : 3, "scores" : [ 100, 20 ] }
Or you can something like the following:
db.students.update({_id: 3}, [
{$set: {scores: {
$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]
}}}
]);
Where P is the index of element you want to stop removing in the array, leaving you with P to the end elements. (meaning all elements prior to P are removed)
Or this:
db.students.update({_id: 3}, [
{$set: {scores: {
$slice: ["$field", P]
}}}
]);
Where P is the index of element you want to stop removing in the array from the end, leaving you with start to P elements. (meaning all elements after P are removed)

Mongo Query, Find whether a string is present in an array

Person = { name: string}
const names = {John, Mark, Fady}
how to make a find query to get all the persons that their names are in the array?
i could make a loop over the array and make a find query for every element but this will not be efficient since it will make N find queries.
db.collection.find( { name: { $in: ['John', 'Mark', 'Fady' ] } } )

Pull from array which is in an array

I have a collection which contains a teams array and that contains a players array.
I would like to delete from the players array.
I think I know hot to delete from an array, but I can't make it work.
There was a case when it deleted all elements from the teams array.
Here it is how the doc look like:
"teams" : [
{
"_guid" : "5c5b3bc0-a957-11e5-b909-b7a1cbe2c8be",
"teamname" : "Ping-Win_team",
"_id" : ObjectId("567a68f6a7c726540b2d746b"),
"players" : [
ObjectId("567a68f6a7c726540b2d7469"),
ObjectId("567a68f7a7c726540b2d746c")
]
}
],
My probation:
db.lobbies.update({ _id: ObjectId('567a68f6a7c726540b2d746a') }, { $pull: { 'teams': { 'players.$': ObjectId('567a68f7a7c726540b2d746c') }}})
Thanks for helping,
Akos
Apply the $pull operator together with the $ positional operator in your update to change the name field. The $ positional operator will identify the correct element in the array to update without explicitly specifying the position of the element in the array, thus your final update statement should look like:
db.lobbies.update(
{ "teams.players": ObjectId("567a68f7a7c726540b2d746c") },
{
"$pull": {
"teams.$.players": ObjectId("567a68f7a7c726540b2d746c")
}
}
)
If I got correctly your question.. you can't pull one of the player IDs because:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
From: https://docs.mongodb.org/v3.0/reference/operator/update/positional/
Otherwise you will pull the whole item of the teams array that contains that specific player.
You can use below code to delete one or multiple values in a nested array
db.sessions.update(
{
"teams": {
$elemMatch: {
"players": {$in: [ObjectId("567a68f7a7c726540b2d746c")]}
}
}},
{
"$pull": {
"teams.$.players": {$in:[ObjectId("567a68f7a7c726540b2d746c")]}
}
})

Query for a list contained in another list in mongodb

I'm fairly new to mongo and while I can manage to do most basic operations with the $in, $or, $all, ect I can't make what I want to work.
I'll basically put a simple form of my problem. Part of my documents are list of number, eg :
{_id:1,list:[1,4,3,2]}
{_id:2,list:[1]}
{_id:3,list:[1,3,4,6]}
I want a query that given a list(lets call it L), would return me every document where their entire list is in L
for example with the given list L = [1,2,3,4,5] I want document with _id 1 and 2 to be returned. 3 musn't be returned since 6 isn't in L.
"$in" doesn't work because it would also return _id 3 and "$all" doesn't work either because it would only return _id 1.
I then thought of "$where" but I can't seem to find how to bound an external variable to the js code. What I call by that is that for example :
var L = [1,2,3,4,5];
db.collections('myCollection').find({$where:function(l){
// return something with the list "l" there
}.bind(null,list)})
I tried to bind list to the function as showed up there but to no avail ...
I'd glady appreciate any hint concerning this issue, thanks.
There's a related question Check if every element in array matches condition with an answer with a nice approach for this scenario. It refers to an array of embedded documents but can be adapted for your scenario like this:
db.list.find({
"list" : { $not : { $elemMatch : { $nin : [1,2,3,4,5] } } },
"list.0" : { $exists: true }
})
ie. the list must not have any element that is not in [1,2,3,4,5] and the list must exist with at least 1 element (assuming that's also a requirement).
You could try using the aggregation framework for this where you can make use of the set operators to achieve this, in particular you would need the $setIsSubset operator which returns true if all elements of the first set appear in the second set, including when the first set equals the second set; i.e. not a strict subset.
For example:
var L = [1,2,3,4,5];
db.collections('myCollection').aggregate([
{
"$project": {
"list": 1,
"isSubsetofL": {
"$setIsSubset": [ "$list", L ]
}
}
},
{
"$match": {
"isSubsetofL": true
}
}
])
Result:
/* 0 */
{
"result" : [
{
"_id" : 1,
"list" : [
1,
4,
3,
2
],
"isSubsetofL" : true
},
{
"_id" : 2,
"list" : [
1
],
"isSubsetofL" : true
}
],
"ok" : 1
}

Resources