1 2 3 Previous Next 35 Replies Latest reply: Jan 9, 2012 3:10 PM by 876250 Go to original post RSS
      • 15. Re: Achieving functionality of many preferences using one context index
        Roger Ford-Oracle
        Create a default thesaurus using
        execute ctx_thes.create_thesaurus('default')
        Then try Barbara's script again.

        The 0 2 3 4 5 look line line numbers - the input and output are slightly out of sync. I don't think you need worry about them.
        • 16. Re: Achieving functionality of many preferences using one context index
          876250
          That is working. After creating a default thesaurus we were able to execute queries properly.


          SQL> execute ctx_thes.create_thesaurus('default');

          PL/SQL procedure successfully completed.
          SQL> column text format a30
          SQL> exec :search_string := 'Mark Antony'

          PL/SQL procedure successfully completed.

          SQL> select * from test_sh
          where contains
          (text,
          'ndata (nd,' || :search_string || ') or
          syn (?!$' || replace (:search_string, ' ', ';?!$') || ')') > 0 2 3 4 5
          6 /

          TEXT_ID TEXT
          ---------- ------------------------------
          1 Mark Antony
          2 MARK ANTONY
          3 mark antony
          4 MarkAntony
          5 MA RK ANTONY
          6 m a r k antony

          6 rows selected.
          The production system which we are going to implement this search is of version 11.1.0.7. How can we achieve all this in 11.1.0.7 ?
          • 17. Re: Achieving functionality of many preferences using one context index
            Barbara Boehmer
            Does it work in 11.1.0.7 after you create a default thesaurus there too? I believe the syntax should work in both releases.
            • 18. Re: Achieving functionality of many preferences using one context index
              Barbara Boehmer
              Also, please bear in mind that using SYN is pointless unless you have some data in your thesaurus. Creating an empty default thesaurus will avoid the error, but it will not find any synonyms.
              • 20. Re: Achieving functionality of many preferences using one context index
                Barbara Boehmer
                If you add a virtual column to the multi-column datastore that eliminates spaces from the data in the table and also add an OR condition to your text query in your contains clause that removes the spaces from the search string, then I believe that, along with the stemming and soundex and fuzzy should return most things that the ndata would return. Please see the demonstration below.
                SCOTT@orcl_11gR2> create table test_sh
                  2    (text_id  number,
                  3       text      clob)
                  4  /
                
                Table created.
                
                SCOTT@orcl_11gR2> insert all
                  2  into test_sh values (1, 'Mark Antony')
                  3  into test_sh values (2, 'MARK ANTONY')
                  4  into test_sh values (3, 'mark antony')
                  5  into test_sh values (4, 'MarkAntony')
                  6  into test_sh values (5, 'MA RK ANTONY')
                  7  into test_sh values (6, 'm a r k antony')
                  8  into test_sh values (7, 'Cleopatra' )
                  9  select * from dual
                 10  /
                
                7 rows created.
                
                SCOTT@orcl_11gR2> begin
                  2    ctx_ddl.create_preference ('nd_mcds', 'multi_column_datastore');
                  3    ctx_ddl.set_attribute
                  4        ('nd_mcds',
                  5         'columns',
                  6          'replace (text, '' '', '''') nd, text text');
                  7    ctx_ddl.set_attribute ('nd_mcds', 'delimiter', 'newline');
                  8    ctx_ddl.create_preference ('test_lex', 'basic_lexer');
                  9    ctx_ddl.set_attribute ('test_lex', 'whitespace', '/\|-_+');
                 10  end;
                 11  /
                
                PL/SQL procedure successfully completed.
                
                SCOTT@orcl_11gR2> create index ix_test_sh
                  2  on test_sh (text)
                  3  indextype is ctxsys.context
                  4  parameters
                  5    ('datastore  nd_mcds
                  6        lexer         test_lex')
                  7  /
                
                Index created.
                
                SCOTT@orcl_11gR2> select token_text from dr$ix_test_sh$i
                  2  /
                
                TOKEN_TEXT
                ----------------------------------------------------------------
                ANTONY
                CLEOPATRA
                K
                M
                MA
                MARK
                MARKANTONY
                R
                RK
                
                9 rows selected.
                
                SCOTT@orcl_11gR2> variable search_string varchar2(100)
                SCOTT@orcl_11gR2> column text format a30
                SCOTT@orcl_11gR2> exec :search_string := 'Mark Antony'
                
                PL/SQL procedure successfully completed.
                
                SCOTT@orcl_11gR2> select * from test_sh
                  2  where  contains
                  3             (text,
                  4              'syn (?!$' || replace (:search_string, ' ', ';?!$') || ') or
                  5            syn (?!$' || replace (replace (:search_string, ' ', ''), ' ', ';?!$') || ')') > 0
                  6  /
                
                   TEXT_ID TEXT
                ---------- ------------------------------
                         1 Mark Antony
                         2 MARK ANTONY
                         3 mark antony
                         4 MarkAntony
                         5 MA RK ANTONY
                         6 m a r k antony
                
                6 rows selected.
                
                SCOTT@orcl_11gR2> exec :search_string := 'MarkAntony'
                
                PL/SQL procedure successfully completed.
                
                SCOTT@orcl_11gR2> /
                
                   TEXT_ID TEXT
                ---------- ------------------------------
                         1 Mark Antony
                         2 MARK ANTONY
                         3 mark antony
                         4 MarkAntony
                         5 MA RK ANTONY
                         6 m a r k antony
                
                6 rows selected.
                • 21. Re: Achieving functionality of many preferences using one context index
                  876250
                  ndata was working very well. Alll functionalities which I requested was working very well. But after implementing the above query, Everything seems to be breaking


                  First Requirement_

                  While using the query with ndata this requirement was working very well. but here it does not.

                  Following query returns 6 rows.
                  SQL> exec :search_string := 'Mark Antony'

                  PL/SQL procedure successfully completed.

                  SQL> select * from test_sh
                  where contains
                  (text,
                  'syn (?!$' || replace (:search_string, ' ', ';?!$') || ') or
                  syn (?!$' || replace (replace (:search_string, ' ', ''), ' ', ';?!$') || ')') > 0 2 3 4 5
                  6 /

                  TEXT_ID TEXT
                  ---------- ------------------------------
                  1 Mark Antony
                  2 MARK ANTONY
                  3 mark antony
                  4 MarkAntony
                  5 MA RK ANTONY
                  6 m a r k antony

                  6 rows selected.
                  The original requirement was if we give any of the 6 special characters /,\,|,-,_,+ instead of the space in between words it should return the same results, but it is not returning here
                  SQL> exec :search_string := 'Mark/Antony';

                  PL/SQL procedure successfully completed.

                  SQL> select * from test_sh
                  where contains
                  (text,
                  'syn (?!$' || replace (:search_string, ' ', ';?!$') || ') or
                  syn (?!$' || replace (replace (:search_string, ' ', ''), ' ', ';?!$') || ')') > 0 2 3 4 5
                  6 /

                  TEXT_ID TEXT
                  ---------- ------------------------------
                  1 Mark Antony
                  2 MARK ANTONY
                  3 mark antony
                  Second Requirement_


                  While using the query with ndata this requirement was working very well. but here it does not. This is the fuzzy functionality. Both records used to get returned, now it is not.

                  SQL> insert into test_sh values(11,'tchikosky');

                  1 row created.

                  SQL> insert into test_sh values(12,'Tchaikovsky');

                  1 row created.

                  SQL> commit;

                  Commit complete.



                  SQL> exec ctx_ddl.sync_index('ix_test_sh');

                  PL/SQL procedure successfully completed.


                  SQL> exec :search_string := 'Tchaikovsky';

                  PL/SQL procedure successfully completed.

                  SQL> select * from test_sh
                  where contains
                  (text,
                  'syn (?!$' || replace (:search_string, ' ', ';?!$') || ') or
                  > syn (?!$' || replace (replace (:search_string, ' ', ''), ' ', ';?!$') || ')') > 0 2 3 4 5
                  6 /

                  TEXT_ID TEXT
                  ---------- ------------------------------
                  12 Tchaikovsky
                  Third Requirement_


                  While using the query with ndata this requirement was working very well. but here it does not. cleopatr'a and cleopatra both used to get returned in below query while using ndata but here it doesn't

                  SQL> insert into test_sh values(14,'cleopatr''a');

                  1 row created.

                  SQL> commit;

                  Commit complete.


                  SQL> exec :search_string := 'cleopatr''a'

                  PL/SQL procedure successfully completed.

                  SQL> /

                  TEXT_ID TEXT
                  ---------- ------------------------------
                  14 cleopatr'a
                  Please guide me as to how to get these functionality working in 11.1.0.7
                  • 22. Re: Achieving functionality of many preferences using one context index
                    876250
                    Also any idea where we can find default thesaurus file in 11.1.0.7.

                    I don't see a sample directory in /u01/app/oracle/product/11.1.0/db_1/ctx
                    • 23. Re: Achieving functionality of many preferences using one context index
                      Barbara Boehmer
                      The first and third requirements are easy. All you have to do is use translate and/or replace to remove the special characters from the search string. For the second requirement, it looks like the fuzzy and soundex and stem are not nesting the way I thought they were, so you need to use each one with each term and put them all together. It seems that the simplest method to do all of this would be to create a function to format the search string. I have provided an example below.
                      SCOTT@orcl_11gR2> create table test_sh
                        2    (text_id  number,
                        3       text      clob)
                        4  /
                      
                      Table created.
                      
                      SCOTT@orcl_11gR2> insert all
                        2  into test_sh values (1, 'Mark Antony')
                        3  into test_sh values (2, 'MARK ANTONY')
                        4  into test_sh values (3, 'mark antony')
                        5  into test_sh values (4, 'MarkAntony')
                        6  into test_sh values (5, 'MA RK ANTONY')
                        7  into test_sh values (6, 'm a r k antony')
                        8  into test_sh values (7, 'Cleopatra' )
                        9  into test_sh values (11, 'tchikosky')
                       10  into test_sh values (12, 'Tchaikovsky')
                       11  into test_sh values (14, 'Cleopatr''a' )
                       12  select * from dual
                       13  /
                      
                      10 rows created.
                      
                      SCOTT@orcl_11gR2> begin
                        2    ctx_ddl.create_preference ('nd_mcds', 'multi_column_datastore');
                        3    ctx_ddl.set_attribute
                        4        ('nd_mcds',
                        5         'columns',
                        6          'replace (text, '' '', '''') nd, text text');
                        7    ctx_ddl.set_attribute ('nd_mcds', 'delimiter', 'newline');
                        8    ctx_ddl.create_preference ('test_lex', 'basic_lexer');
                        9    ctx_ddl.set_attribute ('test_lex', 'whitespace', '/\|-_+');
                       10  end;
                       11  /
                      
                      PL/SQL procedure successfully completed.
                      
                      SCOTT@orcl_11gR2> create index ix_test_sh
                        2  on test_sh (text)
                        3  indextype is ctxsys.context
                        4  parameters
                        5    ('datastore  nd_mcds
                        6        lexer         test_lex')
                        7  /
                      
                      Index created.
                      
                      SCOTT@orcl_11gR2> create or replace function format_search
                        2    (p_string  in varchar2)
                        3    return          varchar2
                        4  as
                        5    v_string_in1  varchar2 (32767);
                        6    v_string_in2  varchar2 (32767);
                        7    v_token          varchar2 (32767);
                        8    v_string_out  varchar2 (32767);
                        9  begin
                       10    -- remove special characters:
                       11    v_string_in1 := ltrim (translate (p_string, '/\|-_+', '         ')) || ' ';
                       12    v_string_in1 := replace (v_string_in1, '''', '');
                       13    -- remove double spaces:
                       14    while instr (v_string_in1, '  ') > 0 loop
                       15        v_string_in1 := replace (v_string_in1, '  ', ' ');
                       16    end loop;
                       17    -- second search string without spaces:
                       18    v_string_in2 := replace (v_string_in1, ' ', '') || ' ';
                       19    if v_string_in2 = v_string_in1 then
                       20        v_string_in2 := null;
                       21    end if;
                       22    -- format search string one token at a time:
                       23    while v_string_in1 is not null loop
                       24        v_token := substr (v_string_in1, 1, instr (v_string_in1, ' ') - 1);
                       25        v_string_out := v_string_out
                       26          || '('
                       27          || 'syn (' || v_token || ') or '
                       28          || '?' || v_token || ' or '
                       29          || '!' || v_token || ' or '
                       30          || '$' || v_token
                       31          || '),';
                       32        v_string_in1 := substr (v_string_in1, instr (v_string_in1, ' ') + 1);
                       33    end loop;
                       34    while v_string_in2 is not null loop
                       35        v_token := substr (v_string_in2, 1, instr (v_string_in2, ' ') - 1);
                       36        v_string_out := v_string_out
                       37          || '('
                       38          || 'syn (' || v_token || ') or '
                       39          || '?' || v_token || ' or '
                       40          || '!' || v_token || ' or '
                       41          || '$' || v_token
                       42          || '),';
                       43        v_string_in2 := substr (v_string_in2, instr (v_string_in2, ' ') + 1);
                       44    end loop;
                       45    -- return formatted string:
                       46    return rtrim (v_string_out, ',');
                       47  end format_search;
                       48  /
                      
                      Function created.
                      
                      SCOTT@orcl_11gR2> show errors
                      No errors.
                      SCOTT@orcl_11gR2> -- examples of what function returns:
                      SCOTT@orcl_11gR2> select format_search ('Mark/Antony') from dual
                        2  /
                      
                      FORMAT_SEARCH('MARK/ANTONY')
                      --------------------------------------------------------------------------------
                      (syn (Mark) or ?Mark or !Mark or $Mark),(syn (Antony) or ?Antony or !Antony or $
                      Antony),(syn (MarkAntony) or ?MarkAntony or !MarkAntony or $MarkAntony)
                      
                      
                      1 row selected.
                      
                      SCOTT@orcl_11gR2> select format_search ('Cleopatr''a') from dual
                        2  /
                      
                      FORMAT_SEARCH('CLEOPATR''A')
                      --------------------------------------------------------------------------------
                      (syn (Cleopatra) or ?Cleopatra or !Cleopatra or $Cleopatra)
                      
                      1 row selected.
                      
                      SCOTT@orcl_11gR2> -- searches using function:
                      SCOTT@orcl_11gR2> variable search_string varchar2(100)
                      SCOTT@orcl_11gR2> column text format a30
                      SCOTT@orcl_11gR2> exec :search_string := 'Mark/Antony'
                      
                      PL/SQL procedure successfully completed.
                      
                      SCOTT@orcl_11gR2> select * from test_sh
                        2  where  contains (text, format_search (:search_string)) > 0
                        3  /
                      
                         TEXT_ID TEXT
                      ---------- ------------------------------
                               1 Mark Antony
                               2 MARK ANTONY
                               3 mark antony
                               4 MarkAntony
                               5 MA RK ANTONY
                               6 m a r k antony
                      
                      6 rows selected.
                      
                      SCOTT@orcl_11gR2> exec :search_string := 'Cleopatr''a'
                      
                      PL/SQL procedure successfully completed.
                      
                      SCOTT@orcl_11gR2> /
                      
                         TEXT_ID TEXT
                      ---------- ------------------------------
                               7 Cleopatra
                              14 Cleopatr'a
                      
                      2 rows selected.
                      
                      SCOTT@orcl_11gR2> exec :search_string := 'Tchaikovsky'
                      
                      PL/SQL procedure successfully completed.
                      
                      SCOTT@orcl_11gR2> /
                      
                         TEXT_ID TEXT
                      ---------- ------------------------------
                              11 tchikosky
                              12 Tchaikovsky
                      
                      2 rows selected.
                      • 24. Re: Achieving functionality of many preferences using one context index
                        Barbara Boehmer
                        873247 wrote:
                        Also any idea where we can find default thesaurus file in 11.1.0.7.

                        I don't see a sample directory in /u01/app/oracle/product/11.1.0/db_1/ctx
                        In earlier versions, you had to download the "companion CD" separately to get a sample thesaurus. Then, in more recent versions, you had to download the "examples" separately to get a sample thesaurus. I don't know where it is now or if it requires a separate download. Hopefully Roger Ford can advise you on that. When you do find it, bear in mind that it is just a sample thesaurus and is not complete or intended for production usage. You need to either purchase a file from elsewhere that you can load or create your own custom thesaurus. A custom thesaurus can be loaded using either ctxload or ctx_thes.
                        • 25. Re: Achieving functionality of many preferences using one context index
                          876250
                          I am very sorry for the late reply Barbara.

                          Thanks for the immediate reply and solution. I created index exactly like the solution you had given.


                          SQL> begin
                          2 ctx_ddl.create_preference ('nd_mcds', 'multi_column_datastore');
                          3 ctx_ddl.set_attribute
                          4 ('nd_mcds',
                          5 'columns',
                          6 'replace (text, '' '', '''') nd, text text');
                          7 ctx_ddl.set_attribute ('nd_mcds', 'delimiter', 'newline');
                          8 ctx_ddl.create_preference ('test_lex', 'basic_lexer');
                          9 ctx_ddl.set_attribute ('test_lex', 'whitespace', '/\|-_+');
                          10 end;
                          11 /

                          PL/SQL procedure successfully completed.

                          SQL> create index ix_test_sh
                          on test_sh (text)
                          indextype is ctxsys.context
                          parameters
                          ('datastore nd_mcds
                          lexer test_lex') 2 3 4 5 6
                          7 /

                          Index created.

                          SQL> create or replace function format_search
                          2 (p_string in varchar2)
                          3 return varchar2
                          4 as
                          5 v_string_in1 varchar2 (32767);
                          6 v_string_in2 varchar2 (32767);
                          7 v_token varchar2 (32767);
                          8 v_string_out varchar2 (32767);
                          begin
                          9 10 -- remove special characters:
                          11 v_string_in1 := ltrim (translate (p_string, '/\|-_+', '            ')) || ' ';
                          12 v_string_in1 := replace (v_string_in1, '''', '');
                          13 -- remove double spaces:
                          14 while instr (v_string_in1, ' ') > 0 loop
                          15 v_string_in1 := replace (v_string_in1, ' ', ' ');
                          end loop;
                          16 17 -- second search string without spaces:
                          18 v_string_in2 := replace (v_string_in1, ' ', '') || ' ';
                          19 if v_string_in2 = v_string_in1 then
                          20 v_string_in2 := null;
                          21 end if;
                          22 -- format search string one token at a time:
                          23 while v_string_in1 is not null loop
                          24 v_token := substr (v_string_in1, 1, instr (v_string_in1, ' ') - 1);
                          25 v_string_out := v_string_out
                          26 || '('
                          27 || 'syn (' || v_token || ') or '
                          28 || '?' || v_token || ' or '
                          29 || '!' || v_token || ' or '
                          30 || '$' || v_token
                          31 || '),';
                          32 v_string_in1 := substr (v_string_in1, instr (v_string_in1, ' ') + 1);
                          33 end loop;
                          34 while v_string_in2 is not null loop
                          35 v_token := substr (v_string_in2, 1, instr (v_string_in2, ' ') - 1);
                          v_string_out := v_string_out
                          36 37 || '('
                          38 || 'syn (' || v_token || ') or '
                          39 || '?' || v_token || ' or '
                          40 || '!' || v_token || ' or '
                          41 || '$' || v_token
                          42 || '),';
                          43 v_string_in2 := substr (v_string_in2, instr (v_string_in2, ' ') + 1);
                          44 end loop;
                          45 -- return formatted string:
                          46 return rtrim (v_string_out, ',');
                          47 end format_search;
                          48 /

                          Function created.

                          SQL> select format_search ('Mark/Antony') from dual;

                          FORMAT_SEARCH('MARK/ANTONY')
                          --------------------------------------------------------------------------------
                          (syn (Mark Antony) or ?Mark Antony or !Mark Antony or $Mark Antony)

                          SQL> select format_search ('Cleopatr''a') from dual;

                          FORMAT_SEARCH('CLEOPATR''A')
                          --------------------------------------------------------------------------------
                          (syn (Cleopatra) or ?Cleopatra or !Cleopatra or $Cleopatra)

                          SQL> variable search_string varchar2(100);
                          SQL> column text format a30
                          SQL> exec :search_string := 'Mark/Antony';

                          PL/SQL procedure successfully completed.

                          SQL> select * from test_sh where contains (text, format_search (:search_string)) > 0;

                          TEXT_ID TEXT
                          ---------- ------------------------------
                          1 Mark Antony
                          2 MARK ANTONY
                          3 mark antony
                          -- Only 3 result r being output here
                          Where as we are expecting 6 results

                          SQL> select * from test_sh where upper(text) like 'M%';

                          TEXT_ID TEXT
                          ---------- ------------------------------
                          1 Mark Antony
                          2 MARK ANTONY
                          3 mark antony
                          4 MarkAntony
                          5 MA RK ANTONY
                          6 m a r k antony

                          6 rows selected.
                          Please let me know where am I going wrong.
                          • 26. Re: Achieving functionality of many preferences using one context index
                            Roger Ford-Oracle
                            What do you get if you just type:
                            select * from test_sh where contains(text, 'markantony') > 0;
                            • 27. Re: Achieving functionality of many preferences using one context index
                              876250
                              That is giving all the results

                              SQL> select * from test_sh where contains(text, 'markantony') > 0;

                              TEXT_ID TEXT
                              ---------- ------------------------------
                              1 Mark Antony
                              2 MARK ANTONY
                              3 mark antony
                              4 MarkAntony
                              5 MA RK ANTONY
                              6 m a r k antony

                              6 rows selected.
                              • 28. Re: Achieving functionality of many preferences using one context index
                                Barbara Boehmer
                                There is a section of the function that you posted that is different from what I posted. You have only one space between quotes where there should be 6 or 2 spaces. Please note the differences below. I have also provided the complete function below without line numbers, so that you can copy and paste it.

                                -- what I posted:
                                  -- remove special characters:
                                  v_string_in1 := ltrim (translate (p_string, '/\|-_+', '      ')) || ' ';
                                  v_string_in1 := replace (v_string_in1, '''', '');
                                  -- remove double spaces:
                                  while instr (v_string_in1, '  ') > 0 loop
                                    v_string_in1 := replace (v_string_in1, '  ', ' ');
                                  end loop;  
                                -- what you posted:
                                  -- remove special characters:
                                  v_string_in1 := ltrim (translate (p_string, '/\|-_+', ' ')) || ' ';
                                  v_string_in1 := replace (v_string_in1, '''', '');
                                  -- remove double spaces:
                                  while instr (v_string_in1, ' ') > 0 loop
                                    v_string_in1 := replace (v_string_in1, ' ', ' ');
                                  end loop;
                                -- complete correct function:
                                create or replace function format_search
                                  (p_string  in varchar2)
                                  return        varchar2
                                as
                                  v_string_in1  varchar2 (32767);
                                  v_string_in2  varchar2 (32767);
                                  v_token       varchar2 (32767);
                                  v_string_out  varchar2 (32767);
                                begin
                                  -- remove special characters:
                                  v_string_in1 := ltrim (translate (p_string, '/\|-_+', '      ')) || ' ';
                                  v_string_in1 := replace (v_string_in1, '''', '');
                                  -- remove double spaces:
                                  while instr (v_string_in1, '  ') > 0 loop
                                    v_string_in1 := replace (v_string_in1, '  ', ' ');
                                  end loop;  
                                  -- second search string without spaces:
                                  v_string_in2 := replace (v_string_in1, ' ', '') || ' ';
                                  if v_string_in2 = v_string_in1 then
                                    v_string_in2 := null;
                                  end if;
                                  -- format search string one token at a time:
                                  while v_string_in1 is not null loop
                                    v_token := substr (v_string_in1, 1, instr (v_string_in1, ' ') - 1);
                                    v_string_out := v_string_out 
                                      || '('
                                      || 'syn (' || v_token || ') or '
                                      || '?' || v_token || ' or '
                                      || '!' || v_token || ' or '
                                      || '$' || v_token
                                      || '),';
                                    v_string_in1 := substr (v_string_in1, instr (v_string_in1, ' ') + 1);
                                  end loop;  
                                  while v_string_in2 is not null loop
                                    v_token := substr (v_string_in2, 1, instr (v_string_in2, ' ') - 1);
                                    v_string_out := v_string_out 
                                      || '('
                                      || 'syn (' || v_token || ') or '
                                      || '?' || v_token || ' or '
                                      || '!' || v_token || ' or '
                                      || '$' || v_token
                                      || '),';
                                    v_string_in2 := substr (v_string_in2, instr (v_string_in2, ' ') + 1);
                                  end loop;  
                                  -- return formatted string:
                                  return rtrim (v_string_out, ',');
                                end format_search;
                                /
                                • 29. Re: Achieving functionality of many preferences using one context index
                                  876250
                                  Thanks a lot Barbara. With your solution we were able to achieve all functionality.
                                  We are very grateful to you.


                                  Thanks & Regards,
                                  Vikas Krishna