couchbase N1ql query select with non-group by fields - couchbase

I am new to couchbase and I have been going through couchbase documents and other online resources for a while but I could't get my query working. Below is the data structure and my query:
Table1:
{
"jobId" : "101",
"jobName" : "abcd",
"jobGroup" : "groupa",
"created" : " "2018-05-06T19:13:43.318Z",
"region" : "dev"
},
{
"jobId" : "102",
"jobName" : "abcd2",
"jobGroup" : "groupa",
"created" : " "2018-05-06T22:13:43.318Z",
"region" : "dev"
},
{
"jobId" : "103",
"jobName" : "abcd3",
"jobGroup" : "groupb",
"created" : " "2018-05-05T19:11:43.318Z",
"region" : "test"
}
I need to get the jobId which has the latest job information (max on created timestamp) for a given jobGroup and region (group by jobGroup and region).
My sql query doesn't help me using self-join on jobId.
Query:
/*
Idea is to pull out the job which was executed latest for all possible groups and region and print the details of that particular job
select * from (select max(DATE_FORMAT_STR(j.created,'1111-11-11T00:00:00+00:00')) as latest, j.jobGroup, j.region from table1 j
group by jobGroup, region) as viewtable
join table t
on keys meta(t).id
where viewtable.latest in t.created and t.jobGroup = viewtable.jobGroup and
viewtable.region = t.region
Error Result: No result displayed
Desired result :
{
"jobId" : "102",
"jobName":"abcd2",
"jobGroup":"groupa",
"latest" :"2018-05-06T22:13:43.318Z",
"region":"dev"
},
{
"jobId" : "103",
"jobName" : "abcd3",
"jobGroup" : "groupb",
"created" : " "2018-05-05T19:11:43.318Z",
"region" : "test"
}

If I understand your query correctly, this can be answered using 'group by' and no join. I tried entering your sample data and the following query gives the correct result:
select max([created,d])[1] max_for_group_region
from default d
group by jobGroup, region;
How does it work? It uses 'group by' to group documents by jobGroup and region, then creates a two-element array holding, for every document in the group:
the 'created' timestamp field
the document where the timestamp came from
It then applies the max function on the set of 2-element arrays. The max of a set of arrays looks for the maximum value in the first array position, and if there's a tie look at the second position, and so on. In this case we are getting the two-element array with the max timestamp.
Now we have an array [ timestamp, document ], so we apply [1] to extract just the document.

I'm seeing some inconsistencies and invalid JSON in your examples, so I'm going to do the best I can. First off, I'm using Couchbase Server 5.5 which provides the new ANSI JOIN syntax. There might be a way to do this in an earlier version of Couchbase Server.
Next, I created an index on the created field: CREATE INDEX ix_created ON bucketname(created).
Then, I use a subquery to get the latest date, aggregated by jobGroup and region. I then join the latest date from this query to the entire bucket and select the fields that (I think) you want in your desired result:
SELECT k.jobId, k.jobName, k.jobGroup, k.created AS latest, k.region
FROM (
SELECT j.jobGroup, j.region, MAX(j.created) as latestDate
FROM so j
GROUP BY j.jobGroup, j.region
) dt
LEFT JOIN so k ON k.created = dt.latestDate;
Problems with this approach:
If two documents have the exact same date, this isn't a reliable way to determine the latest. You can add a LIMIT 1 to the subquery, which would just pick one arbitrarily, or you could ORDER BY whatever your preference is.
Subquery performance: I don't know how large your data set is, but this could be pretty slow.
Requires Couchbase Server 5.5, which is currently in beta.
If you are using a different version of Couchbase Server, you may want to consider asking in the Couchbase N1QL Forums for a more expert answer.

Related

How to control field order in Couchbase N1QL response?

How can I organize the json or table that the query show me? This in chouchbase with n1ql.
example:
select rol, count(*) as cantidad from PPS where type='Usuario'
group by rol
result
[
{
"cantidad": 2,
"rol": "8847cda1-cf52-4af0-880c-5f7c5a281348"
},
{
"cantidad": 2,
"rol": "ef35059f-5953-4da7-b5d5-ee0f9a1c893f"
}
]
I need rol first
I'm sorry, but what you're asking for isn't possible. Within each object the fields are returned in sorted order by name. You could rename the fields to something like "1_rol" and "2_cantidad", but that's the best that N1QL can do.
You might also alias the attributes in the select so that they auto order the way you want:
“select rol as 1, candidad as 2...”
Or, order them into an array:
“select [rol, candidad] as _res...”

Couchbase Full Text search with combination of dynamic fields and N1QL

Consider following documents and consider that is created Full-text index over following documents:
{
email : "A",
"data" : {
"dynamic_property" : "ANY_TYPE",
"dynamic_property2" : {
"property" : "searchableValue"
},
"field" : "VALUE"
}
},
{
email : "B",
"data" : {
"other_dynamic_prop" : "test-searchableValue-2",
}
},
{
email : "A",
"data" : {
"thirdDynamicProp" : {
"childProp" : "this should be searchableValue!"
}
}
}
The goal: Create N1QL query which will match all the documents with have associated given email address AND the data property contains given substring.
Basically following:
SELECT * FROM `bucket` WHERE `email` = 'A' AND `data` LIKE '%searchableValue%';
The expected result is the first and second document because matching criteria. But the query does not work because data is not a text type but is object type. If the data property would be like:
{"data" : "this should be searchableValue!" }
The query would return expected result.
The question is:
How to create such a N1QL query which would return expected result?
I know that Couchbase is not able to do compare substring in the text, but using Full-text index it should be possible since Couchbase 4.5+
Couchbase4.6 and 5.0 have more/better options (explained below). In couchbase4.5, you can use Array Indexing to solve this:
https://developer.couchbase.com/documentation/server/4.5/n1ql/n1ql-language-reference/indexing-arrays.html
https://www.couchbase.com/blog/2016/october/n1ql-functionality-enhancements-in-couchbase-server-4.5.1
For instance, using the travel-sample sample bucket, following array index, and query would do the kind of substring search you want.
create index tmp_geo on `travel-sample`(DISTINCT ARRAY x FOR x IN object_values(geo) END) where type = "airport";
select meta().id, geo from `travel-sample` where type = "airport"
and ANY x IN object_values(geo) SATISFIES to_string(x) LIKE "12%" END;
N1QL introduced a function TOKENS() in 4.6, which can help you create functional index on tokenized sub-objects (instead of array index in the above example):
https://developer.couchbase.com/documentation/server/4.6/n1ql/n1ql-language-reference/string-functions.html
https://dzone.com/articles/more-than-like-efficient-json-search-with-couchbas
And, Couchbase 5.0 developer build (https://blog.couchbase.com/2017/january/introducing-developer-builds) has N1QL function CURL(), which allows you to access any HTTP/REST endpoint as part of the N1QL query (hence, can access the FTS endpoint). See following blogs for more details & examples:
- https://blog.couchbase.com/2017/january/developer-release--curl-n1ql
- https://dzone.com/articles/curl-comes-to-n1ql-querying-external-json-data
Btw, can you clarify if you want partial tokens or only full tokens in the query?
-Prasad
Here are the specific queries based on the answer from #prasad.
Using Couchbase 4.5:
CREATE INDEX idx_email ON `bucket`( email );
SELECT *
FROM `bucket`
WHERE
`email` = 'A'
AND ANY t WITHIN `data` SATISFIES t LIKE '%searchableValue%' END;
Using Couchbase 4.6:
CREATE INDEX idx_email ON `bucket`( email );
CREATE INDEX idx_tokens ON `bucket`( DISTINCT ARRAY t FOR t IN TOKENS( `data` ) END );
SELECT *
FROM `bucket`
WHERE
`email` = 'A'
AND ANY t IN TOKENS( `data` ) SATISFIES t = 'searchableValue' END;

Using N1QL with document keys

I'm fairly new to couchbase and have tried to find the answer to a particular query I'm trying to create with not much success so far.
I've debated between using a view or N1QL for this particular case and settled with N1QL but haven't managed to get it to work so maybe a view is better after all.
Basically I have the document key (Group_1) for the following document:
Group_1
{
"cbType": "group",
"ID": 1,
"Name": "Group Atlas 3",
"StoreList": [
2,
4,
6
]
}
I also have 'store' documents, their keys are listed in this document's storelist. (Store_2, Store_4, Store_6 and they have a storeID value that is 2, 4 and 6) I basically want to obtain all 3 documents listed.
What I do have that works is I obtain this document with its id by doing:
var result = CouchbaseManager.Bucket.Get<dynamic>(couchbaseKey);
mygroup = JsonConvert.DeserializeObject<Group> (result.ToString());
I can then loop through it's storelist and obtain all it's stores in the same manner, but i don't need anything else from the group, all i want are the stores and would have prefered to do this in a single operation.
Does anyone know how to do a N1QL directly unto a specified document value?
Something like (and this is total imaginary non working code I'm just trying to clearly illustrate what I'm trying to get at):
SELECT * FROM mycouchbase WHERE documentkey IN
Group_1.StoreList
Thanks
UPDATE:
So Nic's solution does not work;
This is the closest I get to what I need atm:
SELECT b from DataBoard c USE KEYS ["Group_X"] UNNEST c.StoreList b;
"results":[{"b":2},{"b":4},{"b":6}]
Which returns the list of IDs of the Stores I want for any given group (Group_X) - I haven't found a way to get the full Stores instead of just the ID in the same statement yet.
Once I have, I'll post the full solution as well as all the speed bumps I've encountered in the process.
I apologize if I have a misunderstanding of your question, but I'm going to give it my best shot. If I misunderstood, please let me know and we'll work from there.
Let's use the following scenario:
group_1
{
"cbType": "group",
"ID": 1,
"Name": "Group Atlas 3",
"StoreList": [
2,
4,
6
]
}
store_2
{
"cbType": "store",
"ID": 2,
"name": "some store name"
}
store_4
{
"cbType": "store",
"ID": 4,
"name": "another store name"
}
store_6
{
"cbType": "store",
"ID": 6,
"name": "last store name"
}
Now lets say you wan't to query the stores from a particular group (group_1), but include no other information about the group. You essentially want to use N1QL's UNNEST and JOIN operators.
This might leave you with a query like so:
SELECT
stores.name
FROM `bucket-name-here` AS groups
UNNEST groups.StoreList AS groupstore
JOIN `bucket-name-here` AS stores ON KEYS ("store_" || groupstore.ID)
WHERE
META(groups).id = 'group_1';
A few assumptions are made in this. Both your documents exist in the same bucket and you only want to select from group_1. Of course you could use a LIKE and switch the group id to a percent wildcard.
Let me know if something doesn't make sense.
Best,
Try this query:
select Name
from buketname a join bucketname b ON KEYS a.StoreList
where Name="Group Atlas 3"
Based on your update, you can do the following:
SELECT b, s
FROM DataBoard c USE KEYS ["Group_X"]
UNNEST c.StoreList b
JOIN store_bucket s ON KEYS "Store_" || TO_STRING(b);
I have a similar requirement and I got what I needed with a query like this:
SELECT store
FROM `bucket-name-here` group
JOIN `bucket-name-here` store ON KEYS group.StoreList
WHERE group.cbType = 'group'
AND group.ID = 1

Limiting and sorting by different properties on couchbase

Given a JSON document on couchbase, for example, a milestone collections, which is similar to this:
{
"milestoneDate" : /Date(1335191824495+0100)/,
"companyId" : 43,
"ownerUserId": 475,
"participants" : [
{
"userId": 2,
"docId" : "132546"
},
{
"userId": 67,
"docId" : "153"
}
]
}
If I were to select all the milestones of the company 43 and want to order them by latest first.. my view on couchbase would be something similar to this:
function (doc, meta) {
if(doc.companyId && doc.milestoneDate)
{
//key made up of date particles + company id
var eventKey = dateToArray(new Date(parseInt(doc.milestoneDate.substr(6))));
eventKey.push(doc.companyId);
emit(eventKey, null);
}
}
I do get both dates and the company Id on rest urls.. however, being quite new to couchbase, I am unable to work out how to restrict the view to return only milestones of company 43
The return key is similar to this:
"key":[2013,6,19,16,11,25,14]
where the last element (14) is the company id.. which is quite obviously wrong.
The query parameters that I have tried are:
&descending=true&startkey=[{},43]
&descending=true&startkey=[{},43]&endKey=[{},43]
tried adding companyId to value but couldn't restrict return results by value.
And according to couchbase documentation I need the date parts in the beginning to sort them. How do I restrict them by company id now, please?
thanks.
Put the company id at the start of the array, and because you'll be limiting by company id, couchbase sorts by company id and then by date array so you will be only ever getting the one company's milestone documents
I'd modify the view to emit
emit([doc.copmanyId, eventKey], null);
and then you can query the view with
&descending=true&startkey=[43,{}]
This was what worked for me previously..
I went back and tried it with end key and this seems to work - restricts and orders as required:
&descending=true&startkey=[43,{}]&endkey=[42,{}]
or
&descending=true&startkey=[43,{}]&endkey=[43,{}]&inclusive_end=true
either specify the next incremented/decremented value (based on descending flag) with end key, or use the same endkey as startkey and set inclusiveEnd to true
Both of these options should work fine. (I only tested the one with endkey=42 but they should both work)

mongodb find() order is different from schema order

db.blog.save({ title : "My First Post", author: {name : "Jane", id : 1}})
what should below return as the key order does not match?
db.blog.find({"author" : {"id" : 1, "name" : "Jane"}})
EDIT:
based on official mongodb documentation , the keyorder must match (at least for findOne()). It wont return the match-only object using db.blog.findOne({"author" : {"id" : 1, "name" : "Jane"}})
The order of the keys in your query selector is irrelevant. It doesn't need to match the order of the keys you used when adding the document you're searching for.
UPDATE
If you're just looking for an order-independent way to query based on an embedded document, you need to use dot notation:
db.blog.find({"author.id" : 1, "author.name" : "Jane"})
Normally, as #JohnnyHK states the order of the query keys does not matter except for the example you have shown:
db.blog.find({"author" : {"id" : 1, "name" : "Jane"}})
This query will not return results that do not match exactly. Using the query he shows of:
db.blog.find({"author.id" : 1, "author.name" : "Jane"})
Will be key order independent. The reasons for this difference is because in the first query you are searching by an object as such the querier actually searches for exactly that object (in the simplest terms). The same applies for indexes created on the field which contains a set of sub documents, the order does matter.
According to the JSON definition, the key order doesn't matter.
An object is an unordered collection of zero or more name/value pairs
I don't know anything about MongoDB, but I assume it follows the normal rules of JSON, at which point it should return the "My First Post" entry.