Query Azure Cosmos for items that have all elements - azure

Let's say I have couple sample items in my DB that look like this:
Example 1:
{
'id': 'someid',
'columns': ['apple', 'banana'],
'filters': {'car': 'red', 'truck': 'blue'}
}
Example 2:
{
'id': 'someid',
'columns': ['apple', 'banana', 'carrots'],
'filters': {'truck': 'blue', 'car': 'red', 'boat':'green'}
}
Fast forward to the present, I have a new set of columns and of filters that might look like:
newcolumns=['banana','apple'] and newfilters={'truck':'blue', 'car':'red'}
I want to do a query like
select *
from f
where f.columns=newcolumns and f.filters=newfilters
but I don't care about the order and they have to be a complete match. Neither set can be a super or subset of the other. So in this case my query should return example 1 but not example 2.
As a note, there are two parts to my question, the columns matching is answered by this but the filters field isn't a list so the syntax isn't the same.

You can't really match by doing an equality-comparison on two arrays like you had:
where f.columns=newcolumns and f.filters=newfilters
Instead, use Cosmos DB's built-in ARRAY_CONTAINS(), combined with ARRAY_LENGTH().
Since columns is a scalar array, it would look something like:
WHERE ARRAY_CONTAINS(c.columns, "banana")
AND ARRAY_CONTAINS(c.columns, "apple")
For filters, that isn't an array, so... first check to make sure the key is defined, then check if the value is correct:
WHERE IS_DEFINED(c.filters.truck) AND c.filters.truck="blue"
AND IS_DEFINED(c.filters.car) AND c.filters.car="red"
note: probably a good idea to turn your filters into subdocuments, since your current schema is an anti-pattern (using values as keys). Something like:
{
"filters": [
{ "vehicleType": "truck", "color": "blue" },
{ "vehicleType": "car", "color": "red" }
}
at that point you can compare with ARRAY_CONTAINS() again. Something like:
WHERE ARRAY_CONTAINS(c.filters, { "vehicleType": "truck", "color": "blue"}, true)

Related

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)

How to find using size array in couchdb

How to create a query CouchDB to retrieve all documents where the size of elements is Greater than X.
This query return all documents where movies.size = 3
{
"selector": {
"movies": { "$size": 3}
}
}
I need to find where movies.size > 3
I'm using CouchDB 2.1.1
This isn't supported at the moment in Mango. Altought, it shouldn't be hard to implement. At the moment, you can create a view that index the array lengths. From that, you could use end_key=3 to get document with length lower then 3.
if your list is not very long, consider using $or operator:
"$or": [
{"movies": {"$size": 4}},
{"movies": {"$size": 5}},
{"movies": {"$size": 6}},
]
This works for version 3.1.1.
{
"selector": {
"movies.4": {
"$exists": true
}
}
}
Trivial clarification: the 4 is because of you asked for > 3. If you need >= 3 use movies.3
However, I don't think there is a way to index the elements of an array.
I kept the solution from a MongoDb answerer https://stackoverflow.com/a/15224544/846168

Couchdb views return rows in rows

I am trying to create a view that returns the name with the price changes from the doc.
"name": "USD"
"price_changes": {
"0min": 0,
"15min": 0,
"30min": 0,
"60min": 0,
"90min": 0,
"120min": 0,
"150min": 0,
"180min": 0,
"210min": 0,
"240min": 0,
"270min": 0,
...
What I want is a view that returns all the data in "price_changes".
{"id":"c95718ebd00b13bc9a8b03a0e5005d51",
"key": {"0min":0,"15min":0,"30min":0,"60min":0,"90min":0,"120min":0,"150min":0,...}
{"id":"c95718ebd00b13bc9a8b03a0e5006906",
"key": {"0min":0,"15min":0,"30min":0,"60min":0,"90min":0,"120min":0,"150min":0,...}
...
And views that return a set of the rows, like only "0mins", "15mins" and "30mins" for each name.
Like:
"id":"c95718ebd00b13bc9a8b03a0e5005d51",
"name": "USD",
"key": {"0min":0,"15min":0,"30min":0}
Separately
"id":"c95718ebd00b13bc9a8b03a0e5006906",
"name": "GBP",
"key": {"0min":0,"15min":0,"30min":0}
What I have so far is (but doesn't return what I want):
function (doc) {
var mins, value;
if (doc.price_changes) {
for (mins in doc.price_changes) {
value = doc.price_changes[mins];
emit(doc.id, [value], mins);
}
}
}
Any idea guys? I can't really find anything on returning data like this.
Firstly, in your view map function, you're emitting three things:
emit(doc.id, [value], mins);
Which is not what it should be. As documented, you need to emit key/value pairs (well, of course, key or value can be an array of many things):
Map functions accept a single document as the argument and
(optionally) emit() key/value pairs that are stored in a view.
According to what you say:
What I want is a view that returns all the data in "price_changes".
My understanding is that you need the following map function:
function (doc) {
var mins, value;
if (doc.price_changes) {
for (mins in doc.price_changes) {
value = doc.price_changes[mins];
emit(mins, [value, doc._id]);
}
}
}
And it returns:
$ curl -k -X GET https://admin:****#192.168.1.106:6984/sample-repl/_design/price/_view/price
{"total_rows":11,"offset":0,"rows":[
{"id":"so_q","key":"0min","value":[0,"so_q"]},
{"id":"so_q","key":"120min","value":[0,"so_q"]},
{"id":"so_q","key":"150min","value":[0,"so_q"]},
{"id":"so_q","key":"15min","value":[0,"so_q"]},
{"id":"so_q","key":"180min","value":[0,"so_q"]},
{"id":"so_q","key":"210min","value":[0,"so_q"]},
{"id":"so_q","key":"240min","value":[0,"so_q"]},
{"id":"so_q","key":"270min","value":[0,"so_q"]},
{"id":"so_q","key":"30min","value":[0,"so_q"]},
{"id":"so_q","key":"60min","value":[0,"so_q"]},
{"id":"so_q","key":"90min","value":[0,"so_q"]}
]}
I'm not sure if the above result is what you want, please let me know if I'm wrong.
Thanks! Just wondering how would I return say the first 3 (like: 0min,
15min, 30min)
To get the first 3, you can use limit query parameter like this, as you can see, it will return the first 3:
$ curl -k -X GET https://admin:****#192.168.1.106:6984/sample-repl/_design/price/_view/price?limit=3
{"total_rows":11,"offset":0,"rows":[
{"id":"so_q","key":"0min","value":[0,"so_q"]},
{"id":"so_q","key":"120min","value":[0,"so_q"]},
{"id":"so_q","key":"150min","value":[0,"so_q"]}
]}
In general, you can play around with query parameters to get what you want out of the view/index.

mongodb sort by dynamic property [duplicate]

I have a Mongo collection of messages that looks like this:
{
'recipients': [],
'unRead': [],
'content': 'Text'
}
Recipients is an array of user ids, and unRead is an array of all users who have not yet opened the message. That's working as intended, but I need to query the list of all messages so that it returns the first 20 results, prioritizing the unread ones first, something like:
db.messages.find({recipients: {$elemMatch: userID} })
.sort({unRead: {$elemMatch: userID}})
.limit(20)
But that doesn't work. What's the best way to prioritize results based on whether they fit a certain criteria?
If you want to "weight" results by certain criteria or have any kind of "calculated value" within a "sort", then you need the .aggregate() method instead. This allows "projected" values to be used in the $sort operation, for which only a present field in the document can be used:
db.messages.aggregate([
{ "$match": { "messages": userId } },
{ "$project": {
"recipients": 1,
"unread": 1,
"content": 1,
"readYet": {
"$setIsSubset": [ [userId], "$unread" ] }
}
}},
{ "$sort": { "readYet": -1 } },
{ "$limit": 20 }
])
Here the $setIsSubset operator allows comparison of the "unread" array with a converted array of [userId] to see if there are any matches. The result will either be true where the userId exists or false where it does not.
This can then be passed to $sort, which orders the results with preference to the matches ( decending sort is true on top ), and finally $limit just returns the results up to the amount specified.
So in order to use a calulated term for "sort", the value needs to be "projected" into the document so it can be sorted upon. The aggregation framework is how you do this.
Also note that $elemMatch is not required just to match a single value within an array, and you need only specify the value directly. It's purpose is where "multiple" conditions need to be met on a single array element, which of course does not apply here.

Filter elements within object in DynamoDB

I have a DynamoDB table which contains objects looking like the following:
{
'username': ...,
'subscriptions': [
...
],
...
}
And I would like to filter subscriptions for each user based on some criteria, and get back the objects which have subscriptions matching the criteria, but filter those objects so that only the matching subscriptions are present.
If a user has subscribed to 50 things, but only 3 of them match, I would like to get back an object where the `subscriptions' field is only 3 elements long. I also need the other information contained in the object.
To give a more specific example, suppose that I have two elements in my table:
{
'username': 'alice',
'subscriptions': [
1,
2
],
'email': 'alice#a.com'
},
{
'username': 'bob',
'subscriptions': [
2,
3
],
'email': 'bob#a.com'
}
And I would like to filter to get the subscription `1'. I would like to get back
{
'username': 'alice',
'subscriptions': [
1
],
'email': 'alice#a.com'
}
Or perhaps some other structure which contains all the necessary information (and no other information in order to save bandwidth).
I believe I can do this with scan(), but I do not know the specifics.

Resources